Dynamic block with optional data - dynamic

let say we have this locals:
locals = {
schemas = [
{
name = "is_cool"
attribute_data_type = "Boolean"
mutable = true
required = false
},
{
name = "firstname"
attribute_data_type = "String"
mutable = true
required = false
min_length = 1
max_length = 256
}
]
}
What I would like to achieve is to use dynamic to build schemas and when the schema is a string I would like to add the string_attribute_constraints block.
This is what I did so far but it adds an empty string_attribute_constraints block when the schema is Boolean
dynamic "schema" {
for_each = var.schemas
content {
name = schema.value.name
attribute_data_type = schema.value.attribute_data_type
mutable = schema.value.mutable
required = schema.value.required
string_attribute_constraints {
min_length = lookup(schema.value, "min_length", null)
max_length = lookup(schema.value, "max_length", null)
}
}
}
terraform plan:
+ schema {
+ attribute_data_type = "Boolean"
+ mutable = true
+ name = "is_cool"
+ required = false
+ string_attribute_constraints {}
}

You can use a second nested dynamic block to tell Terraform how many string_attribute_constraints blocks to generate based on your rule:
dynamic "schema" {
for_each = var.schemas
content {
name = schema.value.name
attribute_data_type = schema.value.attribute_data_type
mutable = schema.value.mutable
required = schema.value.required
dynamic "string_attribute_constraints" {
for_each = schema.attribute_data_type == "String" ? [1] : []
content {
min_length = lookup(schema.value, "min_length", null)
max_length = lookup(schema.value, "max_length", null)
}
}
}
}
This works by making the for_each for the nested dynamic be an empty list in the case where we don't want to generate any blocks, and making it a single-element list in the case where we do. Since we need no references to string_attribute_constraints.key or string_attribute_constraints.value inside the block, we can set the value of the single element to anything, and so I just set it to 1 as an arbitrary placeholder.

dynamic "schema" {
for_each = local.my_schema
content {
name = schema.value.name
attribute_data_type = schema.value.attribute_data_type
mutable = schema.value.mutable
required = schema.value.required
dynamic "string_attribute_constraints" {
for_each = schema.value.attribute_data_type == "String" ? [1] : []
content {
min_length = lookup(schema.value, "min_length", 0)
max_length = lookup(schema.value, "max_length", 2048)
}
}
dynamic "number_attribute_constraints" {
for_each = schema.value.attribute_data_type == "Number" ? [1] : []
content {
min_value = lookup(schema.value, "min_value", 0)
max_value = lookup(schema.value, "max_value", 2048)
}
}
}
}

Related

terraform dynamic block using list of map

