Terraform - reference different variable within resource or data source dependent on some logic - variables

With this code, I'm planning on having mulitple variables like "vms". These are key/value maps of VMs and resource groups where I for_each on the data source lookup (backupvm) to get their id. Then I can add these VMs, using id, as a backup item in vault.
data "azurerm_virtual_machine" "backupvm" {
for_each = var.vms
name = each.key
resource_group_name = each.value
}
variable "vms" {
type = map
default = {
# "vm name" = "resource group name"
"vaulttestvm1" = "vaulttestrg"
"vaulttestvm2" = "vaulttestrg"
"vaulttestvm4" = "vaulttestrg"
}
}
resource "azurerm_resource_group" "rg" {
name = var.rg_name
location = var.location
}
resource "azurerm_recovery_services_vault" "vault" {
name = var.vault_name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "Standard"
# change later
soft_delete_enabled = false
}
resource "azurerm_backup_protected_vm" "vms" {
for_each = var.vms
recovery_vault_name = azurerm_recovery_services_vault.vault.name
resource_group_name = azurerm_recovery_services_vault.vault.resource_group_name
source_vm_id = data.azurerm_virtual_machine.backupvm[each.key].id
backup_policy_id = azurerm_backup_policy_vm.VMBackupPolicy.id
}
I need a way to reference var.vms, in both the data source & resource, so I could interchange using some logic for another map type variable. So if it was PowerShell, it would be something like:
name = if ($env -eq "prod") { $var.vms } elseif ($env -eq "stage") { $var.vmsstage } elseif ($env -eq "dev") { $var.vmsdev }
I've spent a day trying different things but haven't really got close. I may be somewhat restricted as I need to lookup the vm id using the data source first then loop through my resource (azurerm_backup_protected_vm) dropping that ID in. A solution to this would prevent me from having to use multiple data sources and resources of the same type. Thanks!

Related

I am trying to add custom validation for variables in my terraform script using map but i am facing error

