Skip to content

Automate IAM User Role Policies#3980

Closed
rudransh-shrivastava wants to merge 11 commits intoOWASP:feature/nest-zappa-migrationfrom
rudransh-shrivastava:feature/iam-policy-automation
Closed

Automate IAM User Role Policies#3980
rudransh-shrivastava wants to merge 11 commits intoOWASP:feature/nest-zappa-migrationfrom
rudransh-shrivastava:feature/iam-policy-automation

Conversation

@rudransh-shrivastava
Copy link
Collaborator

@rudransh-shrivastava rudransh-shrivastava commented Feb 17, 2026

Proposed change

Resolves #3976

Added GitHub secrets:
BOOTSTRAP_AWS_ACCESS_KEY_ID
BOOTSTRAP_AWS_SECRET_ACCESS_KEY
BOOTSTRAP_TF_STATE_BUCKET_NAME
BOOTSTRAP_TF_STATE_DYNAMODB_TABLE_NAME

Removed GitHub secrets:
ZAPPA<UPDATEME>

Create nest-backend user with the policies defined in infrastructure/backend/README.md.
Backend state needs to be migrated:

terraform state pull > terraform.tfstate.backup
<recreate state resources>
terraform state push terraform.tfstate.backup

Create nest-bootstrap user with the policies defined in infrastructure/bootstrap/README.md.
Apply the changes to create a role nest-staging user can assume.

The nest-staging user needs minimum policies to access the remote state.
This is defined in infrastructure/README.md

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

@rudransh-shrivastava rudransh-shrivastava linked an issue Feb 17, 2026 that may be closed by this pull request
2 tasks
@github-actions github-actions bot added docs Improvements or additions to documentation ci labels Feb 17, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a Terraform bootstrap flow with per-environment IAM roles and outputs, refactors backend to per-environment S3/DynamoDB state resources, adds bootstrap Terraform files and docs, and updates the CI workflow to run a staging bootstrap job and pass AWS role-assumption parameters into staging plan/deploy steps.

Changes

Cohort / File(s) Summary
CI/CD Workflow
\.github/workflows/run-ci-cd.yaml
Adds bootstrap-staging-nest job; injects AWS role-assumption parameters (role-to-assume, role-session-name, role-duration-seconds, role-external-id) into staging build/plan/deploy steps; updates job dependencies and poetry sync exclusions.
Bootstrap Terraform & docs
infrastructure/bootstrap/..., infrastructure/bootstrap/README.md, infrastructure/bootstrap/terraform.tfvars.example, infrastructure/bootstrap/terraform.tfbackend.example, infrastructure/bootstrap/.terraform.lock.hcl
New bootstrap Terraform (provider, variables, per-environment IAM roles/policies via for_each), outputs, backend example, lockfile, and inline permissions README.
Backend state refactor
infrastructure/backend/main.tf, infrastructure/backend/variables.tf, infrastructure/backend/outputs.tf, infrastructure/backend/README.md
Introduces state_environments variable and converts global state resources to per-environment for_each S3 buckets, logs, lifecycle, object-lock, public-access blocks, DynamoDB locks, updates IAM/KMS policy documents, module.kms usage, and outputs to maps.
Staging backend example & staging docs
infrastructure/staging/terraform.tfbackend.example, infrastructure/staging/README.md
Updates DynamoDB lock table name to include -staging; adds inline permissions README for nest-staging user with STS assumeRole snippet.
Top-level infra docs
infrastructure/README.md, infrastructure/backend/README.md
Adds Bootstrap IAM Role section, reorders setup steps to include bootstrap flow, updates prerequisites/notes and ECS naming references.
Misc
cspell/custom-dict.txt
Adds new dictionary entries replicationgroup and subgrp.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

backend, deploy

Suggested reviewers

  • kasya
  • arkid15r
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Automate IAM User Role Permissions' accurately reflects the main objective of automating IAM user creation and role permission assignment across multiple environments as shown in the changes.
Description check ✅ Passed The PR description is relevant to the changeset, detailing GitHub secrets added, IAM users created (nest-backend and nest-bootstrap), role assumptions (nest-staging), state migration steps, and policy locations.
Linked Issues check ✅ Passed The PR successfully implements the objectives from issue #3976 by automating IAM user role permissions: it adds infrastructure for nest-backend, nest-bootstrap, and nest-staging users/roles with appropriate policies, updates CI/CD workflows, and documents required permissions.
Out of Scope Changes check ✅ Passed All changes are in-scope with issue #3976: infrastructure files define IAM policies and roles, workflows integrate bootstrap authentication, backend/staging README files document permissions, and cspell dictionary additions support the infrastructure terminology.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 17 files

Confidence score: 2/5

  • Security risk: iam:PassRole is granted unconditionally in infrastructure/bootstrap/main.tf, which nullifies the iam:PassedToService condition and could allow unintended role passing.
  • prevent_destroy = false on the Terraform state bucket in infrastructure/backend/main.tf removes a key safeguard against accidental state deletion, which is a concrete operational risk.
  • Given the high-severity IAM and state-safety issues, this change carries elevated risk and isn’t a straightforward merge.
  • Pay close attention to infrastructure/bootstrap/main.tf and infrastructure/backend/main.tf - overly broad IAM permissions and disabled state-bucket protection.
Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="infrastructure/backend/main.tf">

<violation number="1" location="infrastructure/backend/main.tf:92">
P1: `prevent_destroy = false` on the Terraform state bucket removes the safety net against accidental deletion of state files. If this was changed to facilitate the migration from a single resource to `for_each`, it should be reverted to `true` in a follow-up (or in this PR after state migration). The project README explicitly notes `prevent_destroy` should be `true` by default and only set to `false` before intentional destruction.</violation>
</file>

<file name="infrastructure/bootstrap/main.tf">

