简体   繁体   中英

Terraform variable inteporlation and evaluation

I'm working with modules in Terraform using Yaml approach to manage variables. I have a very simple module that should create parameter in AWS Parameter Store based on my RDS and IAM User modules output.So, I wrote this module:

resource "aws_ssm_parameter" "ssm_parameter" {
  name      = var.parameter_name
  type      = var.parameter_type
  value     = var.parameter_value
  overwrite = var.overwrite
  tags      = var.tags
}

The variables I'm using are stored into a Yaml file like this:

ssms:
  /arquitetura/catalogo/gitlab/token:
    type: SecureString
    value: ManualInclude
  /arquitetura/catalogo/s3/access/key:
    type: String
    value: module.iam_user.access_key
  /arquitetura/catalogo/s3/secret/access/key:
    type: SecureString
    value: module.iam_user.secret_access_key
  /arquitetura/catalogo/rds/user:
    type: String
    value: module.rds_instance.database_username
  /arquitetura/catalogo/rds/password:
    type: SecureString
    value: module.rds_instance.database_password

As we can see, I have in "value" the module output I would like to send to Parameter Store. I'm loading this variable file using file and yamldecode functions:

  ssmfile                  = "./env/${terraform.workspace}/ssm.yaml"
  ssmfilecontent           = fileexists(local.ssmfile) ? file(local.ssmfile) : "ssmFileNotFound: true"
  ssmsettings              = yamldecode(local.ssmfilecontent)

So, I have a local.ssmsettings and I can write a module call like this:

module "ssm_parameter" {
  source = "../aws-ssm-parameter-tf"

  for_each = local.ssmsettings.ssms

  parameter_name  = each.key
  parameter_type  = each.value.type
  parameter_value = each.value.value
  tags            = local.tags
}

Doing this, my parameter is stored as:

{
    "Parameter": {
        "Name": "/arquitetura/catalogo/rds/user",
        "Type": "String",
        "Value": "module.rds_instance.database_username",
        "Version": 1,
        "LastModifiedDate": "2022-12-15T19:02:01.825000-03:00",
        "ARN": "arn:aws:ssm:sa-east-1:111111111111:parameter/arquitetura/catalogo/rds/user",
        "DataType": "text"
    }
}

Value is receiving the string module.rds_instance.database_username instead of the module output.

I know that file function doesn't interpolate variables and I know Terraform doesn't have an eval function.

Does anybody had the same situation that can tell me how you solved the problem or have any clue that I can follow?

I already tried to work with Terraform templates, without success.

Thanks in advance.

Terraform has no way to understand that the value strings in your YAML files are intended to be references to values elsewhere in your module, and even if it did it wouldn't be possible to resolve them from there because this YAML file is not actually a part of the Terraform module, and is instead just a data file that Terraform has loaded into memory.

However, you can get a similar effect by placing all of the values your YAML file might refer to into a map of strings inside your module:

locals {
  ssm_indirect_values = tomap({
    manual_include        = "ManualInclude"
    aws_access_key_id     = module.iam_user.access_key
    aws_secret_access_key = module.iam_user.secret_access_key
    database_username     = module.rds_instance.database_username
    database_password     = module.rds_instance.database_password
  })
}

Then change your YAML data so that the value strings match with the keys in this map:

ssms:
  /arquitetura/catalogo/gitlab/token:
    type: SecureString
    value: manual_include
  /arquitetura/catalogo/s3/access/key:
    type: String
    value: aws_access_key_id
  /arquitetura/catalogo/s3/secret/access/key:
    type: SecureString
    value: aws_secret_access_key
  /arquitetura/catalogo/rds/user:
    type: String
    value: database_username
  /arquitetura/catalogo/rds/password:
    type: SecureString
    value: database_password

You can then substitute the real values instead of the placeholders before you use the data structure in for_each :

locals {
  ssm_file                  = "${path.module}/env/${terraform.workspace}/ssm.yaml"
  ssm_file_content          = file(local.ssm_file)
  ssm_settings              = yamldecode(local.ssm_file_content)

  ssms = tomap({
    for k, obj in local.ssm_settings.ssms :
    k => {
      type  = obj.type
      value = local.ssm_indirect_values[obj.value]
    }
  })
}

module "ssm_parameter" {
  source   = "../aws-ssm-parameter-tf"
  for_each = local.ssms

  parameter_name  = each.key
  parameter_type  = each.value.type
  parameter_value = each.value.value
  tags            = local.tags
}

The for expression in the definition of local.ssms uses the source value string as a lookup key into local.ssm_indirect_values , thereby inserting the real value.

The module "ssm_parameter" block now refers to the derived local.ssms instead of the original local.ssm_settings.ssms , so each.value.value will be the final resolved value rather than the lookup key, and so your parameter should be stored as you intended.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM