Passing Variables into a customer terraform module - amazon-web-services

I am trying to build a module in terraform that can be passed a variable map from a workspace file and lookup the variables to build an AWS CodePipeline. For those not aware, CodePipeline has a series of stages, and inside those stages are actions. My module needs to handle building a pipeline wiht any supported number of stages and actions within those stages.
Below is a part of the module I've wirtten concerned with the creation of the pipeline itself:
dynamic "stage" {
for_each = [for s in var.stages : {
name = s.name
action = s.action
} if(lookup(s, "enabled", true))]
content {
name = stage.value.name
dynamic "action" {
for_each = stage.value.action
content {
name = lookup(action.value, "name", null)
owner = lookup(action.value, "owner", null)
version = lookup(action.value, "version", null)
category = lookup(action.value, "category", null)
provider = lookup(action.value, "provider", null)
input_artifacts = lookup(action.value, "input_artifacts", null)
output_artifacts = lookup(action.value, "output_artifacts", null)
configuration = {
BranchName = lookup(action.value, "BranchName", null)
PollForSourceChanges = lookup(action.value, "PollForSourceChanges", null)
RepositoryName = lookup(action.value, "RepositoryName", null)
ProjectName = lookup(action.value, "ProjectName", null)
}
role_arn = lookup(action.value, "role_arn", null)
run_order = lookup(action.value, "run_order", null)
region = lookup(action.value, "region", null)
}
}
}
}
tags = merge(
{ "Name" = "${var.prefix}-${var.name}" },
var.tags,
)
}
And here is an excerpt from the workspace yaml, where I pass the variables for two stages, each with an associated action:
test_pipeline_prefix: "test"
test_pipeline_name: "test-pipeline"
test_pipeline_description: "this is a POC pipeline"
test_pipeline_s3: "<<REDACTED ARN>>"
test_pipeline_codestar_arn: "<<REDACTED ARN>>"
test_pipeline_tags: [""]
test_pipeline_stages: [{
name: "Source",
action: [{
name = "Source",
category = "Source",
owner = "AWS"
version = "1",
provider = "CodeStarSourceConnection",
output_artifacts = "source_output",
BranchName = "main",
PollForSourceChanges = "false",
RepositoryName = "<<REDACTED REPO>>",
region = "eu-west-2",
run_order = 1
}]
},
{
name: "Plan",
action: [{
name = "Plan",
category = "Build",
owner = "AWS"
version = "1",
provider = "CodeBuild",
input_artifacts = "source_output",
output_artifacts = "build_output",
ProjectName = "pipeline-plan",
run_order = 2
}]
}]
Finally I call it with:
module "codepipeline" {
source = "./modules/codepipeline"
s3 = local.vars.test_pipeline_s3
description = local.vars.test_pipeline_description
prefix = local.vars.test_pipeline_prefix
name = local.vars.test_pipeline_name
stages = local.vars.test_pipeline_stages
codestar_arn = local.vars.test_pipeline_codestar_arn
tags = {
Environment = local.vars.env
Terraform = "true"
}
}
What I am hoping for it to do, is loop through the stages and actions in the "test_pipeline_stages" variable supplied, and loop through to create a stage for the Source, with an action configured to connect to the preexsiting CodeStar connection, and another stage called "Plan" that runs the CodeBuild job as it's action.
The result I'm actually getting is:
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.0.action.0.owner" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.0.action.0.provider" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.0.action.0.category" is required, but no definition was
│ found.
│ Error: Missing required argument
│
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.0.action.0.version" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.1.action.0.version" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.1.action.0.category" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.1.action.0.owner" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 1, in resource "aws_codepipeline" "this":
│ 1: resource "aws_codepipeline" "this" {
│
│ The argument "stage.1.action.0.provider" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 2, in resource "aws_codepipeline" "this":
│ 2: name = "${var.prefix}-${var.name}"
│
│ The argument "stage.0.action.0.name" is required, but no definition was
│ found.
│ Error: Missing required argument
│ with module.codepipeline.aws_codepipeline.this,
│ on modules/codepipeline/main.tf line 2, in resource "aws_codepipeline" "this":
│ 2: name = "${var.prefix}-${var.name}"
│
│ The argument "stage.1.action.0.name" is required, but no definition was
│ found.
This suggest to me that its not indexing the variables prporly but I can't really figure the best way to proceed. Anyone got any ideas?

Thanks to responders so far - the actual fix I'll detail below, but all responses up to this point have helped me get there. I also agree that supplying null values as a fallback is not sensible; I'll look to review that.
The actual issue was simply that my workspace yaml as posted above is... not valid yaml. Once I replaced it with the below, the module began to read out the values correctly.
test_pipeline_stages: [{
name: "Source",
action: [{
ActionName: "Source",
ActionCategory: "Source",
ActionOwner: "AWS",
ActionVersion: "1",
ActionProvider: "CodeStarSourceConnection",
output_artifacts: ["source_output"],
BranchName: "main",
PollForSourceChanges: "false",
RepositoryName: "REDACTED",
ConnectionArn: "REDACTED",
region: "eu-west-2",
run_order: 1
}]
},
{
name: "Plan",
action: [{
ActionName: "Plan",
ActionCategory: "Build",
ActionOwner: "AWS",
ActionVersion: "1",
ActionProvider: "CodeBuild",
input_artifacts: ["source_output"],
output_artifacts: ["build_output"],
ProjectName: "pipeline-plan",
test_pipeline_ans_tags: "",
run_order: 2
}]
}]

comma is missing after owner= "AWS", put comma in workspace file.

Related

Error in assigning gcs IAM permissions using nested map in terraform

Im trying to assign multiple roles to different members using terraform but im running into an error.This is for assigning iam permission in GCP.
Use a combination of nested map. But the nested map became complex since Im using two different variables and use them in creating resources.
main.tf looks like this
locals {
data_access = flatten([
for bkt_key, bkt_value in var.buckets_data : [
for user,roles in var.data_access : [
for role in roles:{
member = user
bkt = bkt_key
role = roles
}]
]
])
}
resource "google_storage_bucket_iam_member" "buckets_data_access" {
for_each = { for access in local.data_access : "${access.bkt}_${access.member}" => access... }
bucket = google_storage_bucket.tf_buckets_data[each.value.bkt].name
role = each.value.role
member = each.value.member
}
terraform.tfvars looks like this, Please note I'm using two different variables in the nested map of main.tf.
buckets_data = {
"landing" = {
region = "nane1",
storage_class = "COLDLINE",
versioning = "false",
data_tier = "raw",
lifecycle_rules = ["retention-2years"],
external_access = []
},
"dftemp" = {
region = "nane1",
storage_class = "STANDARD"
},
"curated" = {
region = "nane1",
storage_class = "STANDARD"
}
}
data_access = {
"group:GCP-npe#bell.ca"= ["roles/storage.objectViewer","roles/Browser"]
}
error I received in my terminal
$ terraform plan
╷
│ Error: Unsupported attribute
│
│ on main.tf line 29, in resource "google_storage_bucket_iam_member" "buckets_data_access":
│ 29: bucket = google_storage_bucket.tf_buckets_data[each.value.bkt].name
│ ├────────────────
│ │ each.value is tuple with 2 elements
│
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 29, in resource "google_storage_bucket_iam_member" "buckets_data_access":
│ 29: bucket = google_storage_bucket.tf_buckets_data[each.value.bkt].name
│ ├────────────────
│ │ each.value is tuple with 2 elements
│
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 30, in resource "google_storage_bucket_iam_member" "buckets_data_access":
│ 30: role = each.value.role
│ ├────────────────
│ │ each.value is tuple with 2 elements
│
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 30, in resource "google_storage_bucket_iam_member" "buckets_data_access":
│ 30: role = each.value.role
│ ├────────────────
│ │ each.value is tuple with 2 elements
│
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 31, in resource "google_storage_bucket_iam_member" "buckets_data_access":
│ 31: member = each.value.member
│ ├────────────────
│ │ each.value is tuple with 2 elements
│
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 31, in resource "google_storage_bucket_iam_member" "buckets_data_access":
│ 31: member = each.value.member
│ ├────────────────
│ │ each.value is tuple with 2 elements
│
│ This value does not have any attributes.
If my understanding is correct of what you are trying to do, the following flattening is better:
locals {
data_access = merge(flatten([
for bkt_key, bkt_value in var.buckets_data : [
for user,roles in var.data_access : {
for role in roles:
"${bkt_key}-${user}-${role}" => {
member = user
bkt = bkt_key
role = role
}}
]
])...) # please do NOT remove the dots
}
then
resource "google_storage_bucket_iam_member" "buckets_data_access" {
for_each = local.data_access
bucket = google_storage_bucket.tf_buckets_data[each.value.bkt].name
role = each.value.role
member = each.value.member
}

Error when using Terraform Plan and Google Provider "Cannot determine region"

When I do a 'terraform plan', I get the following error:
│ Error: Cannot determine region: set in this resource, or set provider-level 'region' or 'zone'.
│
│
│ Error: Cannot determine region: set in this resource, or set provider-level 'region' or 'zone'.
│
│
Operation failed: failed running terraform plan (exit 1)
My provider looks like this
provider "google" {
project = "test"
region = "europe-west2"
zone = "europe-west2-c"
}
An example of a resource is
resource "google_bigquery_dataset" "test" {
location = "EU"
dataset_id = "test"
friendly_name = "test"
}

Terraform AWS provider upgrade issue with RDS

Trying to upgrade AWS provider to version 4, but getting the following error in RDS module:
Error: Conflicting configuration arguments
│
│ with module.my-instance-mysql-eu[0].module.rds.module.db_instance.aws_db_instance.this[0],
│ on .terraform/modules/my-instance-mysql-eu.rds/modules/db_instance/main.tf line 47, in resource "aws_db_instance" "this":
│ 47: db_name = var.db_name
│
│ "db_name": conflicts with replicate_source_db
The error is stating that the db_name attribute conflicts with the replicate_source_db attribute; you cannot specify both attributes, it must be one or the other. This is also mentioned in the Terraform documentation.
If you are replicating an existing RDS database, the database name will be the same as the name of the source. If this is a new database, do not set the replicate_source_db attribute at all.
I encountered a similar issue with the engine & engine_version variables:
│ Error: Conflicting configuration arguments
│
│ with module.production.module.replica_app_db_production.aws_db_instance.db,
│ on modules/rds/postgres/main.tf line 36, in resource "aws_db_instance" "db":
│ 36: engine = var.engine
│
│ "engine": conflicts with replicate_source_db
╵
╷
│ Error: Conflicting configuration arguments
│
│ with module.production.module.replica_app_db_production.aws_db_instance.db,
│ on modules/rds/postgres/main.tf line 37, in resource "aws_db_instance" "db":
│ 37: engine_version = var.engine_version
│
│ "engine_version": conflicts with replicate_source_db
╵
I found a good example of a solution here: https://github.com/terraform-aws-modules/terraform-aws-rds/blob/v5.2.2/modules/db_instance/main.tf
And I managed to solve this with the below conditions:
# Replicas will use source metadata
username = var.replicate_source_db != null ? null : var.username
password = var.replicate_source_db != null ? null : var.password
engine = var.replicate_source_db != null ? null : var.engine
engine_version = var.replicate_source_db != null ? null : var.engine_version
If var.replicate_source_db is not null, then the username/password/engine/engine_version will be set to null (which is what we need as these variables cannot be specified for a replica). And if it is not a replica, then we will have the variables set accordingly :)
You can add the same for the db_name parameter:
db_name = var.replicate_source_db != null ? null : var.db_name

