terraform-aws-modules / terraform-aws-lambda

Terraform module, which takes care of a lot of AWS Lambda/serverless tasks (build dependencies, packages, updates, deployments) in countless combinations 🇺🇦
https://registry.terraform.io/modules/terraform-aws-modules/lambda/aws
Apache License 2.0
901 stars 677 forks source link

Cycle error while using this module with IAM-modules. #603

Closed gowgopal83 closed 12 hours ago

gowgopal83 commented 1 month ago

Description

We use this module to create two different lambda functions(certcheck and certissue) and Certcheck_lambda requires Certissue_lambda function arn for the lambda role IAM policy and as an environment variable.
When we create Lambda's IAM role using resources there are no issues. However, when we use the IAM module to create the Lambda role we end up with cycle errors. Details are given below.

Versions

Providers required by configuration: . ├── provider[registry.terraform.io/hashicorp/aws] ~> 5.32.0 ├── provider[registry.terraform.io/carlpett/sops] ~> 0.7.1 ├── provider[registry.terraform.io/hashicorp/time] ├── module.iam_policies │   └── provider[registry.terraform.io/hashicorp/aws] >= 4.0.0 ├── module.certcheck_lambda │   ├── provider[registry.terraform.io/hashicorp/aws] >= 5.32.0 │   ├── provider[registry.terraform.io/hashicorp/external] >= 1.0.0 │   ├── provider[registry.terraform.io/hashicorp/local] >= 1.0.0 │   └── provider[registry.terraform.io/hashicorp/null] >= 2.0.0 ├── module.certdeploy_lambda │   ├── provider[registry.terraform.io/hashicorp/null] >= 2.0.0 │   ├── provider[registry.terraform.io/hashicorp/aws] >= 5.32.0 │   ├── provider[registry.terraform.io/hashicorp/external] >= 1.0.0 │   └── provider[registry.terraform.io/hashicorp/local] >= 1.0.0 ├── module.certissue_lambda │   ├── provider[registry.terraform.io/hashicorp/null] >= 2.0.0 │   ├── provider[registry.terraform.io/hashicorp/aws] >= 5.32.0 │   ├── provider[registry.terraform.io/hashicorp/external] >= 1.0.0 │   └── provider[registry.terraform.io/hashicorp/local] >= 1.0.0 ├── module.crl_processor_lambda │   ├── provider[registry.terraform.io/hashicorp/local] >= 1.0.0 │   ├── provider[registry.terraform.io/hashicorp/null] >= 2.0.0 │   ├── provider[registry.terraform.io/hashicorp/aws] >= 5.32.0 │   └── provider[registry.terraform.io/hashicorp/external] >= 1.0.0 └── module.iam_assumable_roles └── provider[registry.terraform.io/hashicorp/aws] >= 4.0.0

Providers required by state:

provider[registry.terraform.io/hashicorp/null]

provider[registry.terraform.io/hashicorp/time]

provider[registry.terraform.io/hashicorp/aws]

provider[registry.terraform.io/hashicorp/external]

provider[registry.terraform.io/hashicorp/local]

## Reproduction Code [Required]

data "aws_lambda_function" "cert_issue" { function_name = "acm_pca-certIssue"

depends_on = [module.certissue_lambda]

}

module "certissue_lambda" { source = "terraform-aws-modules/lambda/aws" version = "7.0.0" function_name = "acmpca-certIssue" description = "Acmpca certIssue Transform" handler = "certIssue.lambda_handler" runtime = "python3.9" timeout = "60" create_role = false

lambda_role = aws_iam_role.cert_issue_lambda_role.arn

lambda_role = module.iam_assumable_roles["cert_issue_lambda_role_test"].iam_role_arn

source_path = "${path.module}/python/certIssue.py" attach_cloudwatch_logs_policy = true

layers = [] environment_variables = { SigningAlgorithm = local.signing_algorithm PCAarn = aws_acmpca_certificate_authority.private_ca_authority.arn CSR_PATH = local.csr_path SNS_TOPIC_ARN = aws_sns_topic.failed_cert_rotation_sns_topic.id }

tags = local.tags }

module "certcheck_lambda" { source = "terraform-aws-modules/lambda/aws" version = "7.0.0" function_name = "acm_pca-certCheck" description = "acm_pca cert check Transform" handler = "certCheck.lambda_handler" runtime = "python3.9" timeout = "60" create_role = false

lambda_role = aws_iam_role.cert_check_lambda_role.arn

lambda_role = module.iam_assumable_roles["cert_check_lambda_role_test"].iam_role_arn source_path = "${path.module}/python/certCheck.py"

attach_cloudwatch_logs_policy = true

environment_variables = { AWSREGION = data.aws_region.current.name LAMBDA_CERT_ISSUE_ARN = data.aws_lambda_function.cert_issue.arn DEFAULT_CERT_PATH = local.cert_path DEFAULT_KEY_PATH = local.key_path SNS_TOPIC_ARN = aws_sns_topic.failed_cert_rotation_sns_topic.id }

tags = local.tags

depends_on = [module.certissue_lambda]

}

