Skip to content

fix(ci): pass every module's Lambda zips from plan to apply - #140

Merged
Alexanderamiri merged 1 commit into
mainfrom
fix/identity-lambda-zip
Oct 23, 2026
Merged

Alexanderamiri merged 1 commit into
mainfrom
fix/identity-lambda-zip

Conversation

@Alexanderamiri

Copy link
Copy Markdown
Member

Why

Applying #137 and #138 failed (run 39978752602):
reading ZIP file (identity/builds/auth_claims.zip): no such file or directory.
The plan job uploads only terraform/platform/lambdas/builds/ as the lambda-zips artifact. #138 added a Lambda in the identity module, whose zip is built at identity/builds/. A saved plan doesn't rebuild archive_file outputs, so the apply job needs every zip.

What changed

  • Upload ${TF_ROOT}/*/builds/*.zip (paths relative to TF_ROOT), and fail if nothing matches.
  • Download into ${TF_ROOT}/, so each zip lands in its own module's builds/.

Checks

actionlint. The real check is this PR's own merge run, which must apply the pending #137/#138 changes (10 to add, 30 to change, MEDIUM).

🤖 Generated with Claude Code

The apply job only downloaded lambdas/builds/, so applying the saved plan
failed once a Lambda lived in another module (auth_claims in identity/):
reading ZIP file identity/builds/auth_claims.zip: no such file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Alexanderamiri
Alexanderamiri requested a review from a team as a code owner October 23, 2026 15:33
@github-actions

Copy link
Copy Markdown

Terraform Plan

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

Plan output
aws_ssm_parameter.atlassian_org_id: Refreshing state... [id=/javabin/platform/atlassian-org-id]
aws_ssm_parameter.atlassian_api_key: Refreshing state... [id=/javabin/platform/atlassian-api-key]

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

Terraform will perform the following actions:

  # module.identity.aws_cognito_user_pool.internal will be updated in-place
  ~ resource "aws_cognito_user_pool" "internal" {
        id                        = "eu-central-1_Icikv3dtD"
        name                      = "javabin-internal"
        tags                      = {
            "Name" = "javabin-internal"
        }
        # (12 unchanged attributes hidden)

      + lambda_config {
          + pre_token_generation_config {
              + lambda_arn     = (known after apply)
              + lambda_version = "V2_0"
            }
        }

        # (7 unchanged blocks hidden)
    }

  # module.identity.aws_lambda_function.auth_claims will be created
  + resource "aws_lambda_function" "auth_claims" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + filename                       = "identity/builds/auth_claims.zip"
      + function_name                  = "javabin-auth-claims"
      + handler                        = "handler.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 256
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + reserved_concurrent_executions = -1
      + role                           = "arn:aws:iam::553637109631:role/javabin-auth-claims"
      + runtime                        = "python3.12"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = "T0XK8BQ05sxP5MVCsIGH+JuY++sBeQohaGTkvMdm81M="
      + source_code_size               = (known after apply)
      + tags_all                       = {
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }
      + timeout                        = 5
      + version                        = (known after apply)

      + environment {
          + variables = {
              + "MEMBERSHIPS_TABLE" = "javabin-auth-memberships"
            }
        }
    }

  # module.identity.aws_lambda_permission.auth_claims_cognito will be created
  + resource "aws_lambda_permission" "auth_claims_cognito" {
      + action              = "lambda:InvokeFunction"
      + function_name       = "javabin-auth-claims"
      + id                  = (known after apply)
      + principal           = "cognito-idp.amazonaws.com"
      + source_arn          = "arn:aws:cognito-idp:eu-central-1:553637109631:userpool/eu-central-1_Icikv3dtD"
      + statement_id        = "AllowCognitoInternalPool"
      + statement_id_prefix = (known after apply)
    }

  # module.lambdas.aws_iam_role_policy.app_auth_sync will be updated in-place
  ~ resource "aws_iam_role_policy" "app_auth_sync" {
        id     = "javabin-app-auth-sync:javabin-app-auth-sync"
        name   = "javabin-app-auth-sync"
      ~ policy = jsonencode(
          ~ {
              ~ Statement = [
                    # (2 unchanged elements hidden)
                    {
                        Action   = [
                            "ssm:GetParameter",
                            "ssm:GetParametersByPath",
                        ]
                        Effect   = "Allow"
                        Resource = [
                            "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/apps",
                            "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/apps/*",
                        ]
                        Sid      = "ReadAuthRequests"
                    },
                  + {
                      + Action   = [
                          + "dynamodb:PutItem",
                          + "dynamodb:DeleteItem",
                        ]
                      + Effect   = "Allow"
                      + Resource = "arn:aws:dynamodb:eu-central-1:553637109631:table/javabin-auth-memberships"
                      + Sid      = "WriteClientAllowedGroups"
                    },
                    {
                        Action   = [
                            "ssm:PutParameter",
                            "ssm:DeleteParameter",
                            "ssm:DeleteParameters",
                            "ssm:AddTagsToResource",
                        ]
                        Effect   = "Allow"
                        Resource = [
                            "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/apps/*/*/cognito-client-id",
                            "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/apps/*/*/cognito-client-secret",
                        ]
                        Sid      = "WriteClientCredentials"
                    },
                ]
                # (1 unchanged attribute hidden)
            }
        )
        # (1 unchanged attribute hidden)
    }

  # module.lambdas.aws_iam_role_policy.team_provisioner will be updated in-place
  ~ resource "aws_iam_role_policy" "team_provisioner" {
        id     = "javabin-team-provisioner:javabin-team-provisioner"
        name   = "javabin-team-provisioner"
      ~ policy = jsonencode(
          ~ {
              ~ Statement = [
                  + {
                      + Action   = [
                          + "dynamodb:PutItem",
                          + "dynamodb:DeleteItem",
                          + "dynamodb:Scan",
                        ]
                      + Effect   = "Allow"
                      + Resource = "arn:aws:dynamodb:eu-central-1:553637109631:table/javabin-auth-memberships"
                      + Sid      = "WriteAuthMemberships"
                    },
                    {
                        Action   = "ssm:GetParameter"
                        Effect   = "Allow"
                        Resource = [
                            "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/platform/*",
                            "arn:aws:ssm:eu-central-1:553637109631:parameter/javabin/slack/*",
                        ]
                        Sid      = "SSMRead"
                    },
                    # (5 unchanged elements hidden)
                ]
                # (1 unchanged attribute hidden)
            }
        )
        # (1 unchanged attribute hidden)
    }

  # module.lambdas.aws_lambda_function.team_provisioner will be updated in-place
  ~ resource "aws_lambda_function" "team_provisioner" {
        id                             = "javabin-team-provisioner"
      ~ last_modified                  = "2026-05-27T21:26:19.000+0000" -> (known after apply)
      ~ source_code_hash               = "i1n+uCTcgMExISw4NMQHPZPfcNfF4gcnRruD2Fpod1k=" -> "v8Ouuc1eA9Ys9CXK9Nd67GfN/3InEODCoyu1dIx4oOM="
        tags                           = {}
        # (21 unchanged attributes hidden)

      ~ environment {
          ~ variables = {
              + "MEMBERSHIPS_TABLE"            = "javabin-auth-memberships"
                # (15 unchanged elements hidden)
            }
        }

        # (3 unchanged blocks hidden)
    }

