migrate runtime secrets from ssm to aws secrets manager - #5141
migrate runtime secrets from ssm to aws secrets manager#5141Mr-Rahul-Paul wants to merge 57 commits into
Conversation
Summary by CodeRabbit
WalkthroughThe PR introduces a staged migration from SSM runtime secrets to AWS Secrets Manager. It updates Terraform resources, ECS secret injection, IAM permissions, deployment validation, state migration, documentation, examples, and tests. ChangesRuntime Secrets Migration
Bootstrap IAM Namespace Refactor
Estimated code review effort: 5 (Critical) | ~90+ minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Contribution validation failed:
|
|
|
||
| statement { | ||
| sid = "AppAutoscalingManagement" | ||
| sid = "AppAutoscalingMgmt" |
There was a problem hiding this comment.
I shortened the Management suffix to Mgmt because AWS managed policies have a 6,144-character limit.
shortening them was the smallest low-risk fix.
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.
Re-trigger cubic
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.
Re-trigger cubic
…test Add explicit assertions to test_prepare_mode_does_not_grant_secretsmanager_access verifying that DJANGO_DB_PASSWORD and DJANGO_REDIS_PASSWORD still resolve to their SSM ARNs (var.db_password_arn and var.redis_password_arn) when runtime_secrets_mode is prepare. The existing IAM-empty check alone would not catch a premature switch of the container-secret source to Secrets Manager. Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…unset Environments without RUNTIME_SECRETS_MODE configured in GitHub Variables would resolve to an empty string, causing the validation step to immediately fail with an error even though prepare is the safe migration default. Default RUNTIME_SECRETS_MODE to 'prepare' using the || operator so unset environments continue to deploy safely. Also reuse the already-resolved env var in the Terraform tfvars block instead of reading the raw GitHub variable a second time, ensuring both the validation and apply steps see the same value. Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
|
Contribution validation failed:
|
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
infrastructure/modules/tasks/main.tf (1)
75-85: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winAllow
kms:Decryptvia SSM as well.Both execution-role policies need KMS access for
SecureStringparameters, but thekms:ViaServicecondition only allows Secrets Manager. Addssm.${var.aws_region}.amazonaws.comin:
infrastructure/modules/tasks/main.tfinfrastructure/modules/service/main.tf🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@infrastructure/modules/tasks/main.tf` around lines 75 - 85, Update the kms:Decrypt policy condition in infrastructure/modules/tasks/main.tf lines 75-85 and infrastructure/modules/service/main.tf lines 276-293 to allow both Secrets Manager and SSM via-service values, including ssm.${var.aws_region}.amazonaws.com alongside the existing Secrets Manager service.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@infrastructure/modules/tasks/main.tf`:
- Around line 75-85: Update the kms:Decrypt policy condition in
infrastructure/modules/tasks/main.tf lines 75-85 and
infrastructure/modules/service/main.tf lines 276-293 to allow both Secrets
Manager and SSM via-service values, including
ssm.${var.aws_region}.amazonaws.com alongside the existing Secrets Manager
service.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 20660a26-1af9-420d-a8fb-6f7bfb46b87e
📒 Files selected for processing (17)
.github/workflows/run-deploy.yamlinfrastructure/bootstrap/main.tfinfrastructure/live/README.mdinfrastructure/live/main.tfinfrastructure/modules/cache/README.mdinfrastructure/modules/cache/main.tfinfrastructure/modules/database/README.mdinfrastructure/modules/database/main.tfinfrastructure/modules/parameters/README.mdinfrastructure/modules/parameters/main.tfinfrastructure/modules/parameters/tests/parameters.tftest.hclinfrastructure/modules/service/README.mdinfrastructure/modules/service/main.tfinfrastructure/modules/tasks/README.mdinfrastructure/modules/tasks/main.tfinfrastructure/modules/tasks/modules/task/README.mdinfrastructure/modules/tasks/modules/task/main.tf
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
Signed-off-by: Mr-Rahul-Paul <179798584+Mr-Rahul-Paul@users.noreply.github.com>
|
There was a problem hiding this comment.
Pull request overview
This pull request implements the infrastructure-side migration of runtime secrets from SSM Parameter Store (SecureString) to AWS Secrets Manager, while keeping non-secret configuration in SSM and preserving local .env behavior. It introduces a two-phase rollout (prepare → complete) and updates ECS services/tasks and IAM policies to support Secrets Manager injection when the migration is completed.
Changes:
- Added
runtime_secrets_modeand Secrets Manager resources/outputs across modules to support a staged migration (preparekeeps SSM secret injection;completeswitches to Secrets Manager and removes legacy SecureString parameters). - Updated ECS service/task modules to accept mixed
valueFromreferences (SSM + Secrets Manager) and to conditionally grant Secrets Manager + KMS permissions. - Added a deployment preflight check (
verify-runtime-secrets.sh) and workflow gating to blockcompletedeployments when required secrets lack anAWSCURRENTversion.
Reviewed changes
Copilot reviewed 39 out of 39 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| infrastructure/modules/tasks/variables.tf | Rename container secret input and add Secrets Manager ARN allowlist input. |
| infrastructure/modules/tasks/tests/unit.tftest.hcl | Add IAM policy assertions for optional Secrets Manager/KMS statements. |
| infrastructure/modules/tasks/README.md | Regenerate docs for new/renamed module inputs. |
| infrastructure/modules/tasks/modules/task/variables.tf | Rename task-level container secret input. |
| infrastructure/modules/tasks/modules/task/tests/unit.tftest.hcl | Update unit tests to use renamed task input. |
| infrastructure/modules/tasks/modules/task/README.md | Regenerate docs for renamed task input. |
| infrastructure/modules/tasks/modules/task/main.tf | Switch ECS secrets to use the renamed input map. |
| infrastructure/modules/tasks/main.tf | Extend execution role policy to optionally allow Secrets Manager + KMS decrypt. |
| infrastructure/modules/service/variables.tf | Rename container secret input and add Secrets Manager ARN allowlist input. |
| infrastructure/modules/service/tests/unit.tftest.hcl | Add IAM policy assertions for optional Secrets Manager/KMS statements. |
| infrastructure/modules/service/README.md | Regenerate docs for new/renamed service inputs. |
| infrastructure/modules/service/main.tf | Switch ECS secrets to use renamed input map and extend execution role policy. |
| infrastructure/modules/parameters/variables.tf | Add inputs needed for Secrets Manager migration (KMS key, db/redis secret ARNs, mode). |
| infrastructure/modules/parameters/tests/unit.tftest.hcl | Add tests for prepare/complete behavior and updated resource addressing. |
| infrastructure/modules/parameters/tests/integration.tftest.hcl | Update integration coverage to assert Secrets Manager secrets and KMS key usage. |
| infrastructure/modules/parameters/README.md | Regenerate docs for new Secrets Manager resources and outputs. |
| infrastructure/modules/parameters/outputs.tf | Replace SSM-only outputs with mixed valueFrom outputs and SM allowlist outputs. |
| infrastructure/modules/parameters/main.tf | Create Secrets Manager secrets (external + generated), gate legacy SSM secrets by mode, add state moves. |
| infrastructure/modules/database/variables.tf | Add runtime_secrets_mode input to gate legacy SSM password parameter. |
| infrastructure/modules/database/tests/unit.tftest.hcl | Add tests for complete-mode removal of legacy SSM param and DB credentials secret config. |
| infrastructure/modules/database/tests/integration.tftest.hcl | Add integration test coverage for DB secret/SSM behavior across modes. |
| infrastructure/modules/database/README.md | Regenerate docs for new inputs/outputs related to Secrets Manager. |
| infrastructure/modules/database/outputs.tf | Expose DB credentials secret ARN and make legacy SSM password ARN nullable. |
| infrastructure/modules/database/main.tf | Gate legacy SSM password parameter by mode and add moved block for indexed addressing. |
| infrastructure/modules/cache/variables.tf | Add runtime secrets mode and recovery window inputs for Redis secret migration. |
| infrastructure/modules/cache/tests/unit.tftest.hcl | Add tests for complete-mode removal and Redis secret configuration. |
| infrastructure/modules/cache/tests/integration.tftest.hcl | Add integration test coverage for Redis secret/SSM behavior across modes. |
| infrastructure/modules/cache/README.md | Regenerate docs for Redis Secrets Manager resources and new outputs. |
| infrastructure/modules/cache/outputs.tf | Expose Redis secret ARN and make legacy SSM password ARN nullable. |
| infrastructure/modules/cache/main.tf | Gate legacy SSM Redis password parameter by mode and add Secrets Manager secret + version. |
| infrastructure/live/variables.tf | Add runtime_secrets_mode variable to live stack inputs. |
| infrastructure/live/terraform.staging.tfvars.example | Default staging example to runtime_secrets_mode = \"prepare\". |
| infrastructure/live/terraform.production.tfvars.example | Default production example to runtime_secrets_mode = \"prepare\". |
| infrastructure/live/README.md | Regenerate docs for new live stack input. |
| infrastructure/live/main.tf | Wire new parameters/secret outputs into backend/frontend/tasks, and pass mode into modules. |
| infrastructure/bootstrap/tests/unit.tftest.hcl | Add assertions for Secrets Manager namespace permissions. |
| infrastructure/bootstrap/main.tf | Extend Terraform bootstrap IAM policy to manage both legacy and /project/env/* Secrets Manager namespaces. |
| .github/workflows/run-deploy.yaml | Add runtime mode validation + preflight verification and pass mode into Terraform. |
| .github/scripts/verify-runtime-secrets.sh | Add Secrets Manager preflight to require AWSCURRENT versions before complete deploys. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
infrastructure/modules/parameters/main.tf (1)
16-47: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winKeep
DJANGO_ALGOLIA_APPLICATION_IDasSecureString.
aws_ssm_parameter.typecannot change for an existing Parameter Store parameter. An existing staging or production environment will fail onprepare/apply if this hierarchical parameter type is changed fromSecureStringtoString.Retain
type = "SecureString"foraws_ssm_parameter.django_algolia_application_idand its matching test assertion, or run a separate delete-and-recreate migration.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@infrastructure/modules/parameters/main.tf` around lines 16 - 47, Keep aws_ssm_parameter.django_algolia_application_id configured with type = "SecureString" in infrastructure/modules/parameters/main.tf:16-47; do not change the existing parameter type to String. Update or retain the matching assertion in infrastructure/modules/parameters/tests/unit.tftest.hcl:157-162 to expect SecureString.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@infrastructure/modules/service/tests/unit.tftest.hcl`:
- Around line 15-20: Update infrastructure/modules/service/tests/unit.tftest.hcl
lines 15-20 to provide both SSM and Secrets Manager entries in container_secrets
and assert decoded aws_ecs_task_definition.main.container_definitions contains
matching name/valueFrom mappings; update
infrastructure/modules/tasks/modules/task/tests/unit.tftest.hcl line 7 with a
non-empty mapping and assert aws_ecs_task_definition.task.container_definitions
preserves each name and reference; update
infrastructure/modules/tasks/tests/unit.tftest.hcl lines 6-20 with a non-empty
mapping and assert every affected nested scheduled-task definition receives it.
---
Outside diff comments:
In `@infrastructure/modules/parameters/main.tf`:
- Around line 16-47: Keep aws_ssm_parameter.django_algolia_application_id
configured with type = "SecureString" in
infrastructure/modules/parameters/main.tf:16-47; do not change the existing
parameter type to String. Update or retain the matching assertion in
infrastructure/modules/parameters/tests/unit.tftest.hcl:157-162 to expect
SecureString.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 97dd9fec-fd24-4ecf-8456-3d11f6880a43
📒 Files selected for processing (26)
.github/workflows/run-deploy.yamlinfrastructure/bootstrap/main.tfinfrastructure/bootstrap/tests/unit.tftest.hclinfrastructure/live/README.mdinfrastructure/live/main.tfinfrastructure/modules/cache/README.mdinfrastructure/modules/cache/main.tfinfrastructure/modules/cache/tests/integration.tftest.hclinfrastructure/modules/cache/tests/unit.tftest.hclinfrastructure/modules/database/README.mdinfrastructure/modules/database/main.tfinfrastructure/modules/database/tests/integration.tftest.hclinfrastructure/modules/database/tests/unit.tftest.hclinfrastructure/modules/parameters/README.mdinfrastructure/modules/parameters/main.tfinfrastructure/modules/parameters/tests/integration.tftest.hclinfrastructure/modules/parameters/tests/unit.tftest.hclinfrastructure/modules/service/README.mdinfrastructure/modules/service/main.tfinfrastructure/modules/service/tests/unit.tftest.hclinfrastructure/modules/tasks/README.mdinfrastructure/modules/tasks/main.tfinfrastructure/modules/tasks/modules/task/README.mdinfrastructure/modules/tasks/modules/task/main.tfinfrastructure/modules/tasks/modules/task/tests/unit.tftest.hclinfrastructure/modules/tasks/tests/unit.tftest.hcl



Proposed change
Resolves #5071
Move runtime secrets from SSM Parameter Store to AWS Secrets Manager. Non-secret settings stay in SSM, and local development continues to use
.envfiles.Migration flow
This adds two migration modes:
prepare: creates the new Secrets Manager secrets but keeps ECS reading the existing SSM secrets. This gives maintainers time to add and check the real values.complete: checks that every required secret has anAWSCURRENTvalue, switches ECS to Secrets Manager, and removes the old secret-valued SSM parameters.The rollout is: run
preparein staging, add and verify the secret values, runcomplete, check staging, and then repeat for production.What changed
valueFromreferences./<project>/<environment>/*secret names.completemode when a secret has noAWSCURRENTvalue..envbehavior unchanged.After
complete, seeing both SSM and Secrets Manager references in an ECS task definition is expected: SSM holds normal configuration and Secrets Manager holds credentials.Testing
Terraform formatting and documentation
The affected module READMEs were regenerated, and the final format check returned no errors.
Terraform tests
Output: 378 passed, 0 failed across 17 modules, including 10 passed, 0 failed for the bootstrap IAM tests. This covered both migration modes, generated secret references, state moves, and the staging/production IAM namespace and policy-size checks.
Live module validation
This confirmed that the updated module inputs and outputs connect correctly in the full live configuration without accessing the remote backend.
testing with LocalStack
I used LocalStack fixture (not included in this PR) and fake values only.
First, I created the migration resources in
preparemode:tflocal -chdir=infrastructure/localstack init -backend=false -input=false tflocal -chdir=infrastructure/localstack apply \ -auto-approve \ -input=false \ -var='runtime_secrets_mode=prepare'I then listed both stores:
The SSM output contained the existing
Stringand secret-valuedSecureStringparameters, while Secrets Manager contained the new runtime-secret entries. This confirmed thatpreparekeeps the old source available while creating the new one.I added different fake values to each externally managed secret using:
awslocal secretsmanager put-secret-value \ --secret-id /nest/localstack/GITHUB_TOKEN \ --secret-string 'fake-local-github-token'I repeated this for the other external secrets. Each response contained:
I ran the deployment preflight against LocalStack:
AWS_ACCESS_KEY_ID=test \ AWS_SECRET_ACCESS_KEY=test \ AWS_DEFAULT_REGION=us-east-1 \ AWS_REGION=us-east-1 \ AWS_ENDPOINT_URL=http://localhost:4566 \ bash .github/scripts/verify-runtime-secrets.sh localstack falseIt first rejected a missing value, then passed after all required fake values had an
AWSCURRENTversion. This confirmed thatcompletedeployments are blocked when a secret has not been populated.I then applied
complete:tflocal -chdir=infrastructure/localstack apply \ -auto-approve \ -input=false \ -var='runtime_secrets_mode=complete'The Terraform plan removed the secret-valued SSM resources. A second SSM listing contained only non-secret
Stringconfiguration.Finally, I inspected the generated ECS task definition:
The output showed Secrets Manager ARNs for credentials,
:password::for the database password JSON key, and SSM ARNs only for non-secret configuration. This matches the intended final state from #5071.Final static checks
All three commands passed. I also confirmed that no Terraform state, credentials, tokens, or real secret values are tracked.
The real
prepare → completemigration still needs to be run and checked in staging before production by the maintainers.Checklist