GCP - Terraform - google_project_services module breaks terraform pipeline

As per subject title, when using this module to just delete the default network and nothing else, this module breaks my terraform entirely, even when the module is removed/commented out, I get the same error still:
resource "google_project_service" "service" {
project = var.project_id
service = "compute.googleapis.com"
disable_dependent_services = false
disable_on_destroy = false
provisioner "local-exec" {
command = "gcloud -q compute networks delete default --project=${var.project_id}"
}
}
This is the error I get (replaced my actual project id with "project_id"):
Error: Error when reading or editing Project Service project_id/compute.googleapis.com: Error disabling service "compute.googleapis.com" for project "project_id": googleapi: Error 400: The service compute.googleapis.com is depended on by the following active service(s): container.googleapis.com; Please specify disable_dependent_services=true if you want to proceed with disabling all services.
│ Help Token: Ae-hA1POavq8x9V18i7Um0cW3sx_9lXuuNzjqDzX3zZ3HEYjJ91bGelEobL22DVMdY27NCRrCtZbyE-GbagPtdmxWhdpSamwl0JJomQ4KTRUQDK5
│ Details:
│ [
│ {
│ "#type": "type.googleapis.com/google.rpc.PreconditionFailure",
│ "violations": [
│ {
│ "subject": "?error_code=100001\u0026service_name=compute.googleapis.com\u0026services=container.googleapis.com",
│ "type": "googleapis.com"
│ }
│ ]
│ },
│ {
│ "#type": "type.googleapis.com/google.rpc.ErrorInfo",
│ "domain": "serviceusage.googleapis.com",
│ "metadata": {
│ "service_name": "compute.googleapis.com",
│ "services": "container.googleapis.com"
│ },
│ "reason": "COMMON_SU_SERVICE_HAS_DEPENDENT_SERVICES"
│ }
│ ]
│ , failedPrecondition
Have had issues like this before with this module when wanting to enable gcp apis in a newly created project with terraform so just stopped using it.
Any ideas how I fix the above?
I am doing a terraform init, refresh, plan and apply, this fails and gets the error above on the terraform apply stage.
seems the module/resource was still defined in the state file, removing that fixed it
Since some other service using compute.googleapis.com as a dependent service. so its stopping compute.googleapis.com from disabling because it might affect those dependent service. Here is the Reference Doc1
Try like this it should work
disable_dependent_services = True