module "iam_assumable_roles" { for_each = local.iam_roles source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role" version = "v5.41.0"

create_role = true role_requires_mfa = false

role_name = each.key custom_role_policy_arns = each.value.custom_role_policy_arns create_custom_role_trust_policy = each.value.create_custom_trust_policy custom_role_trust_policy = each.value.custom_role_trust_policy trusted_role_arns = each.value.trusted_role_arns trusted_role_services = each.value.trusted_role_services tags = local.tags }

module "iam_policies" { for_each = local.iam_policies source = "terraform-aws-modules/iam/aws//modules/iam-policy" version = "v5.41.0" name = each.key path = "/" policy = each.value.policy

tags = local.tags }

locals { iam_roles = { "cert_issue_lambda_role_test" = { create_custom_trust_policy = "false" custom_role_trust_policy = "" custom_role_policy_arns = [ "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", ] trusted_role_services = [ "lambda.amazonaws.com" ] trusted_role_arns = [] }, "cert_check_lambda_role_test" = { create_custom_trust_policy = "false" custom_role_trust_policy = "" custom_role_policy_arns = [ "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", module.iam_policies["cert_check_lambda_policy_test"].arn ] trusted_role_services = [ "lambda.amazonaws.com" ] trusted_role_arns = [] }, } iam_policies = { "cert_issue_lambda_policy_test" = { description = "Cert Issue lambda policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "ssm:SendCommand", "ssm:GetCommandInvocation", "dynamodb:PutItem", "acm-pca:IssueCertificate", "sns:Publish" ] Resource = ["*"] } ] }) }, "cert_check_lambda_policy_test" = { description = "CertCheck lambda policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "lambda:InvokeFunction", "dynamodb:PartiQLSelect", "sns:Publish" ] Resource = [ data.aws_lambda_function.cert_issue.arn, aws_dynamodb_table.dynamo_db_certificate_table.arn, aws_sns_topic.failed_cert_rotation_sns_topic.id ] } ] }) } }


Steps to reproduce the behavior:

<!-- Are you using workspaces? -->

`terraform init ` and `terraform apply`

## Expected behavior

* IAM policy created
* IAM role created
* IAM role associated with Lambda, Lambda function has been created.

## Actual behavior