I have a terraform variable:
variable "volumes" {
default = [
{
"name" : "mnt",
"value" : "/mnt/cvdupdate/"
},
{
"name" : "efs",
"value" : "/var"
},
]
}
and I am trying to create a dynamic block
dynamic "volume" {
for_each = var.volumes == "" ? [] : [true]
content {
name = volume["name"]
}
}
but I get an error when I run plan
name = volume["name"]
│
│ The given key does not identify an element in this collection value.
the desired output would be:
volume {
name = "mnt"
}
volume {
name = "efs"
}
what is wrong with my code?
Since you are using for_each, you should use value. Also you condition is incorrect. It all should be:
dynamic "volume" {
for_each = var.volumes == "" ? [] : var.volumes
content {
name = volume.value["name"]
}
}
As you are creating an if-else like condition to pass value to for loop, the condition needs a value to set. https://developer.hashicorp.com/terraform/language/meta-arguments/for_each
Need to replace [true] with var.volumes to pass the value.
for_each = var.volumes == "" ? [] : var.volumes
And, then set the value in the content block with .value to finally set the values to use.
content {
name = volume.value["name"]
The final working code is below as #marcin posted.
dynamic "volume" {
for_each = var.volumes == "" ? [] : var.volumes
content {
name = volume.value["name"]
}
}
You can simply use for_each = var.volumes[*]:
dynamic "volume" {
for_each = var.volumes[*]
content {
name = volume.value["name"]
}
}
or:
dynamic "volume" {
for_each = var.volumes[*]
content {
name = volume.value.name # <------
}
}

azurerm_mssql_virtual_machine - already exists

Trying to do an AZ Terraform deployment, and failing horribly - looking for some ideas what am I missing. Basically I am trying to deploy 2 (maybe later more) VM-s with variable size of disks, joining them to the domain and add SQL server to them. (Be gentle with me, I am from VMWare-Tf background, this is my first SQL deployment on AZ!)
My module:
## main.tf:
# ----------- NIC --------------------------------
resource "azurerm_network_interface" "nic" {
name = "${var.vm_name}-nic"
resource_group_name = var.rg.name
location = var.location
ip_configuration {
name = "${var.vm_name}-internal"
subnet_id = var.subnet_id
private_ip_address_allocation = "Static"
private_ip_address = var.private_ip
}
dns_servers = var.dns_servers
}
# ----------- VM --------------------------------
resource "azurerm_windows_virtual_machine" "vm" {
/* count = length(var.instances) */
name = var.vm_name
location = var.location
resource_group_name = var.rg.name
network_interface_ids = [azurerm_network_interface.nic.id]
size = var.size
zone = var.zone
admin_username = var.win_admin_user
admin_password = var.win_admin_pw # data.azurerm_key_vault_secret.vmadminpwd.value
enable_automatic_updates = "false"
patch_mode = "Manual"
provision_vm_agent = "true"
tags = var.vm_tags
source_image_reference {
publisher = "MicrosoftSQLServer"
offer = "sql2019-ws2019"
sku = "enterprise"
version = "latest"
}
os_disk {
name = "${var.vm_name}-osdisk"
caching = "ReadWrite"
storage_account_type = "StandardSSD_LRS"
disk_size_gb = 250
}
}
# ----------- DOMAIN JOIN --------------------------------
// Waits for up to 1 hour for the Domain to become available. Will return an error 1 if unsuccessful preventing the member attempting to join.
resource "azurerm_virtual_machine_extension" "wait-for-domain-to-provision" {
name = "TestConnectionDomain"
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.9"
virtual_machine_id = azurerm_windows_virtual_machine.vm.id
settings = <<SETTINGS
{
"commandToExecute": "powershell.exe -Command \"while (!(Test-Connection -ComputerName ${var.active_directory_domain_name} -Count 1 -Quiet) -and ($retryCount++ -le 360)) { Start-Sleep 10 } \""
}
SETTINGS
}
resource "azurerm_virtual_machine_extension" "join-domain" {
name = azurerm_windows_virtual_machine.vm.name
publisher = "Microsoft.Compute"
type = "JsonADDomainExtension"
type_handler_version = "1.3"
virtual_machine_id = azurerm_windows_virtual_machine.vm.id
settings = <<SETTINGS
{
"Name": "${var.active_directory_domain_name}",
"OUPath": "",
"User": "${var.active_directory_username}#${var.active_directory_domain_name}",
"Restart": "true",
"Options": "3"
}
SETTINGS
protected_settings = <<SETTINGS
{
"Password": "${var.active_directory_password}"
}
SETTINGS
depends_on = [azurerm_virtual_machine_extension.wait-for-domain-to-provision]
}
# ----------- DISKS --------------------------------
resource "azurerm_managed_disk" "data" {
for_each = var.disks
name = "${var.vm_name}-${each.value.name}"
location = var.location
resource_group_name = var.rg.name
storage_account_type = each.value.sa
create_option = each.value.create
disk_size_gb = each.value.size
zone = var.zone
}
resource "azurerm_virtual_machine_data_disk_attachment" "disk-attachment" {
for_each = var.disks
managed_disk_id = azurerm_managed_disk.data[each.key].id
virtual_machine_id = azurerm_windows_virtual_machine.vm.id
lun = each.value.lun
caching = "ReadWrite"
depends_on = [azurerm_windows_virtual_machine.vm]
}
# ----------- SQL --------------------------------
# configure the SQL side of the deployment
resource "azurerm_mssql_virtual_machine" "sqlvm" {
/* count = length(var.instances) */
virtual_machine_id = azurerm_windows_virtual_machine.vm.id
sql_license_type = "PAYG"
r_services_enabled = true
sql_connectivity_port = 1433
sql_connectivity_type = "PRIVATE"
/* sql_connectivity_update_username = var.sqladmin
sql_connectivity_update_password = data.azurerm_key_vault_secret.sqladminpwd.value */
#The storage_configuration block supports the following:
storage_configuration {
disk_type = "NEW" # (Required) The type of disk configuration to apply to the SQL Server. Valid values include NEW, EXTEND, or ADD.
storage_workload_type = "OLTP" # (Required) The type of storage workload. Valid values include GENERAL, OLTP, or DW.
data_settings {
default_file_path = "F:\\Data"
luns = [1]
}
log_settings {
default_file_path = "G:\\Log"
luns = [2]
}
temp_db_settings {
default_file_path = "D:\\TempDb"
luns = [0]
}
}
}
## provider.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=3.0.1"
#configuration_aliases = [azurerm.corp]
}
}
}
variables.tf
# ----------- COMMON --------------------------------
variable "vm_name" {
type = string
}
variable "rg" {
/* type = string */
description = "STACK - resource group"
}
variable "location" {
type = string
description = "STACK - location"
}
# ----------- NIC --------------------------------
variable "subnet_id" {
type = string
description = "STACK - subnet"
}
variable "private_ip" {
}
variable "dns_servers" {
}
# ----------- VM --------------------------------
variable "size" {
description = "VM - size"
type = string
}
variable "win_admin_user" {
sensitive = true
type = string
}
variable "win_admin_pw" {
sensitive = true
type = string
}
variable "os_storage_type" {
type = string
}
variable "vm_tags" {
type = map(any)
}
variable "zone" {
#type = list
description = "VM AZ"
}
# ----------- DOMAIN JOIN --------------------------------
variable "active_directory_domain_name" {
type = string
}
variable "active_directory_username" {
sensitive = true
}
variable "active_directory_password" {
sensitive = true
}
# ----------- SQL --------------------------------
variable "sql_maint_day" {
type = string
description = "SQL - maintenance day"
}
variable "sql_maint_length_min" {
type = number
description = "SQL - maintenance duration (min)"
}
variable "sql_maint_start_hour" {
type = number
description = "SQL- maintenance start (hour of the day)"
}
# ----------- DISKS --------------------------------
/* variable "disk_storage_account" {
type = string
default = "Standard_LRS"
description = "DATA DISKS - storage account type"
}
variable "disk_create_method" {
type = string
default = "Empty"
description = "DATA DISKS - creation method"
}
variable "disk_size0" {
type = number
}
variable "disk_size1" {
type = number
}
variable "disk_size2" {
type = number
}
variable "lun0" {
type = number
default = 0
}
variable "lun1" {
type = number
default = 1
}
variable "lun2" {
default = 2
type = number
} */
/* variable "disks" {
description = "List of disks to create"
type = map(any)
default = {
disk0 = {
name = "data0"
size = 200
create = "Empty"
sa = "Standard_LRS"
lun = 0
}
disk1 = {
name = "data1"
size = 500
create = "Empty"
sa = "Standard_LRS"
lun = 1
}
}
} */
variable "disks" {
type = map(object({
name = string
size = number
create = string
sa = string
lun = number
}))
}
the actual deployment:
main.tf
/*
PS /home/fabrice> Get-AzVMSize -Location northeurope | where-object {$_.Name -like "*ds13*"}
*/
module "uat_set" {
source = "../modules/vm"
providers = {
azurerm = azurerm.cbank-test
}
for_each = var.uat_set
active_directory_domain_name = local.uat_ad_domain
active_directory_password = var.domain_admin_password
active_directory_username = var.domain_admin_username
disks = var.disk_allocation
dns_servers = local.dns_servers
location = local.uat_location
os_storage_type = local.uat_storage_type
private_ip = each.value.private_ip
rg = data.azurerm_resource_group.main
size = each.value.vm_size
sql_maint_day = local.uat_sql_maintenance_day
sql_maint_length_min = local.uat_sql_maintenance_min
sql_maint_start_hour = local.uat_sql_maintenance_start_hour
subnet_id = data.azurerm_subnet.main.id
vm_name = each.key
vm_tags = var.default_tags
win_admin_pw = var.admin_password
win_admin_user = var.admin_username
zone = each.value.zone[0]
}
variable "uat_set" {
description = "List of VM-s to create"
type = map(any)
default = {
UAT-SQLDB-NE-01 = {
private_ip = "192.168.32.8"
vm_size = "Standard_DS13-4_v2"
zone = ["1"]
}
UAT-SQLDB-NE-02 = {
private_ip = "192.168.32.10"
vm_size = "Standard_DS13-4_v2"
zone = ["2"]
}
}
}
variable "disk_allocation" {
type = map(object({
name = string
size = number
create = string
sa = string
lun = number
}))
default = {
"temp" = {
name = "temp"
size = 200
create = "Empty"
sa = "Standard_LRS"
lun = 0
},
"disk1" = {
name = "data1"
size = 500
create = "Empty"
sa = "Standard_LRS"
lun = 1
},
"disk2" = {
name = "data2"
size = 500
create = "Empty"
sa = "Standard_LRS"
lun = 2
}
}
}
locals {
dns_servers = ["192.168.34.5", "192.168.34.10"]
uat_storage_type = "Standard_LRS"
uat_sql_maintenance_day = "Saturday"
uat_sql_maintenance_min = 180
uat_sql_maintenance_start_hour = 23
uat_ad_domain = "civbdev.local"
uat_location = "North Europe"
}
## variables.tf
# new build variables
variable "Environment" {
default = "DEV"
description = "this is the environment variable used to intperpolate with others vars"
}
variable "default_tags" {
type = map(any)
default = {
Environment = "DEV"
Product = "dev-XXXtemplateXXX"
Terraformed = "https://AllicaBankLtd#dev.azure.com/XXXtemplateXXX/Terraform/DEV"
}
}
variable "admin_username" {
sensitive = true
}
variable "admin_password" {
sensitive = true
}
variable "domain_admin_username" {
sensitive = true
}
variable "domain_admin_password" {
sensitive = true
}
Resources create OK, except the SQL-part
│ Error: A resource with the ID "/subscriptions/<..redacted...>/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines/UAT-SQLDB-NE-02" already exists - to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for "azurerm_mssql_virtual_machine" for more information.
│
│ with module.uat_set["UAT-SQLDB-NE-02"].azurerm_mssql_virtual_machine.sqlvm,
│ on ../modules/vm/main.tf line 115, in resource "azurerm_mssql_virtual_machine" "sqlvm":
│ 115: resource "azurerm_mssql_virtual_machine" "sqlvm" {
│
╵
╷
│ Error: A resource with the ID "/subscriptions/<..redacted...>/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines/UAT-SQLDB-NE-01" already exists - to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for "azurerm_mssql_virtual_machine" for more information.
│
│ with module.uat_set["UAT-SQLDB-NE-01"].azurerm_mssql_virtual_machine.sqlvm,
│ on ../modules/vm/main.tf line 115, in resource "azurerm_mssql_virtual_machine" "sqlvm":
│ 115: resource "azurerm_mssql_virtual_machine" "sqlvm" {
│
╵
Any notions please what I might be missing?
Ta,
Fabrice
UPDATE:
Thanks for those who replied. Just to confirm: it is not an already existing resource. I get this error straight at the time of the creation of these VM-s.
For example, these are my vm-s after the Terraform run (none of them has the sql extension)
Plan even states it will create these:
Terraform will perform the following actions:
# module.uat_set["UAT-SQLDB-NE-01"].azurerm_mssql_virtual_machine.sqlvm will be created
+ resource "azurerm_mssql_virtual_machine" "sqlvm" {
+ id = (known after apply)
+ r_services_enabled = true
+ sql_connectivity_port = 1433
+ sql_connectivity_type = "PRIVATE"
+ sql_license_type = "PAYG"
+ virtual_machine_id = "/subscriptions/..../providers/Microsoft.Compute/virtualMachines/UAT-SQLDB-NE-01"
+ storage_configuration {
+ disk_type = "NEW"
+ storage_workload_type = "OLTP"
+ data_settings {
+ default_file_path = "F:\\Data"
+ luns = [
+ 1,
]
}
+ log_settings {
+ default_file_path = "G:\\Log"
+ luns = [
+ 2,
]
}
+ temp_db_settings {
+ default_file_path = "Z:\\TempDb"
+ luns = [
+ 0,
]
}
}
}
# module.uat_set["UAT-SQLDB-NE-02"].azurerm_mssql_virtual_machine.sqlvm will be created
+ resource "azurerm_mssql_virtual_machine" "sqlvm" {
+ id = (known after apply)
+ r_services_enabled = true
+ sql_connectivity_port = 1433
+ sql_connectivity_type = "PRIVATE"
+ sql_license_type = "PAYG"
+ virtual_machine_id = "/subscriptions/..../providers/Microsoft.Compute/virtualMachines/UAT-SQLDB-NE-02"
+ storage_configuration {
+ disk_type = "NEW"
+ storage_workload_type = "OLTP"
+ data_settings {
+ default_file_path = "F:\\Data"
+ luns = [
+ 1,
]
}
+ log_settings {
+ default_file_path = "G:\\Log"
+ luns = [
+ 2,
]
}
+ temp_db_settings {
+ default_file_path = "Z:\\TempDb"
+ luns = [
+ 0,
]
}
}
}
Plan: 2 to add, 0 to change, 0 to destroy.
Presumably, if these resources would exist somehow - which would be odd, as Tf just created the VM-s - then it would not say in the plan that it will create it now, would it?
So the error is quite the source of my confusion, since if the VM just got created, the creation of the extension failed - how could it possibly be existing?
In this case you should probably just import the modules as the error suggest to your terraform state.
For example
terraform import module.uat_set[\"UAT-SQLDB-NE-02\"].azurerm_mssql_virtual_machine.sqlvm "/subscriptions/<..redacted...>/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines/UAT-SQLDB-NE-02"

terraform azurerm_data_factory_pipeline assing type to the variables

in terraform documentation i found the follow example:
resource "azurerm_data_factory_pipeline" "test" {
name = .....
resource_group_name = ...
data_factory_id = ...
variables = {
"bob" = "item1"
}
but I need to create a boolean variable, in the portal Azure I have the type field.
how can I set the variable like this:
"variables": {
"END": {
"type": "Boolean",
"defaultValue": false
}
}
Based on your question, if you are asking how to create a variable of type boolean in Terraform, that is done like this:
variable "END" {
type = bool
description = "End variable."
default = false
}
You can reference that variable then in the resource definition:
resource "azurerm_data_factory_pipeline" "test" {
name = .....
resource_group_name = ...
data_factory_id = ...
variables = {
"END" = var.END
}
}
Or alternatively you can set it without defining the Terraform variable like this:
resource "azurerm_data_factory_pipeline" "test" {
name = .....
resource_group_name = ...
data_factory_id = ...
variables = {
"END" = false
}
}

Dynamically AWS IAM policy document with principals

I am creating a dynamic AWS IAM policy document "FROM" static to "TO" dynamic but principals part gives "An argument named "principals" is not expected here"
If I delete "principals" from the aws_iam_policy_document it works. Any suggestion would be helpful.
FROM
data "aws_iam_policy_document" "bucket_policy" {
statement {
principals {
type = "AWS"
identifiers = [
"arn:aws:iam::sdfsdfsdeploy",
"arn:aws:iam::sdfsdfsdeploy/OrganizationAccountAccessRole"
]
}
actions = [
"s3:GetObject",
"s3:PutObject"
]
resources = formatlist("arn:aws:s3:::%s/*", var.bucket_name)
}
}
TO
this code in source = "../../modules/s3/main.tf"
data "aws_iam_policy_document" "bucket_policy" {
dynamic "statement" {
for_each = var.policies_list
iterator = role
content {
effect = lookup(role.value, "effect", null)
principals = lookup(role.value, "principals", null)
actions = lookup(role.value, "actions", null)
resources = lookup(role.value, "resources", null)
}
}
}
module "s3_test" {
source = "../../modules/s3"
region = var.region
policies_list = [
{
effect = "Allow"
principals = {
type = "AWS"
identifiers = [
"arn:aws:iam::3ssdfsdfy",
"arn:aws:iam::3ssdfsdfy:role/OrganizationAccountAccessRole"
]
}
actions = [
"s3:GetObject",
"s3:PutObject"
]
resources = formatlist("arn:aws:s3:::%s/*", "teskjkjsdkfkjskdjhkjfhkjhskjdf")
}
]
}
Found it.
variable "policies_list" {
description = "nested block: s3_aws_iam_policy_document"
type = set(object(
{
actions = list(string)
effect = string
principals = set(object(
{
type = string
identifiers = list(string)
}
))
resources = list(string)
}
))
default = []
}
data "aws_iam_policy_document" "bucket_policy" {
dynamic "statement" {
for_each = var. policies_list
iterator = role
content {
effect = lookup(role.value, "effect", null)
actions = lookup(role.value, "actions", null)
dynamic "principals" {
for_each = role.value.principals
content {
type = principals.value["type"]
identifiers = principals.value["identifiers"]
}
}
resources = lookup(role.value, "resources", null)
}
}
}
based on
https://github.com/niveklabs/tfwriter/blob/1ea629ed386bbe6a8f21617a430dae19ba536a98/google-beta/r/google_storage_bucket.md

Looping over map variable using for_each expression in terraform

I have variable that I want to iterate over using for_each in terraform to create multiple instances of submodule - node_groups, which is part of eks module. This is my variable:
variable "frame_platform_eks_node_groups" {
type = map
default = {
eks_kube_system = {
desired_capacity = 1,
max_capacity = 5,
min_capacity = 1,
instance_type = ["m5.large"],
k8s_label = "eks_kube_system",
additional_tags = "eks_kube_system_node"
},
eks_jenkins_build = {
desired_capacity = 1,
max_capacity = 10,
min_capacity = 1,
instance_type = ["m5.large"],
k8s_label = "eks_jenkins_build",
additional_tags = "eks_jenkins_build_node"
}
}
}
And this is my node_groups submodule, which is part of module eks.
module "eks" {
...
node_groups = {
for_each = var.frame_platform_eks_node_groups
each.key = {
desired_capacity = each.value.desired_capacity
max_capacity = each.value.max_capacity
min_capacity = each.value.min_capacity
instance_types = each.value.instance_type
k8s_labels = {
Name = each.value.k8s_label
}
additional_tags = {
ExtraTag = each.value.additional_tags
}
}
When I run terraform plan I am getting following error:
15: each.key = {
If this expression is intended to be a reference, wrap it in parentheses. If
it’s instead intended as a literal name containing periods, wrap it in quotes
to create a string literal.
My intention obviously is to get eks_kube_system and eks_jenkins_build values from the map variable with each.key reference. But something is wrong. Do you have advice what I am doing wrong?
Thank you!
Its not exactly clear what node_groups is as it is not defined in your question, but assuming that it is a list of maps, then the code should be:
module "eks" {
...
node_groups = [
for k,v in var.frame_platform_eks_node_groups:
{
desired_capacity = v.desired_capacity
max_capacity = v.max_capacity
min_capacity = v.min_capacity
instance_types = v.instance_type
k8s_labels = {
Name = v.k8s_label
}
additional_tags = {
ExtraTag = v.additional_tags
}
}
]