DataBricks Sample Terraform Code causes error in AWS VPC module

I'm completely new to DataBricks and trying to deploy an E2 workspace using the sample Terraform code provided by DataBricks. I've just started with the VPC part:
data "aws_availability_zones" "available" {}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
# version = "3.2.0"
name = local.prefix
cidr = var.cidr_block
azs = data.aws_availability_zones.available.names
enable_dns_hostnames = true
enable_nat_gateway = true
single_nat_gateway = true
create_igw = true
private_subnets = [cidrsubnet(var.cidr_block, 3, 1),
cidrsubnet(var.cidr_block, 3, 2)]
manage_default_security_group = true
default_security_group_name = "${local.prefix}-sg"
default_security_group_egress = [{
cidr_blocks = "0.0.0.0/0"
}]
default_security_group_ingress = [{
description = "Allow all internal TCP and UDP"
self = true
}]
}
When I run terraform plan I get this error:
│ Error: Error in function call
│
│ on .terraform/modules/vpc/main.tf line 1090, in resource "aws_nat_gateway" "this":
│ 1090: subnet_id = element(
│ 1091: aws_subnet.public.*.id,
│ 1092: var.single_nat_gateway ? 0 : count.index,
│ 1093: )
│ ├────────────────
│ │ aws_subnet.public is empty tuple
│ │ count.index is 0
│ │ var.single_nat_gateway is true
│
│ Call to function "element" failed: cannot use element function with an empty list.
Would really appreciate any pointers on what is going wrong here.
You set that you want internet gateway create_igw = true, but you haven't specified public_subnets. You must have public_subnets if you have igw.