t validate ╷ │ ╷ │ Error: Cycle: module.certcheck_lambda.var.lambda_role (expand), module.certissue_lambda.output.lambda_function_source_code_hash (expand), module.certissue_lambda.aws_lambda_permission.unqualified_alias_triggers, module.certissue_lambda.output.lambda_function_url (expand), module.certissue_lambda.output.lambda_function_kms_key_arn (expand), module.certissue_lambda.output.lambda_function_signing_profile_version_arn (expand), module.certissue_lambda.output.lambda_event_source_mapping_function_arn (expand), module.certissue_lambda.aws_lambda_provisioned_concurrency_config.current_version, module.certissue_lambda.aws_lambda_permission.current_version_triggers, module.certissue_lambda.aws_lambda_function_event_invoke_config.this, module.certissue_lambda.output.lambda_event_source_mapping_state (expand), module.certissue_lambda.aws_lambda_function_url.this, module.certissue_lambda.output.lambda_function_url_id (expand), module.certissue_lambda.output.lambda_function_name (expand), module.certissue_lambda.output.lambda_function_source_code_size (expand), module.certissue_lambda (close), module.certissue_lambda.output.lambda_function_invoke_arn (expand), module.certissue_lambda.output.lambda_function_arn (expand), module.certissue_lambda.output.lambda_function_qualified_invoke_arn (expand), module.certissue_lambda.output.lambda_function_last_modified (expand), module.certissue_lambda.output.lambda_function_version (expand), module.certissue_lambda.output.lambda_function_qualified_arn (expand), module.certissue_lambda.output.lambda_event_source_mapping_uuid (expand), module.certissue_lambda.aws_lambda_event_source_mapping.this, module.certissue_lambda.output.lambda_event_source_mapping_state_transition_reason (expand), module.iam_assumable_roles.var.role_description (expand), module.iam_assumable_roles.var.custom_role_trust_policy (expand), module.iam_assumable_roles.local.custom_role_trust_policy_condition (expand), module.iam_assumable_roles.var.force_detach_policies (expand), module.iam_assumable_roles.var.mfa_age (expand), module.iam_assumable_roles.data.aws_iam_policy_document.assume_role_with_mfa, module.iam_assumable_roles.var.tags (expand), module.iam_assumable_roles.var.role_permissions_boundary_arn (expand), module.iam_assumable_roles.var.max_session_duration (expand), module.iam_assumable_roles.var.role_sts_externalid (expand), module.iam_assumable_roles.local.role_sts_externalid (expand), module.iam_assumable_roles.data.aws_partition.current, module.iam_assumable_roles.local.partition (expand), module.iam_assumable_roles.var.allow_self_assume_role (expand), module.iam_assumable_roles.var.role_requires_mfa (expand), module.iam_assumable_roles.var.role_name_prefix (expand), module.iam_assumable_roles.var.role_name (expand), module.iam_assumable_roles.local.role_name_condition (expand), module.iam_assumable_roles.var.role_path (expand), module.iam_assumable_roles.var.create_custom_role_trust_policy (expand), module.iam_assumable_roles.var.role_session_name (expand), module.iam_assumable_roles.var.trusted_role_actions (expand), module.iam_assumable_roles.var.trusted_role_services (expand), module.iam_assumable_roles.var.trusted_role_arns (expand), module.iam_assumable_roles.data.aws_caller_identity.current, module.iam_assumable_roles.local.account_id (expand), module.iam_assumable_roles.var.role_requires_session_name (expand), module.iam_assumable_roles.data.aws_iam_policy_document.assume_role, module.iam_assumable_roles.var.create_role (expand), module.iam_assumable_roles.aws_iam_role.this, module.iam_policies.output.path (expand), module.iam_policies.output.description (expand), module.iam_policies.output.arn (expand), module.iam_policies.output.name (expand), module.iam_policies.output.id (expand), module.iam_policies.var.name_prefix (expand), module.iam_policies.var.tags (expand), module.iam_policies.var.create_policy (expand), module.iam_policies.var.name (expand), module.iam_policies.var.description (expand), module.iam_policies.var.path (expand), module.iam_policies.aws_iam_policy.policy, module.iam_policies.output.policy (expand), local.iam_roles (expand), module.iam_assumable_roles (expand), module.iam_assumable_roles.output.iam_role_arn (expand), module.certissue_lambda.var.lambda_role (expand), module.certissue_lambda.aws_lambda_function.this, module.certissue_lambda.output.lambda_function_signing_job_arn (expand), data.aws_lambda_function.cert_issue, module.certcheck_lambda.var.environment_variables (expand), module.certcheck_lambda.aws_lambda_function.this, module.certcheck_lambda.output.lambda_function_arn (expand), local.iam_policies (expand), module.iam_policies (expand), module.iam_policies.var.policy (expand), module.iam_policies (close) │ │ │


## Additional context

Have tried adding the following `depends_on`/ `data_resources` for the lambda modules. but no luck.
gowgopal83 commented 1 month ago

Note: I've the same setup to create another function where the codes work just fine.

module "crl_processor_lambda" {
  source        = "terraform-aws-modules/lambda/aws"
  version       = "7.0.0"
  function_name = "acmpca-crlprocessor"
  description   = "Acmpca crl processor"
  handler       = "crlProcess.lambda_handler"
  runtime       = "python3.9"
  timeout       = "60"
  create_role   = false
  lambda_role   = module.iam_assumable_roles["crl_processor_lambda_role"].iam_role_arn
  source_path   = "${path.module}/python/crlProcess.py"

  layers = [
    aws_lambda_layer_version.lambda_cryptography_layer.arn
  ]
  environment_variables = {
    IAMRATrustAnchorARN = aws_rolesanywhere_trust_anchor.dmz_trust_anchor.arn
    SNS_TOPIC_ARN       = aws_sns_topic.failed_cert_rotation_sns_topic.id
  }

  tags = local.tags
}

module "iam_policies" {
  for_each = local.iam_policies
  source   = "terraform-aws-modules/iam/aws//modules/iam-policy"
  version  = "v5.41.0"
  name     = each.key
  path     = "/"
  policy   = each.value.policy

  tags = local.tags
}

module "iam_assumable_roles" {
  for_each = local.iam_roles
  source   = "terraform-aws-modules/iam/aws//modules/iam-assumable-role"
  version  = "v5.41.0"

  create_role       = true
  role_requires_mfa = false

