Skip to content

fix: keep literal arn in aws:SourceArn policy conditions - terraform cycle (#170)#172

Merged
awsandy merged 1 commit into
aws-samples:masterfrom
ecukalla:fix/sourcearn-policy-condition-cycle
Jul 15, 2026
Merged

fix: keep literal arn in aws:SourceArn policy conditions - terraform cycle (#170)#172
awsandy merged 1 commit into
aws-samples:masterfrom
ecukalla:fix/sourcearn-policy-condition-cycle

Conversation

@ecukalla

Copy link
Copy Markdown
Contributor

Fixes #170.

Problem

A full-account import aborts at validation with a dependency cycle:

Validate and Test Plan  ...

Error: Cycle: aws_lambda_function.my-function, aws_iam_role.my-function-role

Validation after fix failed - exiting
exit 020

The trigger is a lambda execution role whose trust policy restricts assumption to the very function that uses it, via an aws:SourceArn condition. Several vendor-supplied CloudFormation stacks emit execution roles shaped like this, so it isn't an unusual shape - and it takes the whole run down, importing nothing.

What generate-config-out produces:

resource "aws_iam_role" "my-function-role" {
  assume_role_policy = jsonencode({
    Statement = [{
      Action = "sts:AssumeRole"
      Condition = {
        ArnEquals = {
          "aws:SourceArn" = "arn:aws:lambda:<region>:<account>:function:my-function"
        }
      }
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
    }]
  })
}

fixtf then de-references that arn, so the role points at the function:

"aws:SourceArn" = aws_lambda_function.my-function.arn     # role -> lambda   (added by fixtf)

while the function necessarily points back at the role:

role = aws_iam_role.my-function-role.arn                  # lambda -> role   (legitimate)

AWS accepts the trust policy because the arn is predictable. Terraform cannot order the graph, so validate fails.

Fix

Keep the literal arn when the attribute is an aws:SourceArn policy condition, mirroring the guard that already sits three lines above it for policy Resource elements:

if tt1=="Resource":
    return t1
# a policy condition ("aws:SourceArn") naming the function that uses this role:
# referencing it would make the role depend on a lambda that already depends on
# the role - aws allows it, terraform can't order it and errors with a cycle
if tt1.strip('"').lower().endswith(":sourcearn"):
    return t1

This is the same principle as #137 (keep account-portable role arns literal in policy documents), extended from policy Resource elements to policy condition keys: an arn inside an IAM policy document should stay literal.

Testing

Environment: aws2tf v6273, terraform 1.15.8, AWS provider 6.53.0, python 3.12.

  • Reproduced on a full-account import (-f) that aborted at exit 020. With this change terraform validate passes, stage 7 plans clean (0 to add, 0 to destroy) and the run imports to completion.
  • Checked every line shape that reaches this branch: "aws:SourceArn" and "AWS:SourceArn" now keep the literal; Resource still keeps the literal; an ordinary lambda arn attribute (function_arn = "arn:aws:lambda:...") still de-references to aws_lambda_function.<name>.arn. Normal de-referencing is unaffected.
  • I also built the resource dependency graph from the generated .tf files afterwards to confirm no cycles remained anywhere else in the config, not just the one terraform happened to report first.

One caveat

The kept literal leaves the account id and region inline, exactly as the existing Resource guard does - the lambda branch returns before reaching the account/region format(...) substitution that other arn types get. Letting these fall through to that substitution would be the nicer end state, but it would also change the Resource, wildcard and cross-region cases, so I've kept it out of this fix. Happy to follow up if you want it.

…ycle)

A lambda execution role whose trust policy restricts assumption to the function
using it carries that function's arn in an "aws:SourceArn" condition.
Dereferencing the arn made the role depend on the lambda, which already depends
on the role via role=, so terraform validate failed with:

  Error: Cycle: aws_lambda_function.X, aws_iam_role.Y

AWS accepts the trust policy because the arn is predictable; terraform cannot
order the graph. Keep the literal arn, mirroring the existing guard for policy
Resource elements and the approach taken in aws-samples#137.

Fixes aws-samples#170
@awsandy
awsandy merged commit f0875d7 into aws-samples:master Jul 15, 2026
@awsandy

awsandy commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

If you get chance to test/fix the caveat mentioned please do

awsandy pushed a commit that referenced this pull request Jul 15, 2026
globals_replace keeps some lambda ARNs literal instead of de-referencing
them to aws_lambda_function.<name>.arn: an aws:SourceArn policy condition or
a policy Resource naming the function that owns the role (de-referencing
those cycles - #170/#172), a wildcard, a cross-region function, or a
function not collected in this run. Those literals were emitted with the
account id and region hard-coded inline.

Route them through a new sub_acct_region() helper that substitutes this
account's id/region with data.aws_caller_identity / data.aws_region - the
same substitution other ARN types already get later in globals_replace. The
function name stays literal, so the #172 cycle fix is preserved; an ARN in a
different account (this account's id absent) is left fully literal.
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.

Lambda execution role trust policy with aws:SourceArn creates a terraform cycle - run aborts at exit 020

2 participants