Plan: 2 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 adds 2 resources (auth_claims Lambda and its Cognito invoke permission) and updates 4 in place (Cognito user pool pre-token-generation trigger, app_auth_sync and team_provisioner IAM policies, team_provisioner Lambda code); 0 destroys.

  • 🔒 [security] module.lambdas.aws_iam_role_policy.app_auth_sync adds dynamodb:PutItem and DeleteItem on javabin-auth-memberships, and ssm:PutParameter, DeleteParameter(s) and AddTagsToResource on /javabin/apps///cognito-client-id and cognito-client-secret. This lets the app auth sync Lambda write and delete client credentials and membership records. Confirm this is intended and that the wildcard paths cannot match unintended parameters.
  • 🔒 [security] module.lambdas.aws_iam_role_policy.team_provisioner adds dynamodb:PutItem, DeleteItem and Scan on javabin-auth-memberships. Team provisioner can now mutate the membership table that the new auth_claims Lambda reads to build token claims, so a compromised or buggy provisioner could alter access grants.
  • 🔒 [security] New Cognito pre-token-generation V2 trigger invokes javabin-auth-claims, which injects claims into every token issued by the internal user pool. Its code and the membership data it reads determine what claims users receive, so it is a privileged path. Verify the handler and that the Lambda permission is scoped to this user pool only (it is: source_arn is the internal pool ARN).
  • ✅ [routine] New aws_lambda_function.auth_claims (python3.12, 256MB, 5s timeout) and a Cognito invoke permission. Cost is negligible at login volumes. Note the Lambda has no reserved concurrency or dead-letter config; a failing trigger can block sign-ins, so consider monitoring its errors.
  • ✅ [routine] team_provisioner Lambda code hash changes (new deployment package), and the MEMBERSHIPS_TABLE environment variable is added. Also the SSM read scope for team_provisioner is unchanged. The 'last_modified' diff is cosmetic. No destruction and no network or public access changes in this plan.

@Alexanderamiri
Alexanderamiri merged commit 2966987 into main Oct 23, 2026
4 checks passed
@Alexanderamiri
Alexanderamiri deleted the fix/identity-lambda-zip branch October 23, 2026 15:35
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