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
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?
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]
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.
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)
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.