<violation number="1" location="infrastructure/bootstrap/main.tf:57">
P2: `CloudWatchEventsManagement` grants `events:*` actions on `resources = ["*"]`, which fully supersedes the scoped `EventBridgeManagement` statement in part_two (CloudWatch Events and EventBridge share the `events:` IAM prefix). This is likely unintended — the events permissions should probably be scoped to project-specific rules as in `EventBridgeManagement`, not open to all resources. Consider removing this wildcard statement and relying on the scoped one, or scoping the resources here.</violation>

<violation number="2" location="infrastructure/bootstrap/main.tf:374">
P1: Security: `iam:PassRole` in the `IAMManagement` statement is granted unconditionally on the same resources as the `IAMPassRole` statement, making the `StringEquals` condition on `iam:PassedToService` completely ineffective. Any principal with this policy can pass these roles to *any* AWS service, not just `ecs-tasks`, `lambda`, and `rds` as intended.

Remove `iam:PassRole` from the `IAMManagement` actions list so that PassRole is only granted through the condition-restricted `IAMPassRole` statement.</violation>
</file>

<file name="infrastructure/bootstrap/README.md">

<violation number="1" location="infrastructure/bootstrap/README.md:63">
P2: The IAM resource ARNs are hardcoded to a specific account ID even though the note says to replace ${AWS_ACCOUNT_ID}. Use the placeholder variable here to keep the example accurate and avoid copying the wrong account ID.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
infrastructure/README.md (1)

102-108: ⚠️ Potential issue | 🟡 Minor

Navigation hint references wrong directory after bootstrap step.

Line 104 says "If you are in infrastructure/backend" but after completing step 2 the user is in infrastructure/bootstrap/. The cd command cd ../staging/ would be correct from either, but the hint text is misleading.

📝 Proposed fix
-  - Navigate to the main infrastructure directory. If you are in `infrastructure/backend`, you can use:
+  - Navigate to the main infrastructure directory. If you are in `infrastructure/bootstrap`, you can use:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/README.md` around lines 102 - 108, The hint text under "Setup
Main Infrastructure (staging)" is misleading: update the explanatory sentence
that currently reads "If you are in `infrastructure/backend`" to reference the
actual prior step location (`infrastructure/bootstrap`) or remove the
conditional entirely so it simply shows the correct command `cd ../staging/`;
locate the "Setup Main Infrastructure (staging)" section and edit the
surrounding sentence accordingly to ensure the `cd ../staging/` hint matches the
previous step.
infrastructure/backend/main.tf (1)

76-101: ⚠️ Potential issue | 🟡 Minor

Consider adding a DynamoDB-specific statement to the KMS key policy with kms:ViaService condition.

The current KMS key policy allows the root account unrestricted kms:* permissions, which technically permits DynamoDB encryption operations. However, per the learning from PR #3581, best practice is to include an explicit statement with kms:ViaService condition set to dynamodb.*.amazonaws.com and required actions: kms:Encrypt, kms:Decrypt, kms:ReEncrypt*, kms:GenerateDataKey*, kms:DescribeKey, and kms:CreateGrant. This restricts key usage to the DynamoDB service specifically.

Regarding prevent_destroy = false: While state-locking tables are sensitive resources, this infrastructure is designated for testing purposes only per learnings, where production-grade protection controls are not required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/backend/main.tf` around lines 76 - 101, Add a
DynamoDB-specific statement to the KMS key policy used by module.kms so DynamoDB
can use the key only via the service: add a new policy statement (referencing
the KMS resource created in module.kms) that grants Principal {"Service":
"dynamodb.amazonaws.com"} the actions kms:Encrypt, kms:Decrypt, kms:ReEncrypt*,
kms:GenerateDataKey*, kms:DescribeKey and kms:CreateGrant and include the
Condition {"StringEquals": {"kms:ViaService": "dynamodb.*.amazonaws.com"}}; this
ties the key to the aws_dynamodb_table.state_lock usage while keeping the
existing broader admin/root permissions intact.
🧹 Nitpick comments (5)
infrastructure/INFO.md (1)

7-42: Minor: Inconsistent indentation in the JSON policy document.

The JSON block mixes tabs and spaces (e.g., Line 8 uses tabs while Lines 24-39 use spaces). Consider normalizing to a consistent style for readability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/INFO.md` around lines 7 - 42, The JSON policy uses mixed tabs
and spaces causing inconsistent indentation; normalize the file to a single
indentation style (preferably 2 or 4 spaces) throughout by replacing tab
characters with spaces for all objects and arrays (including the top-level
object and nested statements with S3StateManagement, DynamoDBStateManagement,
and KMSStateManagement) so the keys and nested blocks align consistently for
readability.
.github/workflows/run-ci-cd.yaml (1)

625-690: Bootstrap runs terraform apply -auto-approve on every push to main.

Running terraform apply -auto-approve unconditionally on every merge to main adds latency to the pipeline even when bootstrap resources haven't changed. While bootstrap resources should be idempotent, consider adding a plan-check step that skips apply when there are no changes, or gate this on changes to infrastructure/bootstrap/** using a path filter.

Additionally, the bootstrap job uses separate BOOTSTRAP_AWS_ACCESS_KEY_ID / BOOTSTRAP_AWS_SECRET_ACCESS_KEY secrets (lines 646–648) without role assumption, while all other staging jobs assume the nest-staging-terraform role. Ensure the bootstrap IAM user has appropriately scoped permissions (least privilege) since it runs with direct credentials.

infrastructure/backend/main.tf (1)

233-244: State bucket uses AES256 (S3-managed keys) while DynamoDB uses customer-managed KMS.

The DynamoDB table (line 99) uses module.kms.key_arn for encryption, but the state S3 bucket uses AES256. For consistent encryption posture, consider using the same KMS key for the state bucket. The #trivy:ignore suppression suggests this was a conscious choice, but it creates an inconsistency worth documenting if intentional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/backend/main.tf` around lines 233 - 244, The S3 state bucket
resource aws_s3_bucket_server_side_encryption_configuration.state currently uses
sse_algorithm = "AES256", which is inconsistent with the DynamoDB's
customer-managed KMS (module.kms.key_arn); change the configuration to use KMS
by setting sse_algorithm = "aws:kms" and adding kms_master_key_id =
module.kms.key_arn (for_each still driven by local.state_environments and bucket
= aws_s3_bucket.state[each.key].id), and either remove or update the
`#trivy`:ignore comment and add a short note in code/comments explaining the
intentional use of module.kms.key_arn for consistent encryption if you preserve
the suppression.
infrastructure/bootstrap/main.tf (2)