  role_name                       = each.key
  custom_role_policy_arns         = each.value.custom_role_policy_arns
  create_custom_role_trust_policy = each.value.create_custom_trust_policy
  custom_role_trust_policy        = each.value.custom_role_trust_policy
  trusted_role_arns               = each.value.trusted_role_arns
  trusted_role_services           = each.value.trusted_role_services
  tags                            = local.tags

}

locals {
  iam_roles = {
    "crl_processor_lambda_role" = {
      create_custom_trust_policy = "false"
      custom_role_trust_policy   = ""
      custom_role_policy_arns = [
        "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
        module.iam_policies["crl_processor_policy"].id
      ]
      trusted_role_services = [
        "lambda.amazonaws.com"
      ]
      trusted_role_arns = []
    }
  }
  iam_policies = {
    "crl_processor_policy" = {
      description = "CRL processor policy"
      policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Effect = "Allow"
            Action = [
              "s3:GetObject",
              "sns:Publish"
            ]
            Resource = [
              "arn:aws:s3:::${aws_s3_bucket.crl_config_revocation.id}/*",
              aws_sns_topic.failed_cert_rotation_sns_topic.id
            ]
          },
          {
            Effect = "Allow"
            Action = [
              "rolesanywhere:ImportCrl"
            ]
            Resource = [            
           "arn:aws:rolesanywhere:${var.region}:${local.aws_account_id}:crl/*"
            ]
          }
        ]
      })
    },
}

Output:

terraform state show 'module.crl_processor_lambda.aws_lambda_function.this[0]'
# module.crl_processor_lambda.aws_lambda_function.this[0]:
resource "aws_lambda_function" "this" {
    architectures                  = [
        "x86_64",
    ]
    arn                            = "arn:aws:lambda:<>:function:acmpca-crlprocessor"
    description                    = "Acmpca crl processor"
    filename                       = "builds/9a9638bec4b4606363504592a37a2aae791b4959a6860d9c01ddff5fad09a61b.zip"
    function_name                  = "acmpca-crlprocessor"
    handler                        = "crlProcess.lambda_handler"
    id                             = "acmpca-crlprocessor"
    invoke_arn                     = "arn:aws:apigateway:<>:path/2015-03-31/functions/arn:aws:lambda:<>:function:acmpca-crlprocessor/invocations"
    last_modified                  = "2024-07-25T13:58:21.000+0000"
    layers                         = [
        "arn:aws:lambda:<>:layer:cryptography:7",
    ]
    memory_size                    = 128
    package_type                   = "Zip"
    publish                        = false
    qualified_arn                  = "arn:aws:lambda:<>:function:acmpca-crlprocessor:$LATEST"
    qualified_invoke_arn           = "arn:aws:apigateway:<>:lambda:path/2015-03-31/functions/arn:aws:lambda:<>:function:acmpca-crlprocessor:$LATEST/invocations"
    reserved_concurrent_executions = -1
    role                           = "arn:aws:iam::<>:role/crl_processor_lambda_role"
    runtime                        = "python3.9"
    skip_destroy                   = false
    source_code_hash               = "<>"
    source_code_size               = 962
    tags                           = {
        "Account"            = "manage"
        "CiscoMailAlias"     = "dcloud-devops@cisco.com"
        "DataClassification" = "Cisco Confidential"
        "DataTaxonomy"       = "Cisco Operations Data"
        "Environment"        = "tf_onprem"
        "ResourceOwner"      = "dCloud"
        "Terraform"          = "true"
    }
    tags_all                       = {
        "Account"            = "manage"
        "CiscoMailAlias"     = "dcloud-devops@cisco.com"
        "DataClassification" = "Cisco Confidential"
        "DataTaxonomy"       = "Cisco Operations Data"
        "Environment"        = "tf_onprem"
        "ResourceOwner"      = "dCloud"
        "Terraform"          = "true"
    }
    timeout                        = 60
    version                        = "$LATEST"

    environment {
        variables = {
            "IAMRATrustAnchorARN" = "arn:aws:rolesanywhere:<>:trust-anchor/d5b48e1d-ec20-4eb4-9d17-acf8a6fdadae"
            "SNS_TOPIC_ARN"       = "arn:aws:sns:<>:failed_cert_rotation_sns_topic"
        }
    }

    ephemeral_storage {
        size = 512
    }

    logging_config {
        log_format = "Text"
        log_group  = "/aws/lambda/acmpca-crlprocessor"
    }

    tracing_config {
        mode = "PassThrough"
    }
}
github-actions[bot] commented 1 week ago

This issue has been automatically marked as stale because it has been open 30 days with no activity. Remove stale label or comment or this issue will be closed in 10 days

github-actions[bot] commented 12 hours ago

This issue was automatically closed because of stale in 10 days