Terraform-provider-aws: Feature: Support passing CodeBuild `environment_variable`s as a list

Created on 15 Aug 2018  ยท  7Comments  ยท  Source: hashicorp/terraform-provider-aws

Description

Currently to add environment variables to a CodeBuild project, they need to be defined like so:

  environment {
    [...]

    environment_variable {
      "name"  = "SOME_KEY1"
      "value" = "SOME_VALUE1"
    }

    environment_variable {
      "name"  = "SOME_KEY2"
      "value" = "SOME_VALUE2"
    }
  }

This means that multiple environment variables can't be passed in from tfvars.

Preferably, environment variables could be passed in like:

  environment {
    [...]

    environment_variables = [
      {
        "name"  = "SOME_KEY1"
        "value" = "SOME_VALUE1"
      },
      {
        "name"  = "SOME_KEY2"
        "value" = "SOME_VALUE2"
      }
    ]
  }

This would then allow the variables to be passed in as a list:

  environment {
    [...]

    environment_variables = "${var.envars}"
  }

A specific use case for this is where 3rd party modules are being used, such as https://github.com/squidfunk/terraform-aws-github-ci

Currently environment variables can't be passed through, as each environment variable needs to be defined within the resource

New or Affected Resource(s)

  • aws_codebuild_project

References

https://www.terraform.io/docs/providers/aws/r/codebuild_project.html
https://github.com/squidfunk/terraform-aws-github-ci

serviccodebuild

Most helpful comment

Hi @Stretch96 ๐Ÿ‘‹ It might not be explicitly documented throughout the Terraform documentation, but the syntax you are trying to use does actually work, at least in Terraform 0.11.8.

For example, this is a working configuration:

terraform {
  required_version = "0.11.8"
}

provider "aws" {
  region  = "us-east-1"
  version = "1.32.0"
}

variable "environment_variables" {
  type = "list"

  default = [
    {
      name  = "DEFAULT_KEY1"
      value = "DEFAULT_VALUE1"
    },
    {
      name  = "DEFAULT_KEY2"
      value = "DEFAULT_VALUE2"
    },
  ]
}

resource "aws_iam_role" "test" {
  name = "issue-5563-testing"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "codebuild.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "test" {
  role = "${aws_iam_role.test.name}"

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Resource": [
        "*"
      ],
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DescribeDhcpOptions",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface",
        "ec2:DescribeSubnets",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeVpcs"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": "*"
    }
  ]
}
POLICY
}

resource "aws_codebuild_project" "test" {
  name         = "issue-5563-testing"
  service_role = "${aws_iam_role.test.arn}"

  artifacts {
    type = "NO_ARTIFACTS"
  }

  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    image        = "2"
    type         = "LINUX_CONTAINER"

    environment_variable = "${var.environment_variables}"
  }

  source {
    location = "https://github.com/hashibot-test/aws-test.git"
    type     = "GITHUB"
  }
}

If I also define a terraform.tfvars file like the below and run terraform apply:

environment_variables = [
  {
    name  = "OVERRIDE_KEY1"
    value = "OVERRIDE_VALUE1"
  },
  {
    name  = "OVERRIDE_KEY2"
    value = "OVERRIDE_VALUE2"
  },
]

We get the expected values saved in the resource and into the Terraform state:

$ terraform state show aws_codebuild_project.test
...
environment.1401055302.environment_variable.#       = 2
environment.1401055302.environment_variable.0.name  = OVERRIDE_KEY1
environment.1401055302.environment_variable.0.type  = PLAINTEXT
environment.1401055302.environment_variable.0.value = OVERRIDE_VALUE1
environment.1401055302.environment_variable.1.name  = OVERRIDE_KEY2
environment.1401055302.environment_variable.1.type  = PLAINTEXT
environment.1401055302.environment_variable.1.value = OVERRIDE_VALUE2

There are some important notes about this functionality though:

  • Due to some typing changes occurring in the next major version of Terraform, this approach may not work after that upgrade, but I have not personally verified this either which way as that development is not complete. The recommended approach there may wind up being to use the new for expressions.
  • Passing complex types like these into Terraform modules may experience weirdness or not work as expected until the changes mentioned above are implemented.

Does this help answer what you are looking for?

All 7 comments

Hi @Stretch96 ๐Ÿ‘‹ It might not be explicitly documented throughout the Terraform documentation, but the syntax you are trying to use does actually work, at least in Terraform 0.11.8.

For example, this is a working configuration:

terraform {
  required_version = "0.11.8"
}

provider "aws" {
  region  = "us-east-1"
  version = "1.32.0"
}

variable "environment_variables" {
  type = "list"

  default = [
    {
      name  = "DEFAULT_KEY1"
      value = "DEFAULT_VALUE1"
    },
    {
      name  = "DEFAULT_KEY2"
      value = "DEFAULT_VALUE2"
    },
  ]
}

