Skip to content

Keep original boundary description to avoid ForceNew#98

Merged
Alexanderamiri merged 2 commits into
mainfrom
fix/boundary-description
Mar 26, 2026
Merged

Keep original boundary description to avoid ForceNew#98
Alexanderamiri merged 2 commits into
mainfrom
fix/boundary-description

Conversation

@Alexanderamiri
Copy link
Copy Markdown
Member

AWS provider treats IAM policy description changes as ForceNew, which tries to destroy+recreate the policy while it's still attached to 5 roles as a permission boundary. Keep the original description.

Already applied locally — state is clean.

AWS provider treats IAM policy description changes as ForceNew,
which would destroy and recreate the policy while it's still attached
to roles.
@Alexanderamiri Alexanderamiri requested a review from a team as a code owner March 26, 2026 20:04
@github-actions
Copy link
Copy Markdown

Terraform Plan

🚧 Changes detected — Plan: 0 to add, 3 to change, 0 to destroy.

Plan output

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # module.ingress.aws_acm_certificate.wildcard will be updated in-place
  ~ resource "aws_acm_certificate" "wildcard" {
        id                        = "arn:aws:acm:eu-central-1:553637109631:certificate/9b79f56a-3719-4c62-8970-6f08985a7e5b"
        tags                      = {
            "Name" = "javazone.no-wildcard"
        }
        # (15 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # module.lambdas.aws_lambda_function.resource_tagger will be updated in-place
  ~ resource "aws_lambda_function" "resource_tagger" {
        id                             = "javabin-resource-tagger"
      ~ last_modified                  = "2026-03-26T20:02:06.000+0000" -> (known after apply)
      ~ source_code_hash               = "sUcKvezfnFqPwQnbYatW/DuX4YuZUHOSIZJJ9hhO2y0=" -> "lk/mypHvtYyrMSzY8lPGOhrKtJnu0FawUhidl+Nm+2Q="
        tags                           = {}
        # (21 unchanged attributes hidden)

        # (4 unchanged blocks hidden)
    }

  # module.lambdas.aws_ssm_parameter.password_set_function_url will be updated in-place
  ~ resource "aws_ssm_parameter" "password_set_function_url" {
        id        = "/javabin/platform/password-set-function-url"
        name      = "/javabin/platform/password-set-function-url"
        tags      = {}
        # (7 unchanged attributes hidden)
    }

Plan: 0 to add, 3 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

LLM Review

Risk: 🟢 LOW

Routine Lambda function code update and certificate tag management with no infrastructure changes or security implications.

  • [routine] Lambda function resource_tagger code updated (source_code_hash changed). This is a standard function update with no permission or configuration changes.
  • [routine] ACM certificate wildcard tag update for javazone.no. No certificate replacement or validation changes.
  • [routine] SSM parameter password_set_function_url marked for update. No destructive changes or value modifications visible.
  • [routine] Zero resources being created or destroyed. Only 3 in-place updates across Lambda, certificates, and parameters.
  • [routine] No security group, IAM policy, or access control modifications. All existing monitoring, networking, and identity infrastructure remains unchanged.

Use *:* for region:account in NotResource patterns since the boundary
is account-scoped anyway. Frees ~500 bytes of headroom for future
statements (was 2 bytes, now 508).
@github-actions
Copy link
Copy Markdown

Terraform Plan

🚧 Changes detected — Plan: 0 to add, 4 to change, 0 to destroy.

Plan output
Acquiring state lock. This may take a few moments...

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # module.iam.aws_iam_policy.developer_boundary will be updated in-place
  ~ resource "aws_iam_policy" "developer_boundary" {
        id               = "arn:aws:iam::553637109631:policy/javabin-developer-boundary"
        name             = "javabin-developer-boundary"
      ~ policy           = jsonencode(
          ~ {
              ~ Statement = [
                    # (11 unchanged elements hidden)
                    {
                        Action    = [
                            "ec2:DeleteTags",
                            "ec2:CreateTags",
                            "ecs:TagResource",
                            "ecs:UntagResource",
                            "elasticloadbalancing:AddTags",
                            "elasticloadbalancing:RemoveTags",
                            "iam:TagRole",
                            "iam:UntagRole",
                            "sns:TagResource",
                            "sns:UntagResource",
                        ]
                        Condition = {
                            StringEquals = {
                                "aws:ResourceTag/team" = [
                                    "shared",
                                    "platform",
                                ]
                            }
                        }
                        Effect    = "Deny"
                        Resource  = "*"
                        Sid       = "DenyTagStrip"
                    },
                  ~ {
                      ~ Resource = [
                          - "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/platform/*",
                          - "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/slack/*",
                          - "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/platform-overrides/*",
                          + "arn:aws:ssm:*:*:parameter/javabin/platform/*",
                          + "arn:aws:ssm:*:*:parameter/javabin/slack/*",
                          + "arn:aws:ssm:*:*:parameter/javabin/platform-overrides/*",
                        ]
                        # (3 unchanged attributes hidden)
                    },
                  ~ {
                      ~ NotResource = [
                          - "arn:aws:iam::553637109631:role/${aws:PrincipalTag/team}-*",
                          - "arn:aws:iam::553637109631:role/javabin-ecs-execution",
                          + "arn:aws:iam::*:role/${aws:PrincipalTag/team}-*",
                          + "arn:aws:iam::*:role/javabin-ecs-execution",
                        ]
                        # (3 unchanged attributes hidden)
                    },
                  ~ {
                      ~ NotResource = [
                          - "arn:aws:logs:eu-central-1:553637109631:log-group:/ecs/${aws:PrincipalTag/team}/*",
                          - "arn:aws:logs:eu-central-1:553637109631:log-group:/ecs/${aws:PrincipalTag/team}/*:*",
                          + "arn:aws:logs:*:*:log-group:/ecs/${aws:PrincipalTag/team}/*",
                          + "arn:aws:logs:*:*:log-group:/ecs/${aws:PrincipalTag/team}/*:*",
                        ]
                        # (3 unchanged attributes hidden)
                    },
                    {
                        Action   = [
                            "iam:PutRolePolicy",
                            "iam:DeleteRolePolicy",
                            "iam:AttachRolePolicy",
                            "iam:DetachRolePolicy",
                            "iam:DeleteRole",
                            "iam:UpdateRole",
                            "iam:UpdateAssumeRolePolicy",
                        ]
                        Effect   = "Deny"
                        Resource = "arn:aws:iam::553637109631:role/javabin-ci-*"
                        Sid      = "DenyModCI"
                    },
                    {
                        Action    = "ec2:CreateSecurityGroup"
                        Condition = {
                            StringNotEqualsIfExists = {
                                "aws:RequestTag/team" = "${aws:PrincipalTag/team}"
                            }
                        }
                        Effect    = "Deny"
                        Resource  = "*"
                        Sid       = "DenyNoTag"
                    },
                  ~ {
                      ~ NotResource = [
                            "arn:aws:s3:::${aws:PrincipalTag/team}-*",
                          - "arn:aws:dynamodb:eu-central-1:553637109631:table/${aws:PrincipalTag/team}-*",
                          - "arn:aws:sqs:eu-central-1:553637109631:${aws:PrincipalTag/team}-*",
                          - "arn:aws:sns:eu-central-1:553637109631:${aws:PrincipalTag/team}-*",
                          - "arn:aws:rds:eu-central-1:553637109631:db:${aws:PrincipalTag/team}-*",
                          - "arn:aws:rds:eu-central-1:553637109631:subgrp:${aws:PrincipalTag/team}-*",
                          - "arn:aws:ecs:eu-central-1:553637109631:service/*/${aws:PrincipalTag/team}-*",
                          - "arn:aws:ecs:eu-central-1:553637109631:task-definition/${aws:PrincipalTag/team}-*:*",
                          - "arn:aws:ecr:eu-central-1:553637109631:repository/${aws:PrincipalTag/team}-*",
                          - "arn:aws:logs:eu-central-1:553637109631:log-group:/ecs/${aws:PrincipalTag/team}/*",
                          - "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/apps/${aws:PrincipalTag/team}/*",
                          - "arn:aws:lambda:eu-central-1:553637109631:function:${aws:PrincipalTag/team}-*",
                          - "arn:aws:events:eu-central-1:553637109631:rule/${aws:PrincipalTag/team}-*",
                          - "arn:aws:states:eu-central-1:553637109631:stateMachine:${aws:PrincipalTag/team}-*",
                          + "arn:aws:dynamodb:*:*:table/${aws:PrincipalTag/team}-*",
                          + "arn:aws:sqs:*:*:${aws:PrincipalTag/team}-*",
                          + "arn:aws:sns:*:*:${aws:PrincipalTag/team}-*",
                          + "arn:aws:rds:*:*:db:${aws:PrincipalTag/team}-*",
                          + "arn:aws:rds:*:*:subgrp:${aws:PrincipalTag/team}-*",
                          + "arn:aws:ecs:*:*:service/*/${aws:PrincipalTag/team}-*",
                          + "arn:aws:ecs:*:*:task-definition/${aws:PrincipalTag/team}-*:*",
                          + "arn:aws:ecr:*:*:repository/${aws:PrincipalTag/team}-*",
                          + "arn:aws:logs:*:*:log-group:/ecs/${aws:PrincipalTag/team}/*",
                          + "arn:aws:ssm:*:*:parameter/javabin/apps/${aws:PrincipalTag/team}/*",
                          + "arn:aws:lambda:*:*:function:${aws:PrincipalTag/team}-*",
                          + "arn:aws:events:*:*:rule/${aws:PrincipalTag/team}-*",
                          + "arn:aws:states:*:*:stateMachine:${aws:PrincipalTag/team}-*",
                        ]
                        # (3 unchanged attributes hidden)
                    },
                ]
                # (1 unchanged attribute hidden)
            }
        )
        tags             = {
            "Name" = "javabin-developer-boundary"
        }
        # (6 unchanged attributes hidden)
    }

  # module.ingress.aws_acm_certificate.wildcard will be updated in-place
  ~ resource "aws_acm_certificate" "wildcard" {
        id                        = "arn:aws:acm:eu-central-1:553637109631:certificate/9b79f56a-3719-4c62-8970-6f08985a7e5b"
        tags                      = {
            "Name" = "javazone.no-wildcard"
        }
        # (15 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # module.lambdas.aws_lambda_function.resource_tagger will be updated in-place
  ~ resource "aws_lambda_function" "resource_tagger" {
        id                             = "javabin-resource-tagger"
      ~ last_modified                  = "2026-03-26T20:02:06.000+0000" -> (known after apply)
      ~ source_code_hash               = "sUcKvezfnFqPwQnbYatW/DuX4YuZUHOSIZJJ9hhO2y0=" -> "lk/mypHvtYyrMSzY8lPGOhrKtJnu0FawUhidl+Nm+2Q="
        tags                           = {}
        # (21 unchanged attributes hidden)

        # (4 unchanged blocks hidden)
    }

  # module.lambdas.aws_ssm_parameter.password_set_function_url will be updated in-place
  ~ resource "aws_ssm_parameter" "password_set_function_url" {
        id        = "/javabin/platform/password-set-function-url"
        name      = "/javabin/platform/password-set-function-url"
        tags      = {}
        # (7 unchanged attributes hidden)
    }

Plan: 0 to add, 4 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

LLM Review

Risk: 🟡 MEDIUM

Plan updates IAM permission boundary with wildcard region/account changes, updates Lambda function code, and modifies certificate and SSM parameter configurations.

  • 🔒 [security] IAM developer_boundary policy changes from region-specific ARNs (eu-central-1:553637109631) to wildcard region/account ARNs (:). This significantly expands the scope of the permission boundary and could allow developers to access resources outside the intended account/region if they obtain credentials in other regions.
  • [routine] Lambda function resource_tagger code updated (source_code_hash changed). This is a normal function update with no infrastructure changes.
  • [routine] ACM certificate wildcard and SSM parameter password_set_function_url marked for in-place updates. These are configuration refreshes with no destructive changes.
  • 🔒 [security] Multiple resource ARNs in developer_boundary now use wildcards for region and account across DynamoDB, SQS, SNS, RDS, ECS, ECR, Lambda, EventBridge, and Step Functions. This removes the account/region isolation that was previously enforced.

@Alexanderamiri Alexanderamiri merged commit 2b80c44 into main Mar 26, 2026
3 checks passed
@Alexanderamiri Alexanderamiri deleted the fix/boundary-description branch March 26, 2026 20:08
Alexanderamiri added a commit that referenced this pull request May 9, 2026
AWS provider treats IAM policy description changes as ForceNew, which
tries to destroy+recreate the policy while it's still attached to 5
roles as a permission boundary. Keep the original description.

Already applied locally — state is clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant