Ommit optional blocks in terraform module - terraform

Currently I'm trying to create a universal sql_database module in Terraform. I want to have control over arguments I want to include in this resource. For example one time I need only required arguments but next time in another project I need them plus threat_detection_policy block with all nested arguments.
modules/sql_database.tf
resource "azurerm_sql_database" "sql-db" {
name = var.sql-db-name
resource_group_name = data.azurerm_resource_group.rg-name.name
location = var.location
server_name = var.server-name
edition = var.sql-db-edition
collation = var.collation
create_mode = var.create-mode
requested_service_objective_name = var.sql-requested-service-objective-name
read_scale = var.read-scale
zone_redundant = var.zone-redundant
extended_auditing_policy {
storage_endpoint = var.eap-storage-endpoint
storage_account_access_key = var.eap-storage-account-access-key
storage_account_access_key_is_secondary = var.eap-storage-account-access-key-is-secondary
retention_in_days = var.eap-retention-days
}
import = {
storage_uri = var.storage-uri
storage_key = var.storage-key
storage_key_type = var.storage-key-type
administrator_login = var.administrator-login
administrator_login_password = var.administrator-login-password
authentication_type = var.authentication-type
operation_mode = var.operation-mode
}
threat_detection_policy = {
state = var.state
disabled_alerts = var.disabled-alerts
email_account_admins = var.email-account-admins
email_addresses = var.email-addresses
retention_days = var.retention-days
storage_account_access_key = var.storage-account-access-key
storage_endpoint = var.storage-endpoint
use_server_default = var.use-server-default
}
}
modules/variables.tf (few sql_database vars)
variable "sql-db-edition" {
type = string
}
...
variable "state" { #for example this should be optional
type = string
}
...
main.tf
module "sql_database" {
source = "./modules/sql_database"
sql-db-name = "sqldbs-example"
location = "westus"
server-name = "sqlsrv-example"
storage-uri = "" #some values
storage-key = ""
storage-key_type = ""
administrator-login = ""
administrator-login-password = ""
authentication-type = ""
operation-mode = ""
sql-db-edition = "Standard"
collation = "SQL_LATIN1_GENERAL_CP1_CI_AS"
create-mode = "Default"
sql-requested_service_objective_name = "S0"
requested_service_objective_id = ""
read-scale = "false"
zone_redundant = ""
source_database_id = ""
restore_point_in_time = ""
max_size_bytes = ""
source_database_deletion_date = ""
elastic_pool_name = ""
#variables below should be all optional
state = ""
disabled_alerts = ""
email_account_admins = ""
email_addresses = ""
retention_days = 6
storage_account_access_key = ""
storage_endpoint = ""
use_server_default = ""
storage_endpoint = ""
storage_account_access_key = ""
storage_account_access_key_is_secondary = "false"
retention_in_days = 6
}
Thank you in advance for help!

For your requirements, I think a possible way is to set the default values inside the module and make the default values act as you do not set them. For example, in the threat_detection_policy block, the property use_server_default, when you do not set it, the default value is Disabled. And when you want to set them, just input the values in the module block.

Related

How to prompt for user info for a key in a map type variable in Terraform

I have the following variable in terraform:
rds_config_list = [
{
rds_name = "shiftleft"
rds_identifier = "shiftleft-postgres"
rds_password = <USETINPUT>
rds_snapshot_identifier = "shiftleft"
rds_postgres_instance_class = "db.m6g.large"
rds_postgres_engine_version = "13.3"
rds_postgres_family = "postgres13"
rds_postgres_allocated_storage = 100
rds_postgres_max_allocated_storage = 1000
rds_backup_retention_period = 7
rds_postgres_multi_az = false
rds_postgres_deletion_protection = false
},
{
rds_name = "shiftleft2"
rds_identifier = "shiftleft2-postgres"
rds_password = <USETINPUT>
rds_snapshot_identifier = "shiftleft2"
rds_postgres_instance_class = "db.m6g.large"
rds_postgres_engine_version = "13.3"
rds_postgres_family = "postgres13"
rds_postgres_allocated_storage = 100
rds_postgres_max_allocated_storage = 1000
rds_backup_retention_period = 7
rds_postgres_multi_az = false
rds_postgres_deletion_protection = false
}
]
i want to prompt for user input for the passwords for each of them and not have the same password for all databases, this is inside a .tfvars file. is there any way to do it?

How to solve the Invalid Index in Terraform?

When I try to add a PTR record in DNS, I get this error with Invalid index. I am uncertain how to remove the error.
resource "openstack_compute_instance_v2" "app-stage" {
count = length(var.datacenter)
name = "app-stage-${var.datacenter[count.index]}.example.com"
flavor_name = var.flavor["app-stage"]
availability_zone = element(var.datacenter, count.index)
key_pair = var.key_pair
image_id = var.os_image
config_drive = true
user_data = data.template_file.app-stage[count.index].rendered
scheduler_hints {
group = openstack_compute_servergroup_v2.app_sg.id
}
network {
port = openstack_networking_port_v2.app-stage[count.index].id
}
}
resource "dns_aaaa_record_set" "app-stage-dns" {
count = length(var.datacenter)
zone = format("%s.", var.dns_zone)
name = "app-stage-${var.datacenter[count.index]}.example"
addresses = [replace(openstack_compute_instance_v2.app-stage[count.index].access_ip_v6, "/\\[|\\]/", "")]
ttl = 300
}
resource "dns_ptr_record" "app-stage-dns-ptr" {
count = length(var.datacenter)
zone = format("%s.", var.dns_ptr_zone)
ptr = "app-stage-${var.datacenter[count.index]}.example"
name = tolist(dns_aaaa_record_set.app-stage-dns)[count.index].addresses[0]
ttl = 300
This is the error-messages i get when running terraform apply,:
Error: Invalid index
on app-stage.tf line 94, in resource "dns_ptr_record" "app-stage-dns-ptr":
94: name = tolist(dns_aaaa_record_set.app-stage-dns)[count.index].addresses[0]
|----------------
| count.index is 1
| dns_aaaa_record_set.app-stage-dns is tuple with 2 elements
This value does not have any indices.
This is repeated 2 times, since I try to create 2 machines/2records.
Based on the comments.
It should be:
name = tolist(dns_aaaa_record_set.app-stage-dns[count.index].addresses)[0]
not (closing parenthesis in different place)
name = tolist(dns_aaaa_record_set.app-stage-dns)[count.index].addresses[0]

Terraform- Azure IPgroups tags update for no reason

I'm using Azure and terraform to deploy some ipgroups.
I'm using loops and modules to deploy my ipgroups and everything is deploying correctly.
But when I do a terraform plan after my deployment, terraforms says it will update my ipgroups tags.
And of course, I changed nothing between 2 terraform runs :
# module.CreateAzureRmIpGroup.azurerm_ip_group.ipgroup[0] will be updated in-place
~ resource "azurerm_ip_group" "ipgroup" {
id = "xxx"
name = "ipgr-all-allservers-weeu-001"
~ tags = {
+ "Area" = "westeurope"
+ "Business_Line" = "bb"
+ "Creation_Date" = "10/02/2021"
+ "Environment" = "hub"
+ "Gdpr" = "2"
+ "Owner" = "aa"
+ "Ressource_Type" = "ipgroup"
- "area" = "westeurope" -> null
- "business_Line" = "bb" -> null
- "creation_Date" = "10/02/2021" -> null
- "environment" = "hub" -> null
- "gdpr" = "2" -> null
- "owner" = "aa" -> null
- "ressource_Type" = "ipgroup" -> null
}
# (3 unchanged attributes hidden)
}
As you can see , It will pass my tags to null and pass from null to my old tags. Just the tags , not even the ip of ipgroups...
I create the tags like I do for others services and I only have a problem with ipgroups tags.....(no pb with networks/firewalls/vpn)
Here's my ipgroup resource :
resource "azurerm_ip_group" "ipgroup" {
count = length(var.ipgroup)
name = "ipgr-${var.ipgroup[count.index]["ipgr_content"]}-${var.ipgroup[count.index]
["ipgr_loc"]}-${var.ipgroup[count.index]["id"]}"
location = var.ipgroup_location
resource_group_name = var.ipgroup_rg_name
cidrs = split(",", var.ipgroup[count.index]["ipgr_cidr"])
tags = {
Owner = var.ipgroup[count.index]["owner"]
Business_Line = var.ipgroup[count.index]["business_line"]
Area = var.ipgroup[count.index]["area"]
Environment = var.ipgroup[count.index]["environment"]
Creation_Date = var.ipgroup[count.index]["creation_date"]
Gdpr = var.ipgroup[count.index]["gdpr"]
Ressource_Type = "ipgroup"
}}
And this is a tfvars example :
ipgroup = [{
ipgr_content = "all-allservers"
ipgr_loc = "weeu"
id = "001"
ipgr_cidr = "10.0.0.0/20"
owner = "aa"
business_line ="bb"
area = "westeurope"
environment = "hub"
creation_date = "10/02/2021"
gdpr = "2"
},]
I tried to comment my tags but even with that terraform still update the ressource's tag.
I don't know if there is a real impact to have an update everytime (I will have to do an apply if I have to add more ipgroups). I don't want to take the risk when I will use these ipgroups in my firewall.