199-216: ECRAuth statement grants write actions (PutImage, UploadLayerPart, etc.) on resources = ["*"].

Only ecr:GetAuthorizationToken requires * resources. The remaining actions (e.g., BatchCheckLayerAvailability, PutImage, CompleteLayerUpload) support resource-level restrictions and are already scoped in ECRManagement (lines 218–238). The * scope here effectively bypasses that scoping.

Consider keeping only ecr:GetAuthorizationToken in this statement and leaving the rest in ECRManagement.

♻️ Proposed fix
   statement {
     sid    = "ECRAuth"
     effect = "Allow"
     actions = [
-      "ecr:BatchCheckLayerAvailability",
-      "ecr:BatchGetImage",
-      "ecr:CompleteLayerUpload",
-      "ecr:DescribeRepositories",
       "ecr:GetAuthorizationToken",
-      "ecr:GetDownloadUrlForLayer",
-      "ecr:GetRepositoryPolicy",
-      "ecr:InitiateLayerUpload",
-      "ecr:ListImages",
-      "ecr:PutImage",
-      "ecr:UploadLayerPart",
     ]
     resources = ["*"]
   }

Then add the removed actions to the scoped ECRManagement statement if not already present:

   statement {
     sid    = "ECRManagement"
     effect = "Allow"
     actions = [
+      "ecr:BatchCheckLayerAvailability",
+      "ecr:BatchGetImage",
+      "ecr:CompleteLayerUpload",
       "ecr:CreateRepository",
       "ecr:DeleteLifecyclePolicy",
       "ecr:DeleteRepository",
       "ecr:DescribeRepositories",
+      "ecr:GetDownloadUrlForLayer",
       "ecr:GetLifecyclePolicy",
       "ecr:GetRepositoryPolicy",
+      "ecr:InitiateLayerUpload",
+      "ecr:ListImages",
       "ecr:ListTagsForResource",
+      "ecr:PutImage",
       "ecr:PutImageScanningConfiguration",
       "ecr:PutLifecyclePolicy",
       "ecr:SetRepositoryPolicy",
       "ecr:TagResource",
       "ecr:UntagResource",
+      "ecr:UploadLayerPart",
     ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/bootstrap/main.tf` around lines 199 - 216, The ECRAuth
statement currently grants global write permissions; update the statement with
sid "ECRAuth" to only include "ecr:GetAuthorizationToken" and keep resources =
["*"], remove all other ECR actions from that statement, and then ensure the
removed actions (e.g., "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage",
"ecr:CompleteLayerUpload", "ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload", "ecr:ListImages", "ecr:PutImage",
"ecr:UploadLayerPart", etc.) are present in the scoped statement with sid
"ECRManagement" (add them there if missing) so write operations are
resource-scoped rather than "*".

543-559: SSM resources are overly broad.

arn:aws:ssm:*:${account}:* covers all SSM resource types (parameters, documents, sessions, etc.) across all regions. Consider narrowing to the specific parameter path prefix used by the project, e.g.:

resources = [
  "arn:aws:ssm:*:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}-${each.key}-*",
  "arn:aws:ssm:*:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}/${each.key}/*",
]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/bootstrap/main.tf` around lines 543 - 559, The SSM statement
with sid "SSMManagement" currently uses an overly broad resource ARN
("arn:aws:ssm:*:${data.aws_caller_identity.current.account_id}:*"); update the
resources array in that statement to restrict access to only the parameter ARNs
your app uses by replacing the wildcard with parameter-specific ARNs (use the
"parameter/" prefix and incorporate project identifiers like var.project_name
and each.key or the project's parameter path prefix) so only the intended SSM
Parameter resources are allowed instead of all SSM resource types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@infrastructure/bootstrap/main.tf`:
- Around line 412-436: The KMS policy statement with sid "KMSManagement"
currently uses resources = ["*"] which grants management over all keys; tighten
scope by replacing the wildcard with project-scoped ARNs and/or tag-based
conditions: change resources from ["*"] to scoped ARNs (e.g., KMS alias/key ARNs
that include var.project_name like alias/${var.project_name}-*) and/or require a
Condition such as "kms:ResourceAlias" or "kms:ResourceTag/Project" ==
var.project_name so actions in the "KMSManagement" statement only apply to
keys/aliases owned by this project rather than all account keys. Ensure you
reference the same statement block (sid = "KMSManagement") when making the
change.
- Around line 355-410: The IAMManagement statement currently includes
iam:PassRole which overrides the conditional restriction in the IAMPassRole
statement; remove "iam:PassRole" from the actions list in the IAMManagement
statement (the block with sid = "IAMManagement") so that the only Allow for
iam:PassRole comes from the IAMPassRole statement (sid = "IAMPassRole") which
enforces the StringEquals iam:PassedToService condition for
ecs-tasks.amazonaws.com, lambda.amazonaws.com and rds.amazonaws.com; ensure no
other statement duplicates an unconditional iam:PassRole on the same resources.
- Around line 562-583: Add a prerequisite note to infrastructure/README.md under
the "Bootstrap IAM Role" section explaining that the IAM users trusted by the
aws_iam_role "terraform" resource must exist before running terraform apply;
state they must be created manually or via a separate provisioning process,
follow the naming convention ${project_name}-${environment} (e.g., nest-staging,
nest-production) and be created for each environment in local.environments, and
emphasize creation happens prior to running the bootstrap terraform apply so the
assume-role policy (trusting user/${var.project_name}-${each.key}) can succeed.

In `@infrastructure/bootstrap/README.md`:
- Around line 62-65: The policy snippet currently hardcodes the AWS account ID
in the Resource ARNs ("arn:aws:iam::652192963764:role/nest-*-terraform" and
"arn:aws:iam::652192963764:policy/nest-*-terraform"); replace the literal
`652192963764` with the `${AWS_ACCOUNT_ID}` placeholder in both ARNs so the
Resource array consistently uses the configured variable, and ensure any
surrounding README text mentions the `${AWS_ACCOUNT_ID}` substitution where
examples are given.

In `@infrastructure/INFO.md`:
- Around line 33-40: The KMS policy block "KMSStateManagement" only allows
"kms:Decrypt" which is insufficient for S3 PutObject operations using a KMS CMK;
update the policy referenced by the KMSStateManagement block to include
"kms:GenerateDataKey" (add it to the "Action" array alongside "kms:Decrypt") so
Terraform/state writes can obtain data keys for encryption before PutObject.

In `@infrastructure/README.md`:
- Around line 96-100: Update the misleading sentence "Apply the changes to
create the backend resources" in the Bootstrap IAM Role step so it clearly
describes bootstrapping IAM resources; replace that exact line with wording such
as "Apply the changes to create the bootstrap IAM resources" (or similar) in the
same section that contains the "Bootstrap IAM Role" heading to ensure the
description matches the step.

---

Outside diff comments:
In `@infrastructure/backend/main.tf`:
- Around line 76-101: Add a DynamoDB-specific statement to the KMS key policy
used by module.kms so DynamoDB can use the key only via the service: add a new
policy statement (referencing the KMS resource created in module.kms) that
grants Principal {"Service": "dynamodb.amazonaws.com"} the actions kms:Encrypt,
kms:Decrypt, kms:ReEncrypt*, kms:GenerateDataKey*, kms:DescribeKey and
kms:CreateGrant and include the Condition {"StringEquals": {"kms:ViaService":
"dynamodb.*.amazonaws.com"}}; this ties the key to the
aws_dynamodb_table.state_lock usage while keeping the existing broader
admin/root permissions intact.

In `@infrastructure/README.md`:
- Around line 102-108: The hint text under "Setup Main Infrastructure (staging)"
is misleading: update the explanatory sentence that currently reads "If you are
in `infrastructure/backend`" to reference the actual prior step location
(`infrastructure/bootstrap`) or remove the conditional entirely so it simply
shows the correct command `cd ../staging/`; locate the "Setup Main
Infrastructure (staging)" section and edit the surrounding sentence accordingly
to ensure the `cd ../staging/` hint matches the previous step.

---

Nitpick comments:
In `@infrastructure/backend/main.tf`:
- Around line 233-244: The S3 state bucket resource
aws_s3_bucket_server_side_encryption_configuration.state currently uses
sse_algorithm = "AES256", which is inconsistent with the DynamoDB's
customer-managed KMS (module.kms.key_arn); change the configuration to use KMS
by setting sse_algorithm = "aws:kms" and adding kms_master_key_id =
module.kms.key_arn (for_each still driven by local.state_environments and bucket
= aws_s3_bucket.state[each.key].id), and either remove or update the
`#trivy`:ignore comment and add a short note in code/comments explaining the
intentional use of module.kms.key_arn for consistent encryption if you preserve
the suppression.

In `@infrastructure/bootstrap/main.tf`:
- Around line 199-216: The ECRAuth statement currently grants global write
permissions; update the statement with sid "ECRAuth" to only include
"ecr:GetAuthorizationToken" and keep resources = ["*"], remove all other ECR
actions from that statement, and then ensure the removed actions (e.g.,
"ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage",
"ecr:CompleteLayerUpload", "ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload", "ecr:ListImages", "ecr:PutImage",
"ecr:UploadLayerPart", etc.) are present in the scoped statement with sid
"ECRManagement" (add them there if missing) so write operations are
resource-scoped rather than "*".
- Around line 543-559: The SSM statement with sid "SSMManagement" currently uses
an overly broad resource ARN
("arn:aws:ssm:*:${data.aws_caller_identity.current.account_id}:*"); update the
resources array in that statement to restrict access to only the parameter ARNs
your app uses by replacing the wildcard with parameter-specific ARNs (use the
"parameter/" prefix and incorporate project identifiers like var.project_name
and each.key or the project's parameter path prefix) so only the intended SSM
Parameter resources are allowed instead of all SSM resource types.

In `@infrastructure/INFO.md`:
- Around line 7-42: The JSON policy uses mixed tabs and spaces causing
inconsistent indentation; normalize the file to a single indentation style
(preferably 2 or 4 spaces) throughout by replacing tab characters with spaces
for all objects and arrays (including the top-level object and nested statements
with S3StateManagement, DynamoDBStateManagement, and KMSStateManagement) so the
keys and nested blocks align consistently for readability.

@codecov
Copy link

codecov bot commented Feb 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.32%. Comparing base (fc6363b) to head (bd94f8a).

Additional details and impacted files

Impacted file tree graph

@@                      Coverage Diff                      @@
##           feature/nest-zappa-migration    #3980   +/-   ##
=============================================================
  Coverage                         93.32%   93.32%           
=============================================================
  Files                               513      513           
  Lines                             15827    15827           
  Branches                           2178     2134   -44     
=============================================================
  Hits                              14770    14770           
  Misses                              695      695           
  Partials                            362      362           
Flag Coverage Δ
backend 92.88% <ø> (ø)
frontend 94.64% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fc6363b...bd94f8a. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="infrastructure/README.md">

<violation number="1" location="infrastructure/README.md:402">
P2: The staging assume-role policy example is not a valid IAM policy document (missing Version/Statement). Users copying this will get a malformed policy error. Wrap the statement in a standard policy document.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
infrastructure/backend/main.tf (1)

114-160: Logs lifecycle is missing noncurrent_version_expiration now that versioning is enabled.

aws_s3_bucket_versioning.logs (lines 154–160) newly enables versioning on the logs bucket. However, aws_s3_bucket_lifecycle_configuration.logs (lines 114–128) only has expiration.days — on a versioned bucket, that rule creates a delete marker rather than removing the data, so old log versions accumulate indefinitely. By contrast, the state bucket lifecycle (lines 189–191) already includes noncurrent_version_expiration.

♻️ Proposed fix
  rule {
    id     = "expire-logs"
    status = "Enabled"

    abort_incomplete_multipart_upload {
      days_after_initiation = var.abort_incomplete_multipart_upload_days
    }
    expiration {
      days = var.expire_log_days
    }
+   noncurrent_version_expiration {
+     noncurrent_days = var.expire_log_days
+   }
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/backend/main.tf` around lines 114 - 160, The logs S3 lifecycle
only sets expiration on a versioned bucket, which deletes the current object by
creating delete markers but leaves prior versions; update
aws_s3_bucket_lifecycle_configuration.logs to include a
noncurrent_version_expiration block so old object versions are actually removed
(e.g., add noncurrent_version_expiration { days = var.expire_log_days } or
another appropriate var) alongside the existing
expiration/abort_incomplete_multipart_upload so that
aws_s3_bucket_versioning.logs does not cause indefinite accumulation of old
versions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@infrastructure/README.md`:
- Around line 81-88: Remove the blank lines between adjacent blockquote NOTE
callouts so MD028 is resolved: either consolidate the three separate `> [!NOTE]`
blocks into a single `> [!NOTE]` block containing the combined text, or place
the `> [!NOTE]` lines directly adjacent with no empty lines between them (i.e.,
remove the empty lines between the `> [!NOTE]` blocks in the README). Ensure the
final content preserves all guidance (state bucket, defaults, optional region
via `aws_region`) within the single or adjacent NOTE block(s).
- Around line 428-435: The KMS statement with Sid "KMSStateManagement" only
grants kms:Decrypt and must allow DynamoDB to generate and use data keys for
lock table writes; update the policy statement in the "KMSStateManagement" block
to include at minimum kms:GenerateDataKey, kms:Encrypt, kms:Decrypt and
kms:CreateGrant (keeping Effect "Allow" and Resource
"${AWS_BACKEND_KMS_KEY_ARN}") so Terraform/DynamoDB can create/encrypt/decrypt
table keys and create grants for lock acquisition.
- Around line 440-450: Replace the malformed policy snippet with a valid IAM
policy document and fix the grammar: wrap the existing statement (Sid
"STSManagement", Effect "Allow", Action ["sts:AssumeRole","sts:TagSession"],
Resource "arn:aws:iam::${AWS_ACCOUNT_ID}:role/nest-staging-terraform") inside
the required top-level JSON keys ("Version": "2012-10-17" and "Statement": [ ...
]), and update the README sentence to read "staging/ also requires the following
policy to assume its role" (replace "it's" with "its" and add "to").
- Line 14: Remove the broken reference to INFO.md or add the missing INFO.md
file: either delete the line containing "Read `INFO.md` for information related
to policies" from the README, or create an `INFO.md` that duplicates the
"Required Policies" content (the AWS IAM policies documented under the "Required
Policies" section) and ensure the README link points to it; update any
anchor/link text to reference the existing "Required Policies" section if you
choose to remove INFO.md.

---

Duplicate comments:
In `@infrastructure/README.md`:
- Line 96: Replace the misleading sentence "Apply the changes to create the
backend resources" in the README with text that references the bootstrap step
(e.g., "Apply the changes to create the bootstrap resources" or "Apply the
changes to bootstrap the environment") so it correctly describes the bootstrap
step; locate the exact string "Apply the changes to create the backend
resources" and update it accordingly.

---

Nitpick comments:
In `@infrastructure/backend/main.tf`:
- Around line 114-160: The logs S3 lifecycle only sets expiration on a versioned
bucket, which deletes the current object by creating delete markers but leaves
prior versions; update aws_s3_bucket_lifecycle_configuration.logs to include a
noncurrent_version_expiration block so old object versions are actually removed
(e.g., add noncurrent_version_expiration { days = var.expire_log_days } or
another appropriate var) alongside the existing
expiration/abort_incomplete_multipart_upload so that
aws_s3_bucket_versioning.logs does not cause indefinite accumulation of old
versions.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="infrastructure/README.md">

<violation number="1" location="infrastructure/README.md:442">
P2: The policy example is invalid JSON (extra object wrapper and missing statement object). This will break copy/paste usage and produce an invalid IAM policy.</violation>
</file>

<file name="infrastructure/bootstrap/main.tf">

<violation number="1" location="infrastructure/bootstrap/main.tf:118">
P1: Missing ElastiCache resource ARNs for subnet groups and replication groups. The policy grants actions like `CreateCacheSubnetGroup`, `ModifyReplicationGroup`, etc., but the `resources` block only includes `cluster:*` ARN patterns. ElastiCache subnet group and replication group operations use different ARN resource types (`subnetgroup:` and `replicationgroup:`), so these actions will be denied by IAM.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
infrastructure/bootstrap/main.tf (2)

188-196: EC2ResourceSpecific statement is dead code — remove it.

ec2:DescribeFlowLogs and ec2:DescribeNetworkAcls are already covered by the ec2:Describe* wildcard in EC2Management (Line 170) on resources = ["*"]. The more restrictive scoped-resource statement here is fully superseded and has no effect.

♻️ Proposed fix
-  statement {
-    sid    = "EC2ResourceSpecific"
-    effect = "Allow"
-    actions = [
-      "ec2:DescribeFlowLogs",
-      "ec2:DescribeNetworkAcls",
-    ]
-    resources = ["arn:aws:ec2:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"]
-  }
-
   statement {
     sid    = "ECRAuth"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/bootstrap/main.tf` around lines 188 - 196, Remove the dead IAM
policy statement named EC2ResourceSpecific: the statement block that defines sid
= "EC2ResourceSpecific" and lists actions
["ec2:DescribeFlowLogs","ec2:DescribeNetworkAcls"] with the scoped resources ARN
should be deleted because those actions are already covered by the broader
EC2Management statement (sid or block referenced as EC2Management) that grants
"ec2:Describe*" on resources = ["*"]; simply delete the EC2ResourceSpecific
statement block from the policy to avoid redundant, superseded code.

269-285: ECSOrchestration breaks per-environment isolation — add ${each.key} to resource ARNs.

All four resource patterns use ${var.project_name}-* without the ${each.key} segment, meaning every environment role can run tasks, stop tasks, or update services on any environment's cluster or service. Every other service-level statement (ECSClusterManagement, ECSServiceManagement, ECSTaskDefinition) correctly includes ${each.key}.

♻️ Proposed fix
    resources = [
-     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:task-definition/${var.project_name}-*:*",
-     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:service/${var.project_name}-*/*",
-     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:cluster/${var.project_name}-*",
-     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:task/${var.project_name}-*/*"
+     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:task-definition/${var.project_name}-${each.key}-*:*",
+     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:service/${var.project_name}-${each.key}-*/*",
+     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:cluster/${var.project_name}-${each.key}-*",
+     "arn:aws:ecs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:task/${var.project_name}-${each.key}-*/*"
    ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/bootstrap/main.tf` around lines 269 - 285, The
ECSOrchestration IAM policy statement (sid "ECSOrchestration") is too permissive
because its resource ARNs use "${var.project_name}-*" without the
per-environment discriminator; update the four ARNs in that statement to include
"${each.key}" in the name pattern (same style used by
ECSClusterManagement/ECSServiceManagement/ECSTaskDefinition) so each generated
role only targets its own environment; ensure you insert "${each.key}" in the
ARN segment after "${var.project_name}" for task-definition, service, cluster,
and task resources.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@infrastructure/bootstrap/main.tf`:
- Around line 87-120: The ElastiCache ARNs in the IAM statement only include the
cluster: type so ReplicationGroup and CacheSubnetGroup actions
(CreateReplicationGroup, DeleteReplicationGroup, ModifyReplicationGroup,
CreateCacheSubnetGroup, DeleteCacheSubnetGroup, ModifyCacheSubnetGroup) will be
denied; update the resources array used by this statement to also include the
appropriate ElastiCache ARN patterns for replicationgroup and subnetgroup (e.g.,
add entries analogous to
"arn:aws:elasticache:${var.aws_region}:${data.aws_caller_identity.current.account_id}:replicationgroup:${var.project_name}-${each.key}-*"
and
"arn:aws:elasticache:${var.aws_region}:${data.aws_caller_identity.current.account_id}:subnetgroup:${var.project_name}-${each.key}-*")
so the listed ElastiCache actions can succeed.
- Line 117: The CSpell checker is flagging the AWS RDS ARN segment "subgrp" (as
seen in the string "arn:aws:rds:...:subgrp:...") as an unknown word; add the
token subgrp to your CSpell custom dictionary (custom-dict.txt) so the
spellcheck accepts RDS DB subnet group ARNs. Update the dictionary by adding a
single line with subgrp, commit the change, and rerun the spellcheck to verify
the warning is cleared.

In `@infrastructure/README.md`:
- Line 397: Fix the spelling and punctuation in the README line that currently
reads "All users (`nest-staging`, `nest-bootstrap`, etc) require these minumum
policies to use the terraform remote backend:" by changing "minumum" to
"minimum" and adding a period after the parenthesis so it becomes "... etc.)
require these minimum policies to use the terraform remote backend."; update the
exact string in the README to reflect these corrections.

---

Duplicate comments:
In `@infrastructure/bootstrap/main.tf`:
- Around line 410-419: The existing IAM statement with sid "KMSManagement"
leaves kms:ListResourceTags scoped to "*"—change this by splitting the
statement: keep a statement (e.g., sid "KMSManagement_CreateAndAliases") that
allows "kms:CreateKey" and "kms:ListAliases" with resources = ["*"], and add a
separate statement (e.g., sid "KMSManagement_ListResourceTags") that grants only
"kms:ListResourceTags" with resources set to the project-specific KMS key ARNs
(use your existing aws_kms_key resources, data sources, or a variable that lists
the key ARNs). Locate the statement with sid "KMSManagement" and replace it with
these two statements so kms:ListResourceTags is no longer a wildcard.

In `@infrastructure/README.md`:
- Around line 439-454: The README's STS policy block and a grammar mistake are
incorrect: fix the malformed JSON for the nest-staging IAM User so it is valid
JSON and a proper IAM policy (remove the extra outer brace, ensure "Statement"
is an array of object(s) with string "Sid", "Effect", array "Action", and string
"Resource", e.g. a single object with "Sid": "STSManagement", "Effect": "Allow",
"Action": ["sts:AssumeRole","sts:TagSession"], "Resource":
"arn:aws:iam::${AWS_ACCOUNT_ID}:role/nest-staging-terraform"), and correct the
wording from "it's own role" to "its own role".
- Line 95: Update the misleading sentence under the "Bootstrap IAM Role" step
that currently reads "Apply the changes to create the backend resources:" so it
accurately describes this step; locate the "Bootstrap IAM Role" heading and
replace that sentence with a precise description such as "Apply the changes to
create the bootstrap IAM role and related resources:" (or similar wording) so
the step reflects creating the IAM role rather than generic backend resources.
- Around line 427-434: The KMSStateManagement statement only allows kms:Decrypt
but needs additional KMS actions for DynamoDB/remote state lock writes; update
the statement with the Sid "KMSStateManagement" to include at minimum
"kms:Encrypt", "kms:GenerateDataKey", "kms:ReEncrypt*", and "kms:DescribeKey"
(keep "kms:Decrypt" as well) and scope them to the same Resource
"${AWS_BACKEND_KMS_KEY_ARN}" so DynamoDB lock and Terraform state operations can
encrypt/decrypt and generate data keys properly.
- Around line 80-87: Remove the blank lines between the consecutive blockquote
note markers so markdownlint MD028 is not triggered: locate the three
consecutive lines beginning with "> [!NOTE]" in infrastructure/README.md and
ensure each "> [!NOTE]" block starts immediately on the next line (no empty line
separating them) so the note blocks are adjacent; if you intentionally need
spacing, combine related notes into a single block or use a different separator
(e.g., a heading) instead of blank lines between the "> [!NOTE]" blocks.

---

Nitpick comments:
In `@infrastructure/bootstrap/main.tf`:
- Around line 188-196: Remove the dead IAM policy statement named
EC2ResourceSpecific: the statement block that defines sid =
"EC2ResourceSpecific" and lists actions
["ec2:DescribeFlowLogs","ec2:DescribeNetworkAcls"] with the scoped resources ARN
should be deleted because those actions are already covered by the broader
EC2Management statement (sid or block referenced as EC2Management) that grants
"ec2:Describe*" on resources = ["*"]; simply delete the EC2ResourceSpecific
statement block from the policy to avoid redundant, superseded code.
- Around line 269-285: The ECSOrchestration IAM policy statement (sid
"ECSOrchestration") is too permissive because its resource ARNs use
"${var.project_name}-*" without the per-environment discriminator; update the
four ARNs in that statement to include "${each.key}" in the name pattern (same
style used by ECSClusterManagement/ECSServiceManagement/ECSTaskDefinition) so
each generated role only targets its own environment; ensure you insert
"${each.key}" in the ARN segment after "${var.project_name}" for
task-definition, service, cluster, and task resources.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
cspell/custom-dict.txt (1)

171-173: ⚠️ Potential issue | 🔴 Critical

Add replicationgroup to unblock the CI pipeline.

The pipeline is currently failing with CSpell: Unknown word (replicationgroup) — triggered by the new arn:aws:elasticache:...:replicationgroup:... ARNs added to infrastructure/bootstrap/main.tf. subgrp was correctly added (resolving the prior error), but replicationgroup also needs to be added.

📝 Proposed fix
  relativedelta
+ replicationgroup
  repositorycontributor
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cspell/custom-dict.txt` around lines 171 - 173, Add the missing token
"replicationgroup" to the custom dictionary so CSpell recognizes ARNs like
"arn:aws:elasticache:...:replicationgroup:..."; update the same list that
contains "relativedelta", "repositorycontributor", and "requirepass" by
inserting "replicationgroup" (exact spelling) to unblock the CI pipeline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@infrastructure/bootstrap/main.tf`:
- Line 486: The IAM policy currently lists an invalid action
"lambda:GetFunctionUrlConfigs" which has no effect; replace that string with the
correct AWS Lambda action "lambda:GetFunctionUrlConfig" so the policy actually
permits fetching a function URL's configuration. Locate the policy statement
containing "lambda:GetFunctionUrlConfigs" and update the action name to the
singular form.

---

Outside diff comments:
In `@cspell/custom-dict.txt`:
- Around line 171-173: Add the missing token "replicationgroup" to the custom
dictionary so CSpell recognizes ARNs like
"arn:aws:elasticache:...:replicationgroup:..."; update the same list that
contains "relativedelta", "repositorycontributor", and "requirepass" by
inserting "replicationgroup" (exact spelling) to unblock the CI pipeline.

---

Duplicate comments:
In `@infrastructure/bootstrap/main.tf`:
- Around line 412-421: The IAM statement with sid "KMSManagement" currently
grants kms:CreateKey, kms:ListAliases, and kms:ListResourceTags to resources =
["*"]; keep kms:CreateKey in a statement with resources = ["*"] (since CreateKey
cannot be resource-scoped) and move kms:ListAliases and kms:ListResourceTags
into a new, separate statement scoped to KMS key ARNs (e.g.,
arn:aws:kms:${var.aws_region}:${data.aws_caller_identity.current.account_id}:key/*
or alias ARNs) to reduce the blast radius; update the policy by splitting the
actions into two statements (one for kms:CreateKey with resources=["*"] and one
for ListAliases/ListResourceTags with the tightened resource ARN pattern).
- Around line 597-608: The trust policy in assume_role_policy references AWS
principal ARN
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/${var.project_name}-${each.key}"
but the README Bootstrap IAM Role section doesn't document creating those IAM
users, so role assumption will fail; either update the README to add a
prerequisite step that creates IAM users named "${var.project_name}-${each.key}"
(or documents a script/CloudFormation to create them) and examples for
var.project_name/each.key, or change the trust policy in assume_role_policy to
reference an existing principal type (e.g., IAM roles or a group/role ARN
pattern) that you actually create in this stack (adjust Principal to "AWS =
\"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/..." or use a
condition), and ensure the README and variables reflect the chosen approach so
operators can successfully assume the role.

In `@infrastructure/README.md`:
- Around line 432-447: The STS assume-role policy snippet uses the wrong Sid and
has a grammar error: replace the misleading "S3StateManagement" Sid with a
clearer "STSManagement" (update the Statement object containing "Action":
["sts:AssumeRole","sts:TagSession"] and "Resource":
"arn:aws:iam::${AWS_ACCOUNT_ID}:role/nest-staging-terraform") and fix the
sentence "to assume it's own role" to the possessive "to assume its own role".
- Around line 420-430: The KMSStateManagement IAM statement only allows
kms:Decrypt which prevents DynamoDB from acquiring/releasing Terraform state
locks; update the statement with the additional required KMS
actions—kms:GenerateDataKey, kms:Encrypt and kms:CreateGrant—so the
"KMSStateManagement" Sid (targeting Resource ${AWS_BACKEND_KMS_KEY_ARN}) permits
Decrypt, GenerateDataKey, Encrypt and CreateGrant for DynamoDB
PutItem/DeleteItem and grant creation during lock operations.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="infrastructure/staging/README.md">

<violation number="1" location="infrastructure/staging/README.md:17">
P2: Remove the trailing comma in the S3 Resource array so the JSON policy is valid.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
infrastructure/bootstrap/main.tf (2)

190-198: EC2ResourceSpecific is dead code — fully shadowed by EC2Management.

ec2:DescribeFlowLogs and ec2:DescribeNetworkAcls are already covered by the ec2:Describe* wildcard in EC2Management (line 172) with resources = ["*"], which is more permissive than the specific ARN here. The entire EC2ResourceSpecific statement can be removed.

♻️ Proposed removal
-  statement {
-    sid    = "EC2ResourceSpecific"
-    effect = "Allow"
-    actions = [
-      "ec2:DescribeFlowLogs",
-      "ec2:DescribeNetworkAcls",
-    ]
-    resources = ["arn:aws:ec2:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"]
-  }
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/bootstrap/main.tf` around lines 190 - 198, The statement block
labeled EC2ResourceSpecific is redundant because the EC2Management statement
already grants ec2:Describe* on resources="*", which subsumes
ec2:DescribeFlowLogs and ec2:DescribeNetworkAcls; remove the entire
EC2ResourceSpecific statement block from the policy to eliminate dead code, and
run a quick plan/apply validation to confirm no other policy depends on that SID
and that overall intended permissions remain unchanged.

464-471: lambda:ListVersionsByFunction in LambdaList (resources = ["*"]) makes the same entry in LambdaManagement (line 492) redundant.

Since the LambdaList statement already grants lambda:ListVersionsByFunction on *, the entry at line 492 adds no effective permission. Consider removing it from LambdaManagement to keep the policy clean.

♻️ Proposed change
   statement {
     sid    = "LambdaManagement"
     ...
     actions = [
       ...
       "lambda:ListFunctionUrlConfigs",
-      "lambda:ListVersionsByFunction",
       "lambda:PublishVersion",
       ...
     ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/bootstrap/main.tf` around lines 464 - 471, The LambdaList
policy statement grants lambda:ListVersionsByFunction on resources = ["*"],
making the identical permission in the LambdaManagement statement redundant;
remove the duplicate lambda:ListVersionsByFunction entry from the
LambdaManagement policy (or collapse/merge the two statements) so the policy
contains a single authoritative LambdaList statement granting that action,
keeping the resource scoping and other LambdaManagement actions intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@infrastructure/bootstrap/main.tf`:
- Around line 488-490: The LambdaManagement IAM statement currently lists
"lambda:ListFunctions" but that action does not support resource-level ARNs;
remove "lambda:ListFunctions" from the LambdaManagement statement and add it to
the LambdaList statement (which already uses resources = ["*"]) so the action is
granted with Resource="*"; update the policy document to ensure LambdaManagement
no longer includes "lambda:ListFunctions" and LambdaList includes
"lambda:ListFunctions".

In `@infrastructure/README.md`:
- Line 13: The link fragment `#required-policies` in the README (the line "Refer
to the respective `README.md` files and [Required Policies](`#required-policies`)
for more information.") is broken; either restore a "Required Policies" heading
(e.g., "## Required Policies") with the referenced content or change the link to
an existing heading or file (for example point to an existing policies section
or to a specific README). Update the referenced text in the same README so the
anchor matches an actual heading (or replace the link with a correct
path/heading) and ensure the heading slug exactly matches `required-policies`
(case/spacing) to satisfy markdownlint MD051.
- Line 391: Remove the stray closing brace character '}' at the end of the
README.md file (it appears after the last content block and is a leftover
HCL/JSON artifact); open the file, delete that lone '}' so the file ends with
the intended Markdown content, and save the file to ensure no literal brace is
rendered in the documentation.

In `@infrastructure/staging/README.md`:
- Around line 39-47: The Sid for the STS statement that contains Action
["sts:AssumeRole","sts:TagSession"] and Resource
"arn:aws:iam::${AWS_ACCOUNT_ID}:role/nest-staging-terraform" is a duplicate
("S3StateManagement"); rename that Sid to a unique, descriptive identifier
(e.g., "STSRoleAssumption") so the statement for role assumption is distinct
from the S3 state management statement.
- Line 3: The README note only mentions ${AWS_ACCOUNT_ID} and
${AWS_BACKEND_KMS_KEY_ARN} but omits ${AWS_REGION},
${TERRAFORM_STATE_BUCKET_NAME}, and ${TERRAFORM_DYNAMODB_TABLE_NAME}; update the
note text so it lists all five placeholders (${AWS_ACCOUNT_ID}, ${AWS_REGION},
${TERRAFORM_STATE_BUCKET_NAME}, ${TERRAFORM_DYNAMODB_TABLE_NAME},
${AWS_BACKEND_KMS_KEY_ARN}) and clarifies they must be replaced with appropriate
values wherever they appear in the JSON block.

---

Nitpick comments:
In `@infrastructure/bootstrap/main.tf`:
- Around line 190-198: The statement block labeled EC2ResourceSpecific is
redundant because the EC2Management statement already grants ec2:Describe* on
resources="*", which subsumes ec2:DescribeFlowLogs and ec2:DescribeNetworkAcls;
remove the entire EC2ResourceSpecific statement block from the policy to
eliminate dead code, and run a quick plan/apply validation to confirm no other
policy depends on that SID and that overall intended permissions remain
unchanged.
- Around line 464-471: The LambdaList policy statement grants
lambda:ListVersionsByFunction on resources = ["*"], making the identical
permission in the LambdaManagement statement redundant; remove the duplicate
lambda:ListVersionsByFunction entry from the LambdaManagement policy (or
collapse/merge the two statements) so the policy contains a single authoritative
LambdaList statement granting that action, keeping the resource scoping and
other LambdaManagement actions intact.

@sonarqubecloud
Copy link

@rudransh-shrivastava
Copy link
Collaborator Author

testing CI/CD, will reopen after that.

@rudransh-shrivastava rudransh-shrivastava changed the title Automate IAM User Role Permissions Automate IAM User Role Policies Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci docs Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automate IAM User Role Policies

1 participant

Comments