resource "aws_iam_role" "test" {
  name = "issue-5563-testing"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "codebuild.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "test" {
  role = "${aws_iam_role.test.name}"

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Resource": [
        "*"
      ],
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DescribeDhcpOptions",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface",
        "ec2:DescribeSubnets",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeVpcs"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": "*"
    }
  ]
}
POLICY
}

resource "aws_codebuild_project" "test" {
  name         = "issue-5563-testing"
  service_role = "${aws_iam_role.test.arn}"

  artifacts {
    type = "NO_ARTIFACTS"
  }

  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    image        = "2"
    type         = "LINUX_CONTAINER"

    environment_variable = "${var.environment_variables}"
  }

  source {
    location = "https://github.com/hashibot-test/aws-test.git"
    type     = "GITHUB"
  }
}

If I also define a terraform.tfvars file like the below and run terraform apply:

environment_variables = [
  {
    name  = "OVERRIDE_KEY1"
    value = "OVERRIDE_VALUE1"
  },
  {
    name  = "OVERRIDE_KEY2"
    value = "OVERRIDE_VALUE2"
  },
]

We get the expected values saved in the resource and into the Terraform state:

$ terraform state show aws_codebuild_project.test
...
environment.1401055302.environment_variable.#       = 2
environment.1401055302.environment_variable.0.name  = OVERRIDE_KEY1
environment.1401055302.environment_variable.0.type  = PLAINTEXT
environment.1401055302.environment_variable.0.value = OVERRIDE_VALUE1
environment.1401055302.environment_variable.1.name  = OVERRIDE_KEY2
environment.1401055302.environment_variable.1.type  = PLAINTEXT
environment.1401055302.environment_variable.1.value = OVERRIDE_VALUE2

There are some important notes about this functionality though:

  • Due to some typing changes occurring in the next major version of Terraform, this approach may not work after that upgrade, but I have not personally verified this either which way as that development is not complete. The recommended approach there may wind up being to use the new for expressions.
  • Passing complex types like these into Terraform modules may experience weirdness or not work as expected until the changes mentioned above are implemented.

Does this help answer what you are looking for?

Ah yep, that does help, I'll give it a go ๐Ÿ‘

Does that apply to all resource properties that allow multiples? I don't think that is documented (It's not documented on the codebuild_project page).

@Stretch96 that's correct, it should apply to any schema.TypeList and schema.TypeSet attributes in any Terraform resource. We would prefer to _not_ document this on individual resources since its generic, but if you feel some of the https://www.terraform.io documentation could be improved, I think its worth submitting something upstream in Terraform core: https://github.com/hashicorp/terraform

I'm going to close this out since the specific use case is supported, but feel free to reach out if I'm missing something. ๐Ÿ‘

This may still be an issue. I have a couple of vars that I'd like to put in as data. For example:

variable environment_variables {
  type = "list",
  default= [
    {
      "name"  = "BUCKET_NAME"
      "value" = "${var.bucket_name}"
    }
  ]
}

But it won't work. Clearly I cant use default. But what do I use?

@automaticgiant If you want to be able to use a set of defaults, and also allow adding in custom environment variables, you can do something like this:

variables.tf:

variable "bucket_name" {
  type .  = "string"
  default = ""
}

variable "environment_variables" {
  type    = "list"
  default = []
}

aws_codebuild_project resource:

environment {
  [...]
  environment_variable = [
    {
      "name"  = "BUCKET_NAME",
      "value" = "${var.bucket_name}"
    },
    "${var.environment_variables}"
  ]
  [...]

For terraform 0.12.x I had to change to something like the following:

variable "environment_variables" {
  type = list(string)

  default = [
    {
      name  = "DEFAULT_KEY1"
      value = "DEFAULT_VALUE1"
    },
    {
      name  = "DEFAULT_KEY2"
      value = "DEFAULT_VALUE2"
    },
  ]
}

resource "aws_codebuild_project" "codebuild" {
  name         = var.name
  description  = var.codebuild_project_description
  service_role = aws_iam_role.codebuild.arn

  artifacts {
    type = "CODEPIPELINE"
  }

  environment {
    compute_type = var.codebuild_compute_type
    image        = var.codebuild_image
    type         = var.codebuild_type

    dynamic "environment_variable" {
      for_each = [for v in var.environment_variables: {
        name  = v.name
        value = v.value
      }]

      content {
        name   = environment_variable.value.name
        value  = environment_variable.value.value
      }
    }
  }

  source {
    type = "CODEPIPELINE"
  }
}

I'm going to lock this issue because it has been closed for _30 days_ โณ. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

Was this page helpful?
0 / 5 - 0 ratings