I have created 4 subnets and created output values to get all subnets ids in one variable .So how can I plan to retrieve 2 values to attach the nics

Error: Unbalanced parentheses
on .terraform\modules\nics\main.tf line 19, in resource "azurerm_network_interface" "NIC1":
19: subnet_id = "${element(var.subnetwork-subnetid.*.id, (0,1))}"
output values of subnets:
output "subnetwork-subnetid" {
value = concat(azurerm_subnet.subnetwork.*.id, azurerm_subnet.subnetwork6.*.id)
}
nic.tf
resource "azurerm_network_interface" "NIC1" {
#count = "${length(var.subnetwork-subnetid)}"
#for_each= toset(var.subipv4)
count = "${length(var.subipv4)}"
name = "${lookup(element(var.subipv4, count.index), "name")}"
#name = var.nic-name
location = var.rg-location
resource_group_name = var.rg-name
enable_ip_forwarding = true
enable_accelerated_networking = true
ip_configuration {
name = "ipconfig"
subnet_id = "${element(var.subnetwork-subnetid.*.id, (0,1))}"
private_ip_address_allocation = "Dynamic"
#public_ip_address_id = azurerm_public_ip.pubip.id
#public_ip_address_id = azurerm_public_ip.pubip.*.id
primary = true
}
tags = {
name = "${lookup(element(var.subipv4, count.index), "name")}"
}
}```
Please someone help me in this issue.Thanks!
Second argument in element is index:
index finds the index for a particular element value.
Thus to get few elements from the list based on indices, you can do:
subnet_id = [ for idx in [0, 1]: element(var.subnetwork-subnetid.*.id, idx) ]
If you want a range of indies, you can use slice:
subnet_id = slice(var.subnetwork-subnetid.*.id, 0, 2)

Terraform nested loop in nested list

I'm trying to create a dynamic method to create vms in multiple environments that will be configured by the end user.
Tried for loops with nested loops. flatten function, count, etc but haven't found a way to reach my goal.
I have terrafrom.tfvars with the follwing structure:
Bigip_devices = {
main_hub = {
region = "eastus"
azs = ["1"] #Azure availabilty zones
vnet_name = "vnet-main" # Vnet name to deploy to
bigip_instance_count = 2 # Number of instnaces to delpoy
cluster = "yes" # Deploy as a cluster or stand alone device
version = "" # Leave blank for default value
sku = "" # Leave blank for default value - f5-bigip-virtual-edition-25m-best-hourly
offer = "" # Leave blank for default value - f5-big-ip-best
instance_type = "" # Leave blank for default value - Standard_DS3_v2
disable_password_authentication = "" #Leave blank for default value
tags = ""
}
spoke = {
region = "eastus"
azs = ["1","2"] #Azure availabilty zones
vnet_name = "vnet-spoke" # Vnet name to deploy to
bigip_instance_count = 4 # Number of instnaces to delpoy
cluster = "yes" # Deploy as a cluster or stand alone device
version = "" # Leave blank for default value
sku = "" # Leave blank for default value - f5-bigip-virtual-edition-25m-best-hourly
offer = "" # Leave blank for default value - f5-big-ip-best
instance_type = "" # Leave blank for default value - Standard_DS3_v2
disable_password_authentication = "" #Leave blank for default value
tags = ""
}
}
What is the correct method to iterate each key in the list( in the example the are 2 keys - main hub and spoke) and to create the amount of virtual machines corresponding to the bigip_instance_count setting.
In the above example, I want to create 2 environments, one with 2 devices and the second with 4 devices.
Is there a way to achieve it?
It would be really convenient if you transform the above complex JSON into a collection that has one element per resource you want to create. To achieve this, you could use the flatten function.
locals {
# A list of objects with one object per instance.
flattened_values = flatten([
for ip_key, ip in var.Bigip_devices : [
for index in range(ip.bigip_instance_count) : {
region = ip.region
azs = ip.azs
ip_index = index
ip_key = ip_key
cluster = ip.cluster
version = ip.version
sku = ip.sku
offer = ip.offer
instance_type = ip.instance_type
disable_password_authentication = ip.disable_password_authentication
tags = ip.tags
}
]
])
}
With the above flattened function, you get below list of collection of resources, you would like to create.
flattened_value_output = [
{
"azs" = [
"1",
]
"cluster" = "yes"
"disable_password_authentication" = ""
"instance_type" = ""
"ip_index" = 0
"ip_key" = "main_hub"
"offer" = ""
"region" = "eastus"
"sku" = ""
"tags" = ""
"version" = ""
},
{
"azs" = [
"1",
]
"cluster" = "yes"
"disable_password_authentication" = ""
"instance_type" = ""
"ip_index" = 1
"ip_key" = "main_hub"
"offer" = ""
"region" = "eastus"
"sku" = ""
"tags" = ""
"version" = ""
},
{
"azs" = [
"1",
"2",
]
"cluster" = "yes"
"disable_password_authentication" = ""
"instance_type" = ""
"ip_index" = 0
"ip_key" = "spoke"
"offer" = ""
"region" = "eastus"
"sku" = ""
"tags" = ""
"version" = ""
},
{
"azs" = [
"1",
"2",
]
"cluster" = "yes"
"disable_password_authentication" = ""
"instance_type" = ""
"ip_index" = 1
"ip_key" = "spoke"
"offer" = ""
"region" = "eastus"
"sku" = ""
"tags" = ""
"version" = ""
},
{
"azs" = [
"1",
"2",
]
"cluster" = "yes"
"disable_password_authentication" = ""
"instance_type" = ""
"ip_index" = 2
"ip_key" = "spoke"
"offer" = ""
"region" = "eastus"
"sku" = ""
"tags" = ""
"version" = ""
},
{
"azs" = [
"1",
"2",
]
"cluster" = "yes"
"disable_password_authentication" = ""
"instance_type" = ""
"ip_index" = 3
"ip_key" = "spoke"
"offer" = ""
"region" = "eastus"
"sku" = ""
"tags" = ""
"version" = ""
},
]
From the above collection, you could iterate & create resources with unique keys like below::
resource "some_resource" "example" {
for_each = {
# Generate a unique string identifier for each instance
for ip in local.flattened_value_output : format("%s-%02d", ip.ip_key, ip.ip_index + 1) => ip
}
}
This way, the creation or updating of resources is guaranteed as each resource uses a unique key.
For more details, refer this discussion I had with Hashicorp personnel.

Resources