chore(pulumi): remove orphan CI IAM users from all stacks (#212)#214
chore(pulumi): remove orphan CI IAM users from all stacks (#212)#214
Conversation
CloudTrail confirms zero activity over 90 days across dev/stage/prod
in eu-central-1 and us-east-1. No workflow in this repo or the
thunderbird org references these users. All three access keys are
Active but unused.
Removes tb_pulumi.iam.UserWithAccessKey from the StackAccessPolicies
on_apply callback. Next pulumi up will destroy mailstrom-{dev,stage,prod}-ci
and their access keys across all three stacks.
Part of thunderbird/platform-infrastructure#212
ryanjjung
left a comment
There was a problem hiding this comment.
This ought to be safe. We never really set up much automation around this.
I was also going to suggest removing the StackAccessPolicies as well. Rationale: they can be easily rebuilt if we need them, but nobody needs them right now. Removing them would clean up a lot of constant ugly drift in the pulumi plans.
Great! I'll start with this one. We have a new way of doing it now with identity center anyhoo |
|
Yes, and this was always in response to a security audit where it was determined we needed a way to grant individuals (like third party folks) limited env-app access. If someone like that ever shows up, we can make that happen. |
Pre-merge notespulumi preview: pre-existing failure (unrelated to this PR)
The CI user resources confirmed in stateVerified via Recommended apply approach: targeted destroyBecause the full # In /workspaces/mailstrom/pulumi, with PULUMI_ACCESS_TOKEN set:
# dev
pulumi stack select mzla-services/mailstrom/dev
TBPULUMI_DISABLE_PROTECTION=True PULUMI_CONFIG_PASSPHRASE='' pulumi up -y --target 'urn:pulumi:dev::mailstrom::tb:iam:UserWithAccessKey::mailstrom-dev-ci' --target-dependents
# stage
pulumi stack select mzla-services/mailstrom/stage
TBPULUMI_DISABLE_PROTECTION=True PULUMI_CONFIG_PASSPHRASE='' pulumi up -y --target 'urn:pulumi:stage::mailstrom::tb:iam:UserWithAccessKey::mailstrom-stage-ci' --target-dependents
# prod
pulumi stack select mzla-services/mailstrom/prod
TBPULUMI_DISABLE_PROTECTION=True PULUMI_CONFIG_PASSPHRASE='' pulumi up -y --target 'urn:pulumi:prod::mailstrom::tb:iam:UserWithAccessKey::mailstrom-prod-ci' --target-dependents |
State drift detail — full
|
| Count | Resource type | Notes |
|---|---|---|
| 1 | aws:ec2/instance:Instance |
Bastion mailstrom-dev-public-rjung-instance (id: i-0b1a0de64036229af) |
| 1 | aws:ec2/securityGroup:SecurityGroup |
Bastion SG |
| 4 | aws:ec2/securityGroupRule:SecurityGroupRule |
Bastion + Stalwart ingress rules |
| 1 | tb:ec2:SshableInstance + tb:network:SecurityGroupWithRules |
Bastion wrapper resources |
| 2 | aws:secretsmanager/secret:Secret + versions |
mailstrom/dev/stalwart.postboot.redis_backend (removed from config but not applied) |
| 2 | tb:secrets:SecretsManagerSecret |
Same redis secret wrappers |
| 2 | aws:iam/group:Group |
SAP readonly + admin groups |
| 18 | aws:iam/groupPolicyAttachment:GroupPolicyAttachment |
SAP group policy attachments |
| 19 | aws:iam/policy:Policy |
SAP service policies |
| 9 | CI user resources | ✅ Expected — the purpose of this PR |
stage — 88 deletes
| Count | Resource type | Notes |
|---|---|---|
| 24 | aws:cloudwatch/metricAlarm:MetricAlarm |
All monitoring alarms gone from program |
| 1 | aws:sns/topic:Topic + 1 topicSubscription |
Monitoring SNS topic |
| 1 | tb:cloudwatch:CloudFrontDistributionAlarmGroup |
|
| 3 | tb:cloudwatch:Ec2InstanceAlarmGroup |
|
| 8 | tb:cloudwatch:LbTargetGroupAlarmGroup |
|
| 2 | aws:secretsmanager/secret:Secret + versions |
stalwart.postboot.redis_backend |
| 2 | aws:iam/group:Group |
SAP groups |
| 18 | aws:iam/groupPolicyAttachment:GroupPolicyAttachment |
SAP attachments |
| 19 | aws:iam/policy:Policy |
SAP policies |
| 9 | CI user resources | ✅ Expected |
prod — 86 deletes + 1 replace + error
| Count | Resource type | Notes |
|---|---|---|
| 29 | aws:cloudwatch/metricAlarm:MetricAlarm |
All monitoring alarms |
| 1 | aws:sns/topic:Topic + 1 topicSubscription |
|
| 1 | tb:cloudwatch:CloudFrontDistributionAlarmGroup |
|
| 4 | tb:cloudwatch:Ec2InstanceAlarmGroup |
|
| 8 | tb:cloudwatch:LbTargetGroupAlarmGroup |
|
| 2 | aws:secretsmanager/secret:Secret + versions |
stalwart.postboot.redis_backend |
| 2 | aws:iam/group:Group |
SAP groups |
| 14 | aws:iam/groupPolicyAttachment:GroupPolicyAttachment |
SAP attachments |
| 15 | aws:iam/policy:Policy |
SAP policies |
| 1 | aws:ec2/securityGroupRule:SecurityGroupRule |
Cannot be deleted — preview fails with: resource "mailstrom-prod-stalwart-privlbsg-management-ingress-2" cannot be deleted |
| 9 | CI user resources | ✅ Expected |
Likely root causes
- Redis backend secret —
stalwart.postboot.redis_backendwas removed fromconfig.{dev,stage,prod}.yamlbutpulumi upwas never run; the secret and its wrapper remain in state. - Bastion host (dev) —
mailstrom-dev-public-rjungremoved from config but not destroyed. - CloudWatch monitoring (stage + prod) —
monitoring_optsevaluates toNonefor these stacks, so theCloudWatchMonitoringGroupblock is skipped; all alarms/SNS remain in state. - SAP policy drift —
StackAccessPoliciesis regenerating group policy attachments with new IDs, causing the old ones to appear as orphans. - Security group rule conflict (prod) — a Stalwart security group rule has a dependency Pulumi cannot resolve for deletion.
Live preview links for reference:
- dev: https://app.pulumi.com/mzla-services/mailstrom/dev/previews/c183f93a-7094-46d9-a99d-7c6083baeb08
- stage: https://app.pulumi.com/mzla-services/mailstrom/stage/previews/3daca320-4a26-42cf-91c4-2d75ea9192d1
- prod: https://app.pulumi.com/mzla-services/mailstrom/prod/previews/18d94f23-f6f1-4ed4-8234-c7c1da8841dd
Preview re-run with setup-{dev,stage,prod}.sh sourced (2026-04-22)Re-ran Counts unchanged: dev 58 deletes, stage 88 deletes, prod 86 deletes + 1 blocked deletion (security group rule). Live preview links:
The targeted destroy for the CI user resources is still the correct path forward for this PR. The drift needs to be addressed in a separate issue before running a full |
Summary
Removes the
tb_pulumi.iam.UserWithAccessKeyCI user block fromStackAccessPolicies.on_applyacross all three stacks (dev, stage, prod).Investigation findings (thunderbird/platform-infrastructure#212):
thunderbird/mailstrom/.github/workflows/referencesAWS_ACCESS_KEY_IDorAWS_SECRET_ACCESS_KEY— the two workflows present (validate.yml,nightly-integration-tests.yml) don't touch AWSgh search code --owner thunderbird "mailstrom-ci"returned no resultsUsers being removed:
mailstrom-dev-ciarn:aws:iam::768512802988:user/mailstrom-dev-ciAKIA3F3XOYCWN7CIZV6Emailstrom-stage-ciarn:aws:iam::768512802988:user/mailstrom-stage-ciAKIA3F3XOYCWN7CBCRFOmailstrom-prod-ciarn:aws:iam::768512802988:user/mailstrom-prod-ciAKIA3F3XOYCWAIUIZPF3All three users confirmed in Pulumi state via
pulumi stack export(9 child resources each: user, access key, group membership, Secrets Manager secret/version, IAM policy/attachment).Pre-existing issues — do not run a full
pulumi upTwo separate pre-existing problems surface during preview. Neither is introduced by this PR.
1. Pre-existing state drift (all stacks)
Running
pulumi previewwithout--targetshows 58–88 unexpected resource deletions per stack, including EC2 instances, security group rules, and IAM group policy attachments that are unrelated to the CI user. This is pre-existing Pulumi state drift — these resources exist in state but the program no longer produces them (likely due to tb_pulumi API changes since the lastpulumi up).Do not run a full
pulumi upuntil this drift is investigated separately. A targeted destroy (below) avoids touching these resources entirely.2. Provider mismatch without region env vars (dev/prod)
Without
AWS_DEFAULT_REGION/AWS_REGION=eu-central-1, dev and prod fail immediately:Setting the region env vars resolves this. All apply commands below include them.
Merge checklist
--target(avoids the pre-existing drift above):NoSuchEntityfor each user:No OIDC migration needed
Mailstrom has no automated deploy workflow. Operator deployments run locally via SSO credentials. Once these orphan users are deleted there is no remaining AWS auth surface in this repo's CI.
Closes thunderbird/platform-infrastructure#212