I am trying to add custom validation for the variables in my terraform script for S3 bucket. But i am facing an error that is mentioned as below:
Reference to undeclared input variable
on main.tf line 2, in resource "aws_s3_bucket" "gouth_bucket_1_apr_2021":
2: bucket = var.bucket #"terraform-s3-bucket"
An input variable with the name "bucket" has not been declared. This variable
can be declared with a variable "bucket" {} block."
Can anyone help me on the same.please let me know which file needs the necessary changes and how.
Thanks in Advance
Below is my code :
main.tf :
resource "aws_s3_bucket" "gouth_bucket_1_apr_2021" {
bucket = var.bucket
acl = "private"
tags= var.tags
}
s3.tfvars :
bucket = "first-bucket-gouth"
#Variables of Tags
tags= {
name = "s3bucket",
account_id = "1234567",
owner = "abc#def.com",
os= "windows",
backup = "N",
application = "abc",
description = "s3 bucket",
env = "dev",
ticketid = "101",
marketami = "NA",
patching = "NA",
dc = "bangalore"
}
validation.tf :
variable "tags" {
type = map(string)
validation {
condition = length(var.tags["env"]) > 0
error_message = "Environment tag is required !!"
}
validation {
condition = length(var.tags["owner"]) > 0
error_message = "Owner tag is required !!"
}
validation {
condition = length(var.tags["dc"]) > 0
error_message = "DC tag is required !!"
}
validation {
condition = can(var.tags["account_id"])
error_message = "Acoount ID tag is required!!"
}
}
I can see two potential issues.
You are referencing var.bucket in your resource, but you are not defining a variable for it anywhere in your definition. This could simply look like:
variable "bucket" {}
You may not be picking up your tfvars file, if you are running Terraform with the tfvars file as an option like so terraform plan -var-file=s3.tfvars then thats ok, or you can rename your tfvars file to something.auto.tfvars or terraform.tfvars to get automatically used. (See > https://www.terraform.io/docs/language/values/variables.html#variable-definitions-tfvars-files)
I hope this answers your question.

Terraform update access policies

I am facing a problem and workflow related to terraform to automate the creation of storage account, key vaults and access policies.
What I am trying to achieve is as follow:
I have a storage-account that runs with a for_each loop:
//==================================================
// Automation storage accounts
//==================================================
resource "azurerm_storage_account" "storage-foreach" {
for_each = var.storage-foreach
access_tier = "Hot"
account_kind = "StorageV2"
account_replication_type = "LRS"
account_tier = "Standard"
location = var.location
name = each.value
resource_group_name = azurerm_resource_group.tenant-testing-hamza.name
depends_on = [azurerm_key_vault_key.client-key]
identity {
type = "SystemAssigned"
}
lifecycle {
prevent_destroy = false
}
}
this storage account resource, loops through this variable to create the storage accounts
variable "storage-foreach" {
type = map(string)
default = { "storage1" = "storage1", "storage2" = "storage2", "storage3" = "storage3", "storage4" = "storage4"}
}
so far everything works smoothly. Than I wanted to add those storage accounts object id to my key vault access policy, as follow:
resource "azurerm_key_vault_access_policy" "storage" {
for_each = var.storage-foreach
key_vault_id = azurerm_key_vault.tenantsnbshared.id
tenant_id = "<tenant-id"
object_id = azurerm_storage_account.storage-foreach[each.key].identity.0.principal_id
key_permissions = ["get", "Create", "List", "Restore", "Recover", "Unwrapkey", "Wrapkey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify"]
secret_permissions = ["get", "set", "list", "delete", "recover"]
}
so far everything works just fine while creating the resource, I have all the access policies in place. But, If I try to remove, for example the storage1 from my variable, the storage account get deleted and the access policies related to that specific storage, which is good.
And here the main issue I am facing. If I try to add again the same storage in the variable and run a terraform apply , what happen is that the 3 policies still existing they get removed and the access policy for the storage account get created. If I do one more time terraform apply the logic get inverted, it will delete the first storage account access policy and add the other 3.
I can't find a solution to just update my access policies accordingly to the element I have set in my variable.

how to associate floating ip address to a instance in openstack using terraform

I am using terraform to create couple of instances in openstack and I would like to automatically assign floatings ip address to them without any manual intervention.
My .tf file is as below:
resource "openstack_networking_floatingip_v2" "floating-ip" {
count = 4
pool = "floating-ip-pool"
}
resource "openstack_compute_floatingip_associate_v2" "fip-associate" {
floating_ip = openstack_networking_floatingip_v2.floating-ip.address[count.0]
instance_id = openstack_compute_instance_v2.terraform-vm.id[count.0]
}`
I am getting an error
"Error: Missing resource instance key
on image-provisioning.tf line 33, in resource "openstack_compute_floatingip_associate_v2" "fip-associate":
33: instance_id = openstack_compute_instance_v2.terraform-vm.id[count.0]"
My terraform version is : Terraform v0.12.24
+ provider.openstack 1.26.0
able to resolve using for_each option in terraform :
resource "openstack_compute_instance_v2" "terraform_vm" {
image_id = "f8b9189d-2518-4a32-b1ba-2046ea8d47fd"
for_each = var.instance_name
name = each.key
flavor_id = "3"
key_pair = "openstack vm key"
security_groups = ["default"]
network {
name = "webapps-network"
}
}
resource "openstack_networking_floatingip_v2" "floating_ip" {
pool = "floating-ip-pool"
for_each = var.instance_name
}
resource "openstack_compute_floatingip_associate_v2" "fip_associate" {
for_each = var.instance_name
floating_ip = openstack_networking_floatingip_v2.floating_ip[each.key].address
instance_id = openstack_compute_instance_v2.terraform_vm[each.key].id
}

Declaring list and map variable values in terraform.tfvars

I recently moved over the values for my variables within my terraform code to terraform.tfvars. I am now getting an error that is due to how I am declaring my list and map variables. The code where I am getting the error is replicated below:
image_id = var.web_amis[var.region]
this is how I have these variables specified in terraform.tfvars:
web_amis = ["ami-0dacb0c129b49f529", "ami-00068cd7555f543d5", ]
this is the error code I am getting:
Error: Invalid index
on autoscaling.tf line 3, in resource "aws_launch_configuration" "web_lc":
3: image_id = var.web_amis[var.region]
|----------------
| var.region is "us-east-2"
| var.web_amis is tuple with 2 elements
The given key does not identify an element in this collection value: a number
is required.
You're trying to access a list element with a non index key instead of by position.
What you probably want instead is to have your web_amis variable be a map that is keyed by the region name:
main.tf
variable "region" {}
variable "web_amis" {}
resource "foo_bar" "baz" {
# ...
image_id = var.web_amis[var.region]
}
terraform.tfvars
web_amis = {
us-east-2 = "ami-0dacb0c129b49f529"
us-west-2 = "ami-00068cd7555f543d5"
}
But, this is a very old school and inelegant way of doing things with Terraform nowadays. Instead you could use the aws_ami data source to look up the AMI for the region based on filters such as tags or the name of the AMI.
A basic example is given in the aws_instance resource documentation:
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "HelloWorld"
}
}

Terraform 0.12.5 | Bigquery table resource doesn't support external data configuration block

I'm creating a module to make easy to provision a BigQuery table in GCP.
My module is working, but now I'm trying to add the option to create the table based in external data, like a GCS bucket.
In the doc (https://www.terraform.io/docs/providers/google/r/bigquery_table.html#external_data_configuration) are saying this configuration is supported, but I get only this error:
Acquiring state lock. This may take a few moments...
Error: Unsupported block type
on ../../modules/bq_table/main.tf line 24, in resource "google_bigquery_table" "default":
24: external_data_configuration {
Blocks of type "external_data_configuration" are not expected here.
Im using the last Terraform version (0.12.5) and google provider v2.10.0 in Mac OS.
Here is my module code in HCL2:
resource "google_bigquery_table" "default" {
dataset_id = "${terraform.workspace}_${var.bq_dataset_id}"
table_id = "${terraform.workspace}_${var.bq_table_id}"
project = (var.project_id != "" ? var.project_id : null)
description = (var.bq_table_description != "" ? var.project_id : null)
expiration_time = (var.bq_table_expiration_time != null ? var.project_id : null)
friendly_name = (var.bq_table_name != "" ? var.project_id : null)
dynamic "external_data_configuration" {
for_each = var.bq_table_external_data_configuration
content {
autodetect = true
source_format = "NEWLINE_DELIMITED_JSON"
source_uris = [external_data_configuration.value]
}
}
time_partitioning {
type = "DAY"
field = var.bq_table_partition_field
}
labels = var.bq_table_labels
schema = (var.bq_table_schema != "" ? var.bq_table_schema : null)
dynamic "view" {
for_each = (var.bq_table_view_query != "" ? {query = var.bq_table_view_query} : {})
content {
query = view.value
}
}
depends_on = ["null_resource.depends_on"]
}
Above im using Dynamic blocks, but tried to use normally and the error is the same.
The for_each property inside your dynamic block expects an array value. Try wrapping the input variable in an array:
dynamic "external_data_configuration" {
for_each = var.bq_table_external_data_configuration ? [var.bq_table_external_data_configuration] : []
content {
autodetect = true
source_format = "NEWLINE_DELIMITED_JSON"
source_uris = [external_data_configuration.value]
}
}
Conditional blocks are still a bit of a hassle even after Terraform 0.12; read here for more.