I'm trying to write code for AKS with all possible arguments, and then in variables I specify map. Problem is I don't want to specify all of the optional arguments in my variables each time, I wonder if there is a possibility to skip them in variables.
Here is my example:
main.tf
resource "azurerm_kubernetes_cluster" "this" {
for_each = var.AKS_Config
name = each.value.AKS_Name
location = each.value.AKS_Location
resource_group_name = each.value.AKS_ResourceGroupName
dns_prefix = each.value.dns_prefix
#dns_prefix_private_cluster = # dns_prefix or dns_prefix_private_cluster must be specified
kubernetes_version = each.value.kubernetes_version
automatic_channel_upgrade = each.value.automatic_channel_upgrade
api_server_authorized_ip_ranges = each.value.api_server_authorized_ip_ranges
dynamic "identity" {
for_each = each.value.identity
content {
type = identity.value.type
user_assigned_identity_id = can(identity.value.user_assigned_identity_id) == true ? identity.value.user_assigned_identity_id : {}
}
}
}
variables.tf
variable "AKS_Config" {
default = {
"AKS_1" = {
AKS_Name = "AKSTest001"
AKS_Location = "West Europe"
AKS_ResourceGroupName = "SDSADAS"
dns_prefix = "AKSTESTPREFIX"
default_node_pool_name = "test-name"
node_count = 1
vm_sku = "Standard_D2_v2"
kubernetes_version = "1.21.7"
automatic_channel_upgrade = "stable"
api_server_authorized_ip_ranges = ["16.0.0.0/16"]
identity = {
type = "SystemAssigned"
}
default_node_pool = {
name = "nodetest01"
node_count = 3
vm_size = "Standard_D2_v2"
availability_zones = ["1", "2", "3"]
auto_scaling_enabled = true
enable_host_encryption = false
enable_node_public_ip = true
fips_enabled = true
kubelet_disk_type = "OS"
max_pods = 2
}
}
}
}
As you can see there are bunch of optional arguments for AKS, but I don't want to specify each one of them as 'null' every deployment.
Is there a option to achieve that? Some kind of function 'if key does not exist skip it'
Thank you
EDIT:
What do you think about this "workaround"?
dynamic "identity" {
for_each = can(each.value.identity) == true ? each.value.identity : {}
content {
type = each.value.identity.type
user_assigned_identity_id = can(each.value.identity.user_assigned_identity_id) == true ? each.value.identity.user_assigned_identity_id : null
}
}
Using can it's working, or at least terraform validate doesn't throw any problems and I don't need to specify optional parameters in map, but I don't know if this should be avoided in my case
Generally there are two options I see to handle this problem:
Split the default map to different vaiables, and assign to each variable a default value.
Use the merge() terraform function in order to override specific values in the default map you declared to be used in the for_each.
Related
I am attempting to merge my tfvars values into a locals block, but it appears the merge is not working. For example, I put the location value in the tfvars to null and based on the code, in that case, it should instead choose the location of the Resource Group. I get an error of "The argument location is required, but no definition was found." Makes me feel that the whole merge is not happening at all.
Similarly, the merge of the tags is not working.
Does the merge only work for values that don't exist in the tfvars or can it take existing values, run logic on them, and produce the output? Maybe my whole idea of the merge is wrong if it cannot do the latter.
Any help appreciated.
According to the documentation: "If more than one given map or object defines the same key or attribute, then the one that is later in the argument sequence takes precedence."
##Data Source for Resource Groups
data "azurerm_resource_group" "resource_groups" {
for_each = { for law in var.log_analytics_workspace : law.name => law }
name = each.value.resource_group_name
}
###LOCALS###
locals {
laws = { for law in var.log_analytics_workspace : law.name => merge(
{
location = law.location != null ? law.location : data.azurerm_resource_group.resource_groups[law.name].location
sku = coalesce(law.sku, "PerGB2018")
retention_in_days = coalesce(law.retention_in_days, 30)
daily_quota_gb = coalesce(law.daily_quota_gb, 1)
internet_ingestion_enabled = false
internet_query_enabled = false
cmk_for_query_forced = false
reservation_capacity_in_gb_per_day = law.sku != "CapacityReservation" ? null : law.reservation_capacity_in_gb_per_day
tags = merge(data.azurerm_resource_group.resource_groups[law.name].tags, law.tags)
}, law)
}
}
#Log Analytics Resource
resource "azurerm_log_analytics_workspace" "log_analytics_workspace" {
for_each = local.laws
name = each.value.name
resource_group_name = each.value.resource_group_name
location = each.value.location
sku = each.value.sku
retention_in_days = each.value.retention_in_days
daily_quota_gb = each.value.daily_quota_gb
internet_ingestion_enabled = each.value.internet_ingestion_enabled
internet_query_enabled = each.value.internet_query_enabled
cmk_for_query_forced = each.value.cmk_for_query_forced
reservation_capacity_in_gb_per_day = each.value.reservation_capacity_in_gb_per_day
tags = each.value.tags
}
log_analytics_workspace = [
{
name = "law-eastus-dev-01"
resource_group_name = "rg-eastus-dev-01"
location = "eastus"
sku = "PerGB2018"
retention_in_days = 7
daily_quota_gb = 1
reservation_capacity_in_gb_per_day = 100
tags = {
law = "dev-east"
}
}
]
Changes to Outputs:
+ laws = {
+ law-eastus-dev-01 = {
+ cmk_for_query_forced = false
+ daily_quota_gb = 1
+ internet_ingestion_enabled = false
+ internet_query_enabled = false
+ location = "eastus"
+ name = "law-eastus-dev-01"
+ reservation_capacity_in_gb_per_day = 100
+ resource_group_name = "rg-eastus-dev-01"
+ retention_in_days = 7
+ sku = "PerGB2018"
+ tags = {
+ "law" = "dev-east"
}
}
}
I had my code setup to export the dynamic private ip address when the VM is created. I did this via an outputs value. Since then, I have updated to tf 0.13 and I'm using a for_each in the module but when I reference this value now I get the below error. I'm not sure how I can export the dynamic private address attribute of the NIC now the for_each has been set to be used in the source_address_prefixes. I understand what the error is saying but not sure on correct way of exporting the value to the object map?
Error: Unsupported attribute
on main.tf line 66, in resource "azurerm_network_security_rule" "fico-app-sr-80":
66: source_address_prefixes = module.fico_web_vm.linux_vm_ips[0]
|----------------
| module.fico_web_vm is object with 1 attribute "web-1"
This object does not have an attribute named "linux_vm_ips".
The outputs.tf file:
output "linux_vm_names" {
value = [azurerm_linux_virtual_machine.virtual_machine.*.name]
}
output "linux_vm_ips" {
value = [azurerm_network_interface.network_interface.*.private_ip_address]
}
output "linux_vm_nsg" {
value = azurerm_network_security_group.network_security_group.name
}
Security rule where the error is referencing, located in the main.tf:
resource "azurerm_network_security_rule" "fico-app-sr-80" {
name = "nsr-${var.environment}-${var.directorate}-${var.business_unit}-${var.vm_identifier}${var.instance_number}-http80"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "80"
source_address_prefixes = module.fico_web_vm.linux_vm_ips[0]
destination_address_prefix = "VirtualNetwork"
resource_group_name = azurerm_resource_group.rg_dwp_fico_app.name
network_security_group_name = "module.fico_app_vm.linux_vm_nsg"
}
The NIC which is in the root module:
resource "azurerm_network_interface" "network_interface" {
name = "nic-${var.environment}-${var.directorate}-${var.business_unit}-${var.vm_identifier}-${var.vm_name}"
resource_group_name = var.resource_group
location = var.location
enable_ip_forwarding = "false"
enable_accelerated_networking = "false"
ip_configuration {
name = "ipconfig1"
subnet_id = data.azurerm_subnet.dwp_subnet.id
private_ip_address_allocation = "Dynamic"
primary = "true"
}
The module that builds the web_vm in main.tf:
module "fico_web_vm" {
for_each = var.web_servers
source = "../modules/compute/linux_vm"
source_image_id = var.web_image_id
location = var.location
vm_name = each.key
vm_identifier = "${var.vm_identifier}${var.instance_number}"
vm = each.value
disks = each.value["disks"]
resource_group = azurerm_resource_group.rg_dwp_fico_web.name
directorate = var.directorate
business_unit = var.business_unit
environment = var.environment
network_rg_identifier = var.network_rg_identifier
subnet_name = "sub-dwp-${var.environment}-${var.directorate}-${var.business_unit}-fe01"
diag_storage_account_name = var.diag_storage_account_name
ansible_storage_account_name = var.ansible_storage_account_name
ansible_storage_account_key = var.ansible_storage_account_key
log_analytics_workspace_name = var.log_analytics_workspace_name
backup_policy_name = var.backup_policy_name
}
variables for the app in the main.tf:
# Web VM Variables
variable "web_servers" {
description = "Variable for defining each instance"
type = map(object({
size = string
admin_username = string
public_key = string
disks = list(number)
zone_vm = string
zone_disk = list(string)
}))
}
The tfvars file that the variables.tf translates to and which the for_each loops over:
web_servers ={
web-1 = {
size = "Standard_B2s"
admin_username = xxxx
public_key = xxxx
disks = [32, 32]
zone_vm = "1"
zone_disk = ["1"]
}
}
As I see, there two mistakes in your code.
First:
network_security_group_name = "module.fico_app_vm.linux_vm_nsg"
need to change into:
network_security_group_name = module.fico_app_vm.linux_vm_nsg
The double quotes are usually used to set a string when you use them only, not other resources.
Second:
source_address_prefixes = module.fico_web_vm.linux_vm_ips[0]
need to change into:
source_address_prefixes = module.fico_web_vm.linux_vm_ips
and the output also should be change into:
output "linux_vm_ips" {
value = azurerm_network_interface.network_interface.*.private_ip_address
}
I see you just create one NIC without count and for_each, but when you set the value with azurerm_network_interface.network_interface.*.private_ip_address then it also returns a list. And the source_address_prefixes option expects a list, so you just need to give the output.
Maybe it's better to change the output like this:
output "linux_vm_ip" {
value = azurerm_network_interface.network_interface.private_ip_address
}
And then you can set the source_address_prefixes like this:
source_address_prefixes = [ module.fico_web_vm.linux_vm_ip ]
How do I do a for_each loop for the following?
I want to create a tfe_variable node_count & vm_size.
I need both these tfe_variables in both wksp1 and wksp2
variable "custom_variables" {
type = map(object({
node_count = number
vm_size = string
}))
default = {
wksp1 = {
node_count = 2
vm_size = "Standard_D2_v3"
},
wksp2 = {
node_count = 5
vm_size = "Standard_D2_v5"
}
}
}
resource "tfe_variable" "custom" {
for_each = {
# for each workspace & variable in var.custom_variables create a tfe_variable
}
key = each.value.name
value = each.value.value
category = "terraform"
workspace_id = each.value.workspace_id
}
You're really close! Here are a couple of things to consider:
Option 1: Multiple tfe_variable resources
Create a tfe_variable resource for each variable you want to create
Make sure the key in you custom_variables map is the workspace ID.
variable "custom_variables" {
type = map(object({
node_count = number
vm_size = string
}))
default = {
wksp1_id = {
node_count = 2
vm_size = "Standard_D2_v3"
},
wksp2_id = {
node_count = 5
vm_size = "Standard_D2_v5"
}
}
}
resource "tfe_variable" "node_count" {
for_each = var.custom_variables
key = "node_count"
value = each.value.node_count
category = "terraform"
workspace_id = each.key
}
resource "tfe_variable" "vm_size" {
for_each = var.custom_variables
key = "vm_size"
value = each.value.vm_size
category = "terraform"
workspace_id = each.key
}
The drawback to this option is that you'll need an additional resource for each variable.
Option 2: A list of variable objects
Define a list of the keys, values, and workspace IDs of each variable
Use count to iterate the list
variable "custom_variables" {
type = list(object({
key = string
value = string
workspace_id = string
}))
default = [
{
key = "node_count"
value = "2"
workspace_id = "wksp1_id"
},
{
key = "node_count"
value = "5"
workspace_id = "wksp2_id"
},
{
key = "vm_size"
value = "Standard_D2_v3"
workspace_id = "wksp1_id"
},
{
key = "vm_size"
value = "Standard_D2_v5"
workspace_id = "wksp2_id"
}
]
}
resource "tfe_variable" "custom" {
count = length(var.custom_variables)
key = var.custom_variables[count.index].key
value = var.custom_variables[count.index].value
workspace_id = var.custom_variables[count.index].workspace_id
category = "terraform"
}
There are a couple of drawbacks to this approach as well:
There is a fair amount of duplicated code in the variable definition
The value must always be of the same type
If you're struggling with loop concepts in Terraform, this blog post might help you.
The main requirement to keep in mind for for_each is that we always need to create a map that has one element for each instance of the resource we want to create. In this case, that means you need a map with one element per workspace per variable, because tfe_variable describes a single variable on a single workspace.
Our job then is to write an expression to project the map-of-objects value coming in via the variable to a collection that has a separate element per variable. Here's one way to get that done, using the flatten function in a way similar to an example in its documentation:
locals {
workspace_variables = flatten([
for ws_name, ws in var.custom_variables : [
for var_name, value in ws : {
workspace = ws_name
variable = var_name
value = value
}
]
])
}
The above should produce a local.workspace_variables that looks like this:
[
{ workspace = "wksp1", variable = "node_count", value = 2 },
{ workspace = "wksp1", variable = "vm_size", value = "Standard_D2_v3" },
{ workspace = "wksp2", variable = "node_count", value = 5 },
{ workspace = "wksp2", variable = "vm_size", value = "Standard_D2_v5" },
]
This now meets the requirement of having one element per desired tfe_variable instance, so our only remaining job is to project this into a map to provide unique identifiers for each element and describe how to populate the tfe_variable arguments based on these objects:
resource "tfe_variable" "custom" {
for_each = {
for wsv in local.workspace_variables : "${wsv.workspace}.${wsv.variable}" => wsv
}
key = each.value.variable
value = each.value.value
category = "terraform"
workspace_id = each.value.workspace
}
One thing I didn't contend with above, because it wasn't directly you question, is the value of workspace_id in tfe_variable. If I recall correctly, that argument is expecting a workspace id rather than a workspace name, in which case you might need a slightly more complicated expression for the workspace_id argument. If you already have a tfe_workspace resource using the workspace names as keys then something like this might work, for example:
workspace_id = tfe_workspace.example[each.value.workspace].id
If your workspaces are created in a different way then you may have to do something more complicated here, but that's getting far off the topic of your original question so I won't try to dig into that here. I'm happy to try to help with it in a separate question on this site though, if you like.
I would like to pass a variable that will allow me to specify the list of VPC and subnet settings for an AWS instance. There are fixed VPC and subnet settings that make sense so I just want to allow a user to pick one using a single variable, i.e. use A or B.
For instance, let's say I have two available VPCs, and these are specified in a variables.tf file for a module my_instance:
variable "a_vpc_cidr_block" { default = "105.191.44.0/22" }
variable "a_vpc_id" { default = "id_a"}
variable "a_vpc_name" { default = "vpc_a" }
variable "a_subnet_availability_zone" { default = "us-east-1a" }
variable "a_subnet_cidr_block" { default = "105.191.25.0/25" }
variable "a_subnet_name" { default = "instance_A" }
variable "b_vpc_cidr_block" { default = "105.191.45.0/22" }
variable "b_vpc_id" { default = "id_b"}
variable "b_vpc_name" { default = "vpc_b" }
variable "b_subnet_availability_zone" { default = "us-east-1a" }
variable "b_subnet_cidr_block" { default = "105.191.35.0/25" }
variable "b_subnet_name" { default = "instance_B" }
The my_instance module will take a single input variable that an environment will specify, with a value of either 'A' or 'B' (is there a way to limit options for a variable to a list of values such as options=['A', 'B']?), and will be called like so in the terraform.tf for a Terraform configuration with a single instance:
module "my_instance" {
source = "../../modules/my_instance"
option = "A"
}
I want to now implement some logic within the module's main file (modules/my_instance/my_instance.tf) where it decides on which of the two collections of VPC and subnet settings it should use from the ones in modules/my_instance/variables.tf. I want to something like this (pseudocode):
if var.option == 'A'
vpc_cidr_block = var.a_vpc_cidr_block
vpc_id = var.a_vpc_id
vpc_name = var.a_vpc_name
subnet_availability_zone = var.a_subnet_availability_zone
subnet_cidr_block = var.a_subnet_cidr_block
subnet_name = var.a_subnet_name
else if var.option == 'B'
vpc_cidr_block = var.b_vpc_cidr_block
vpc_id = var.b_vpc_id
vpc_name = var.b_vpc_name
subnet_availability_zone = var.b_subnet_availability_zone
subnet_cidr_block = var.b_subnet_cidr_block
subnet_name = var.b_subnet_name
else
raise an error
# get a data resource identified by the VPC variables
data "aws_vpc" "instance_vpc" {
cidr_block = var.vpc_cidr_block
tags = {
Name = var.vpc_name
}
}
# get a data resource identified by the VPC variables
data "aws_subnet" "instance_subnet" {
vpc_id = var.vpc_id
cidr_block = var.subnet_cidr_block
availability_zone = var.subnet_availability_zone
tags = {
Name = var.subnet_name
}
}
# create an AWS key pair resource
resource "aws_key_pair" "instance_aws_key_pair" {
key_name = "component_key_${terraform.workspace}"
public_key = file("~/.ssh/terraform.pub")
}
# create the AWS EC2 instance
resource "aws_instance" "my_aws_instance" {
key_name = aws_key_pair.instance_aws_key_pair.key_name
ami = "ami-b12345"
instance_type = "t2.micro"
subnet_id = data.aws_subnet.instance_subnet.id
connection {
type = "ssh"
user = "terraform"
private_key = file("~/.ssh/terraform")
host = self.public_ip
}
tags = {
"Name" : "my_instance_name"
"Terraform" : "true"
}
}
Is this a matter of somehow using a count, something like this:
count = var.option == 'A'? 1 : 0
Is there a way to do this, or is there a better approach? I am very new to Terraform so I may be missing something obvious.
You have a couple of questions here.
Firstly, you should be able to use the newer, experimental custom validation rules to assert that a value is in a specific list of values.
Secondly, for determining which set of variables to use, I'd recommend going with a good old map in a local value.
For example,
locals {
vpc_info = {
"A" = {
vpc_cidr_block = var.a_vpc_cidr_block
vpc_id = var.a_vpc_id
vpc_name = var.a_vpc_name
subnet_availability_zone = var.a_subnet_availability_zone
subnet_cidr_block = var.a_subnet_cidr_block
subnet_name = var.a_subnet_name
}
"B" = {
vpc_cidr_block = var.b_vpc_cidr_block
vpc_id = var.b_vpc_id
vpc_name = var.b_vpc_name
subnet_availability_zone = var.b_subnet_availability_zone
subnet_cidr_block = var.b_subnet_cidr_block
subnet_name = var.b_subnet_name
}
}
}
Then you should be able to reference a specific field, within the chose option like the following
local.vpc_info[var.option].vpc_name
Let me know if this hits all your questions.
Using below terraform code. main.tf file is as below
data "azurerm_resource_group" "tf-rg-external" {
name = var.rg_name
}
data "azurerm_virtual_network" "tf-vn" {
name = var.vnet_name
resource_group_name = var.rg_name
}
# Reference existing subnet
data "azurerm_subnet" "tf-sn" {
name = var.subnet_name
virtual_network_name = data.azurerm_virtual_network.tf-vn.name
resource_group_name = var.rg_name
}
module "sql_vm" {
source = "Azure/compute/azurerm"
location = data.azurerm_resource_group.tf-rg-external.location
vnet_subnet_id = data.azurerm_virtual_network.tf-vn.subnets
resource_group_name = data.azurerm_resource_group.tf-rg-external.name
admin_password = var.admin_password
admin_username = var.admin_username
boot_diagnostics = var.boot_diagnostics
boot_diagnostics_sa_type = var.boot_diagnostics_sa_type
# data_disk = var.data_disk
# data_disk_size_gb = var.data_disk_size_gb
# data_sa_type = var.data_sa_type
delete_os_disk_on_termination = var.delete_os_disk_on_termination
enable_accelerated_networking = var.enable_accelerated_networking
# flag is_windows_image is required only when you use a custom image to spin up a VM
# is_windows_image
# flag vm_os_id is required only when you are using custom image
# you need to provide id of your custom image
# vm_os_id
nb_instances = var.nb_instances
#nb_public_ip = var.nb_public_ip
public_ip_address_allocation = var.public_ip_address_allocation
storage_account_type = var.storage_account_type
vm_hostname = var.vm_hostname
vm_os_offer = var.vm_os_offer
vm_os_publisher = var.vm_os_publisher
# vm_os_simple is to be used is you do not wish to specify offer, publisher and sku
# vm_os_simple = UbuntuServer, WindowsServer, RHEL, openSUSE-Leap, CentOS, Debian, CoreOS and SLES
vm_os_sku = var.vm_os_sku
vm_os_version = var.vm_os_version
vm_size = var.vm_size
}
my variables.tf file is as below:
variable "public_ip_address_allocation" {
type = string
default = "Dynamic"
}
variable "storage_account_type" {
type = string
default = "Standard_LRS"
}
variable "vm_hostname" {
type = string
default = "testvm"
}
variable "vm_os_offer" {
type = string
default = "WindowsServer"
}
variable "vm_os_publisher" {
type = string
default = "MicrosoftWindowsServer"
}
variable "vm_os_sku" {
type = string
default = "2012-R2-Datacenter"
}
variable "vm_os_version" {
type = string
default = "latest"
}
variable "vm_size" {
type = string
default = "Standard_B1ms"
}
variable "admin_password" {
type = string
default = "Symphony12#$%"
}
variable "admin_username" {
type = string
default = "devopsadmin"
}
variable "boot_diagnostics" {
type = bool
default = "true"
}
variable "boot_diagnostics_sa_type" {
type = string
default = "Standard_LRS"
}
# variable "data_disk" {
# type = bool
# default ="false"
# }
# variable "data_disk_size_gb" {
# type = number
# default = 0
# }
# variable "data_sa_type" {
# type = string
# }
variable "delete_os_disk_on_termination" {
type = bool
default = true
}
variable "enable_accelerated_networking" {
type = bool
default = false
}
variable "nb_instances" {
type = number
default = 2
}
# variable "nb_public_ip" {
# type = bool
# default = "1"
# }
# variable "location" {
# type = string
# }
# variable "vnet_subnet_id" {
# type = string
# }
variable "rg_name" {
type = string
default = "nxt-grp-prd-manage-rgp-au-se"
}
variable "subnet_name" {
type = string
default = "subnet_1"
}
variable "vnet_name" {
type = string
default = "virtual_network_1"
}
When I run terraform plan, it reports Error below:
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
data.azurerm_virtual_network.tf-vn: Refreshing state...
data.azurerm_resource_group.tf-rg-external: Refreshing state...
data.azurerm_subnet.tf-sn: Refreshing state...
------------------------------------------------------------------------
Warning: "public_ip_address_allocation": [DEPRECATED] this property has been deprecated in favor of `allocation_method` to better match the api
on .terraform/modules/sql_vm/Azure-terraform-azurerm-compute-fb014dd/main.tf line 248, in resource "azurerm_public_ip" "vm":
248: resource "azurerm_public_ip" "vm" {
Warning: "public_ip_address_allocation": [DEPRECATED] this property has been deprecated in favor of `allocation_method` to better match the api
on .terraform/modules/sql_vm/Azure-terraform-azurerm-compute-fb014dd/main.tf line 248, in resource "azurerm_public_ip" "vm":
248: resource "azurerm_public_ip" "vm" {
Error: domain_name_label must contain only lowercase alphanumeric characters, numbers and hyphens. It must start with a letter and end only with a number or letter
on .terraform/modules/sql_vm/Azure-terraform-azurerm-compute-fb014dd/main.tf line 248, in resource "azurerm_public_ip" "vm":
248: resource "azurerm_public_ip" "vm" {
I would like to have an ability to override what it is in default module, something like below
domain_name_label = "false"
Unfortunately, when I do so in my main file, I get error like below:
Error: Unsupported argument
on main.tf line 48, in module "sql_vm":
48: domain_name_label = "false"
An argument named "domain_name_label" is not expected here.
So, is there not anyway to override, as this is an out-of-box configuration, I would be much happier to see it working.
I would like to have an ability to override what it is in default module, something like below
For that, the tf module should declare that as a variable. Your module Azure/compute/azurerm doesn't have a variable defined for domain_name_label.
Taking a step back and looking at your original issue: Look at the following block of code from this module:
https://github.com/Azure/terraform-azurerm-compute/blob/master/main.tf#L248
resource "azurerm_public_ip" "vm" {
count = "${var.nb_public_ip}"
name = "${var.vm_hostname}-${count.index}-publicIP"
location = "${var.location}"
resource_group_name = "${azurerm_resource_group.vm.name}"
public_ip_address_allocation = "${var.public_ip_address_allocation}"
domain_name_label = "${element(var.public_ip_dns, count.index)}"
tags = "${var.tags}"
}
Please set a value for public_ip_dns in your call to this module. That is expected to fix your issue.
Example:
(Note the line public_ip_dns = ["my-new-good-vm"]):
module "sql_vm" {
source = "Azure/compute/azurerm"
location = data.azurerm_resource_group.tf-rg-external.location
vnet_subnet_id = data.azurerm_virtual_network.tf-vn.subnets
resource_group_name = data.azurerm_resource_group.tf-rg-external.name
admin_password = var.admin_password
admin_username = var.admin_username
public_ip_dns = ["my-new-good-vm"]
boot_diagnostics = var.boot_diagnostics
boot_diagnostics_sa_type = var.boot_diagnostics_sa_type
# data_disk = var.data_disk
# data_disk_size_gb = var.data_disk_size_gb
# data_sa_type = var.data_sa_type
delete_os_disk_on_termination = var.delete_os_disk_on_termination
enable_accelerated_networking = var.enable_accelerated_networking
# flag is_windows_image is required only when you use a custom image to spin up a VM
# is_windows_image
# flag vm_os_id is required only when you are using custom image
# you need to provide id of your custom image
# vm_os_id
nb_instances = var.nb_instances
#nb_public_ip = var.nb_public_ip
public_ip_address_allocation = var.public_ip_address_allocation
storage_account_type = var.storage_account_type
vm_hostname = var.vm_hostname
vm_os_offer = var.vm_os_offer
vm_os_publisher = var.vm_os_publisher
# vm_os_simple is to be used is you do not wish to specify offer, publisher and sku
# vm_os_simple = UbuntuServer, WindowsServer, RHEL, openSUSE-Leap, CentOS, Debian, CoreOS and SLES
vm_os_sku = var.vm_os_sku
vm_os_version = var.vm_os_version
vm_size = var.vm_size
}