I am trying to develop a module that if the variable DeployPrivateEndpoint == true will deploy the private endpoint and if false it will not be deployed.
I currently have the following code:
resource "azurerm_container_registry" "ACR" {
count = length(var.ACR_Name)
name = var.ACR_Name[count.index]
resource_group_name = var.resourcegroup_name
location = var.location
sku = var.ACR_Sku
admin_enabled = var.ACR_AdminEnabled
georeplication_locations = var.ACR_GeoRepLocation
}
resource "azurerm_private_dns_zone" "PDZ" {
count = var.DeployPrivateEndpoint == true ? 1 : 0
name = "privatelink.azurecr.io"
resource_group_name = var.resourcegroup_name
}
resource "azurerm_private_endpoint" "PEP" {
count = var.DeployPrivateEndpoint == true ? length(var.PEP_Name) : 0
name = var.PEP_Name[count.index]
location = var.location
resource_group_name = var.resourcegroup_name
subnet_id = element(concat(var.subnet_id[*], [""]), count.index)
private_dns_zone_group {
name = "private-dns-zone-group"
private_dns_zone_ids = element(concat(azurerm_private_dns_zone.PDZ.*.id, [""]), count.index)
}
private_service_connection {
name = var.PEP_Name[count.index]
private_connection_resource_id = element(concat(azurerm_container_registry.ACR.*.id, [""]), count.index)
subresource_names = [ "registry" ]
is_manual_connection = false
}
}
The code only crashes on the part at private_dns_zone_group at this point if the value of the variable is false. Terraform expects that a private_dns_zone_ids will be given, but it is not created because the variable is set to false. I get the following error:
Error: Invalid index
on .terraform\modules\containerRegistry\outputs.tf line 10, in
output "ACR_PDZID": 10: value =
azurerm_private_dns_zone.PDZ.0.id
|----------------
| azurerm_private_dns_zone.PDZ is empty tuple
any help is appreciated!
Edit:
The module is called trough a main that looks like this:
terraform {
required_version = ">= 0.13"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.47.0"
}
}
}
provider "azurerm" {
subscription_id = "****"
client_id = "****"
client_secret = "*****"
tenant_id = "*****"
features {}
}
module "ResourceGroups" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/ResourceGroup"
location = var.location
RG_Name = var.RG_Name
}
module "VirtualNetwork" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/VirtualNetwork"
resourcegroup_name = module.ResourceGroups.RG_Name[0]
location = var.location
VNET_Name = var.vnet_name
VNET_Cidr = var.vnet_cidr
}
module "Subnet" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/Subnet"
resourcegroup_name = module.ResourceGroups.RG_Name[0]
location = var.location
VNET_name = module.VirtualNetwork.VNET_Name[0]
SNET_cidr = var.subnet_cidr
SNET_name = var.subnet_names
}
module "containerRegistry" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/ContainerRegistry"
resourcegroup_name = module.ResourceGroups.RG_Name[0]
location = var.location
subnet_id = module.Subnet.SNET_ID
PEP_Name = ["****", "*****"]
ACR_Name = ["****", "*****" ]
ACR_Sku = "Premium"
DeployPrivateEndpoint = false
}
The output.tf file form the module looks lik this:
output "ACR_ID" {
value = azurerm_container_registry.ACR.*.id
}
output "ACR_LoginServer" {
value = azurerm_container_registry.ACR.*.login_server
}
output "ACR_PDZID" {
value = azurerm_private_dns_zone.PDZ.0.id
}
output "ACR_PEPID" {
value = azurerm_private_endpoint.PEP.*.id
}
You should tune a bit your ACR_PDZID output, change 0 to *
output "ACR_PDZID" should look like this:
output "ACR_PDZID" {
value = azurerm_private_dns_zone.PDZ.*.id
}
Related
I'm trying to deploy a simple infrastructure in Azure through Terraform, the infrastructure is made of an Application Gateway (with Web Application Firewall, so the WAF_v2 version) with two virtual machines in the backend.
At the beginning I have implemented the Application Gateway (Standard_v2) without the WAF, and worked properly, but when I have implemented the WAF, I got the following error after lunching "terraform init" command (see attached screenshot also):
Error: Failed to query available provider packages
│
│ Could not retrieve the list of available versions for provider hashicorp/example: provider registry registry.terraform.io does not have a provider named
│ registry.terraform.io/hashicorp/example
│
│ All modules should specify their required_providers so that external consumers will get the correct providers when using a module. To see which modules are currently depending
│ on hashicorp/example, run the following command:
│ terraform providers
So I run the command "terraform providers" as suggested by Terraform and got this:
Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/azurerm] >= 2.97.0
├── provider[registry.terraform.io/hashicorp/example]
└── provider[registry.terraform.io/hashicorp/random]
In the following you can see the Terraform code of my infrastructure:
terraform {
required_version = ">=0.12"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=2.97.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg1" {
name = "myResourceGroupAG"
location = "francecentral"
}
resource "example_wafpolicy" "exampleWAF" {
name = "example_wafpolicy_name"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
custom_rules {
name = "Rule1"
priority = 1
rule_type = "MatchRule"
match_conditions {
match_variables {
variable_name = "RemoteAddr"
}
operator = "IPMatch"
negation_condition = false
match_values = ["XX.XX.XX.XX"]
}
action = "Block"
}
policy_settings {
enabled = true
mode = "Prevention"
request_body_check = true
file_upload_limit_in_mb = 100
max_request_body_size_in_kb = 128
}
managed_rules {
managed_rule_set {
type = "OWASP"
version = "3.2"
}
}
}
resource "azurerm_virtual_network" "vnet1" {
name = "myVNet"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
address_space = ["10.21.0.0/16"]
}
resource "azurerm_subnet" "frontend" {
name = "myAGSubnet"
resource_group_name = azurerm_resource_group.rg1.name
virtual_network_name = azurerm_virtual_network.vnet1.name
address_prefixes = ["10.21.0.0/24"]
}
resource "azurerm_subnet" "backend" {
name = "myBackendSubnet"
resource_group_name = azurerm_resource_group.rg1.name
virtual_network_name = azurerm_virtual_network.vnet1.name
address_prefixes = ["10.21.1.0/24"]
}
resource "azurerm_public_ip" "pip1" {
name = "myAGPublicIPAddress"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
allocation_method = "Static"
sku = "Standard"
}
resource "azurerm_application_gateway" "network" {
name = "myAppGateway"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
sku {
name = "WAF_v2"
tier = "WAF_v2"
capacity = 2
}
gateway_ip_configuration {
name = "my-gateway-ip-configuration"
subnet_id = azurerm_subnet.frontend.id
}
frontend_port {
name = var.frontend_port_name
port = 80
}
frontend_ip_configuration {
name = var.frontend_ip_configuration_name
public_ip_address_id = azurerm_public_ip.pip1.id
}
backend_address_pool {
name = var.backend_address_pool_name
}
backend_http_settings {
name = var.http_setting_name
cookie_based_affinity = "Disabled"
port = 80
protocol = "Http"
request_timeout = 20
}
http_listener {
name = var.listener_name
frontend_ip_configuration_name = var.frontend_ip_configuration_name
frontend_port_name = var.frontend_port_name
protocol = "Http"
firewall_policy_id = example_wafpolicy.exampleWAF.id
}
request_routing_rule {
name = var.request_routing_rule_name
rule_type = "Basic"
priority = 25
http_listener_name = var.listener_name
backend_address_pool_name = var.backend_address_pool_name
backend_http_settings_name = var.http_setting_name
}
firewall_policy_id {
id = example_wafpolicy.exampleWAF.id
}
waf_configuration {
content{
enabled = lookup(waf_configuration.value,"enabled",true)
file_upload_limit_mb = lookup(waf_configuration.value,"file_upload_limit_mb",30)
firewall_mode = lookup(waf_configuration.value,"firewall_mode","Prevention")
max_request_body_size_kb = lookup(waf_configuration.value,"max_request_body_size_kb",128)
request_body_check = lookup(waf_configuration.value,"request_body_check",true)
rule_set_type = lookup(waf_configuration.value,"rule_set_type","OWASP")
rule_set_version = lookup(waf_configuration.value,"rule_set_version", "3.1")
}
}
}
resource "azurerm_network_interface" "nic" {
count = 2
name = "nic-${count.index+1}"
location = azurerm_resource_group.rg1.location
resource_group_name = azurerm_resource_group.rg1.name
ip_configuration {
name = "nic-ipconfig-${count.index+1}"
subnet_id = azurerm_subnet.backend.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_network_interface_application_gateway_backend_address_pool_association" "nic-assoc01" {
count = 2
network_interface_id = azurerm_network_interface.nic[count.index].id
ip_configuration_name = "nic-ipconfig-${count.index+1}"
backend_address_pool_id = tolist(azurerm_application_gateway.network.backend_address_pool).0.id
}
resource "random_password" "password" {
length = 16
special = true
lower = true
upper = true
numeric = true
}
resource "azurerm_windows_virtual_machine" "vm" {
count = 2
name = "myVM${count.index+1}"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
size = "Standard_DS1_v2"
admin_username = "azureadmin"
admin_password = random_password.password.result
network_interface_ids = [
azurerm_network_interface.nic[count.index].id,
]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2019-Datacenter"
version = "latest"
}
}
resource "azurerm_virtual_machine_extension" "vm-extensions" {
count = 2
name = "vm${count.index+1}-ext"
virtual_machine_id = azurerm_windows_virtual_machine.vm[count.index].id
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.10"
settings = <<SETTINGS
{
"commandToExecute": "powershell Add-WindowsFeature Web-Server; powershell Add-Content -Path \"C:\\inetpub\\wwwroot\\Default.htm\" -Value $($env:computername)"
}
SETTINGS
}
In the following the script with the variables:
variable "backend_address_pool_name" {
default = "myBackendPool"
}
variable "frontend_port_name" {
default = "myFrontendPort"
}
variable "frontend_ip_configuration_name" {
default = "myAGIPConfig"
}
variable "http_setting_name" {
default = "myHTTPsetting"
}
variable "listener_name" {
default = "myListener"
}
variable "request_routing_rule_name" {
default = "myRoutingRule"
}
variable "redirect_configuration_name" {
default = "myRedirectConfig"
}
variable "example_wafpolicy_name" {
default = "myFirewallPolicy"
}
At the beginning of the code you can see match_values = ["XX.XX.XX.XX"], the IP address is set in this manner just for opening this question in Stackoverflow, normally in my code there is a normal IP address.
I would really appreciate your help to fix this error and in general to deploy an Application Gateway with WAF and two virtual machines in the backend in Azure through Terraform.
I have tried to search something online but it seems that this topic has never been opened by someone.
Issue was caused because of the naming convention which used in terraform code base "example_wafpolicy" and terraform provider.
Solution:
Need to replace with below mention resource tag
resource "azurerm_web_application_firewall_policy" "example" {
Replicated the same code base in local, please find below code snippet.
Main if file as follows:
resource "azurerm_resource_group" "rg1" {
name = "************"
location = "West Europe"
}
resource "azurerm_web_application_firewall_policy" "exampleWAF" {
name = "example_wafpolicy_name"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
custom_rules {
name = "Rule1"
priority = 1
rule_type = "MatchRule"
match_conditions {
match_variables {
variable_name = "RemoteAddr"
}
operator = "IPMatch"
negation_condition = false
match_values = ["192.168.1.0/24", "10.0.0.0/24"]
}
action = "Block"
}
policy_settings {
enabled = true
mode = "Prevention"
request_body_check = true
file_upload_limit_in_mb = 100
max_request_body_size_in_kb = 128
}
managed_rules {
managed_rule_set {
type = "OWASP"
version = "3.2"
}
}
}
resource "azurerm_virtual_network" "vnet1" {
name = "myVNet"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
address_space = ["10.21.0.0/16"]
}
resource "azurerm_subnet" "frontend" {
name = "myAGSubnet"
resource_group_name = azurerm_resource_group.rg1.name
virtual_network_name = azurerm_virtual_network.vnet1.name
address_prefixes = ["10.21.0.0/24"]
}
resource "azurerm_subnet" "backend" {
name = "myBackendSubnet"
resource_group_name = azurerm_resource_group.rg1.name
virtual_network_name = azurerm_virtual_network.vnet1.name
address_prefixes = ["10.21.1.0/24"]
}
resource "azurerm_public_ip" "pip1" {
name = "myAGPublicIPAddress"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
allocation_method = "Dynamic"
sku = "Basic"
}
locals {
backend_address_pool_name = "${azurerm_virtual_network.vnet1.name}-beap"
frontend_port_name = "${azurerm_virtual_network.vnet1.name}-feport"
frontend_ip_configuration_name = "${azurerm_virtual_network.vnet1.name}-feip"
http_setting_name = "${azurerm_virtual_network.vnet1.name}-be-htst"
listener_name = "${azurerm_virtual_network.vnet1.name}-httplstn"
request_routing_rule_name = "${azurerm_virtual_network.vnet1.name}-rqrt"
redirect_configuration_name = "${azurerm_virtual_network.vnet1.name}-rdrcfg"
}
resource "azurerm_application_gateway" "network" {
name = "example-appgateway"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
sku {
name = "Standard_Small"
tier = "Standard"
capacity = 2
}
gateway_ip_configuration {
name = "my-gateway-ip-configuration"
subnet_id = azurerm_subnet.frontend.id
}
frontend_port {
name = local.frontend_port_name
port = 80
}
frontend_ip_configuration {
name = local.frontend_ip_configuration_name
public_ip_address_id = azurerm_public_ip.pip1.id
}
backend_address_pool {
name = local.backend_address_pool_name
}
backend_http_settings {
name = local.http_setting_name
cookie_based_affinity = "Disabled"
path = "/path1/"
port = 80
protocol = "Http"
request_timeout = 60
}
http_listener {
name = local.listener_name
frontend_ip_configuration_name = local.frontend_ip_configuration_name
frontend_port_name = local.frontend_port_name
protocol = "Http"
}
request_routing_rule {
name = local.request_routing_rule_name
rule_type = "Basic"
http_listener_name = local.listener_name
backend_address_pool_name = local.backend_address_pool_name
backend_http_settings_name = local.http_setting_name
}
}
resource "azurerm_network_interface" "nic" {
count = 2
name = "nic-${count.index+1}"
location = azurerm_resource_group.rg1.location
resource_group_name = azurerm_resource_group.rg1.name
ip_configuration {
name = "nic-ipconfig-${count.index+1}"
subnet_id = azurerm_subnet.backend.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_network_interface_application_gateway_backend_address_pool_association" "nic-assoc01" {
count = 2
network_interface_id = azurerm_network_interface.nic[count.index].id
ip_configuration_name = "nic-ipconfig-${count.index+1}"
backend_address_pool_id = tolist(azurerm_application_gateway.network.backend_address_pool).0.id
}
resource "random_password" "password" {
length = 16
special = true
lower = true
upper = true
numeric = true
}
resource "azurerm_windows_virtual_machine" "vm" {
count = 2
name = "myVM${count.index+1}"
resource_group_name = azurerm_resource_group.rg1.name
location = azurerm_resource_group.rg1.location
size = "Standard_DS1_v2"
admin_username = "azureadmin"
admin_password = random_password.password.result
network_interface_ids = [
azurerm_network_interface.nic[count.index].id,
]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2019-Datacenter"
version = "latest"
}
}
resource "azurerm_virtual_machine_extension" "vm-extensions" {
count = 2
name = "vm${count.index+1}-ext"
virtual_machine_id = azurerm_windows_virtual_machine.vm[count.index].id
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.10"
settings = <<SETTINGS
{
"commandToExecute": "powershell Add-WindowsFeature Web-Server; powershell Add-Content -Path \"C:\\inetpub\\wwwroot\\Default.htm\" -Value $($env:computername)"
}
SETTINGS
}
provider tf file as follows:
terraform {
required_version = "~>1.3.3"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=3.0.0"
}
}
}
provider "azurerm" {
features {}
skip_provider_registration = true
}
upon running terraform plan and apply
terraform plan
terraform apply -auto-approve
Plan as follows:
Apply as follows
Azure Portal verification:
Does anyone could give a hint on how should i do. i want to deploy 4 Az linux VM but 2 in each different resource group.
eg: rg-one will have 2 linux and rg-two will the other two
the way i did it is too create two block with azure_linux_virtual_machine, it is working but i was wondering if it could not be simplify.
thank in advance for the head up
here a snipped what i have done.
# Fetch exisiting resource_group
data "azurerm_resource_group" "rg-dock001" {
name = var.resource_group01
}
# Fetch vm network
data "azurerm_virtual_network" "vm_network" {
name = var.vm_network
resource_group_name = var.rg_name_network
}
output "azurerm_virtual_network" {
value = data.azurerm_virtual_network.vm_network.id
}
# Fetch vm subnet
data "azurerm_subnet" "vm_subnet" {
name = var.vm_subnet
resource_group_name = var.rg_name_network
virtual_network_name = var.vm_network
}
output "subnet_id" {
value = data.azurerm_subnet.vm_subnet.id
}
resource "azurerm_network_interface" "ens124-01" {
name = var.vm_nic01[count.index]
count = length(var.vm_nic01)
location = var.rg_location
resource_group_name = var.resource_group01
ip_configuration {
name = "internal"
subnet_id = data.azurerm_subnet.vm_subnet.id
private_ip_address_allocation = "Static"
private_ip_address = "10.241.25.${count.index + 10}"
}
tags = var.vm_tags
}
output "private_ip01" {
value = length(azurerm_network_interface.ens124-01.*.private_ip_address)
}
# Fetch existing image
data "azurerm_image" "custom_docker_image" {
name_regex = var.packer_image
sort_descending = true
resource_group_name = var.resource_group_image
}
output "image_id" {
value = data.azurerm_image.custom_docker_image.id
}
# create and display an SSH key
resource "tls_private_key" "ssh" {
algorithm = "RSA"
rsa_bits = 4096
}
output "tls_private_key" {
value = tls_private_key.ssh.private_key_pem
sensitive = true
}
resource "azurerm_linux_virtual_machine" "main01" {
name = var.vm_name01[count.index]
count = length(var.vm_name01)
resource_group_name = var.resource_group01
location = var.rg_location
size = "standard_ds3_v2"
admin_username = var.username
admin_password = var.password
disable_password_authentication = true
network_interface_ids = ["${element(azurerm_network_interface.ens124-01.*.id, count.index)}"]
source_image_id = data.azurerm_image.custom_docker_image.id
computer_name = var.vm_name01[count.index]
admin_ssh_key {
username = var.ssh_username
public_key = tls_private_key.ssh.public_key_openssh
}
os_disk {
name = "disk-int-dock-0${count.index + 1}"
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
tags = var.vm_tags
}
data "azurerm_resource_group" "rg-dock002" {
name = var.resource_group02
}
resource "azurerm_network_interface" "ens124-02" {
name = var.vm_nic02[count.index]
count = length(var.vm_nic02)
location = var.rg_location
resource_group_name = var.resource_group02
ip_configuration {
name = "internal"
subnet_id = data.azurerm_subnet.vm_subnet.id
private_ip_address_allocation = "Static"
private_ip_address = "10.241.25.${count.index + 20}"
}
tags = var.vm_tags
}
output "private_ip02" {
value = length(azurerm_network_interface.ens124-02.*.private_ip_address)
}
resource "azurerm_linux_virtual_machine" "main02" {
name = var.vm_name02[count.index]
count = length(var.vm_name02)
resource_group_name = var.resource_group02
location = var.rg_location
size = "standard_ds3_v2"
admin_username = var.username
admin_password = var.password
disable_password_authentication = true
network_interface_ids = ["${element(azurerm_network_interface.ens124-02.*.id, count.index)}"]
source_image_id = data.azurerm_image.custom_docker_image.id
computer_name = var.vm_name02[count.index]
admin_ssh_key {
username = var.ssh_username
public_key = tls_private_key.ssh.public_key_openssh
}
os_disk {
name = "disk-int-dock-0${count.index + 1}"
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
tags = var.vm_tags
}````
As per your requirement you can use something like below :
provider "azurerm" {
features {}
}
variable "VM" {
default= {
vm1={
rg_name= "ajaytest",
vnet_name="ajay-vnet",
subnet_name="default",
nic_name = "ansumanVM-nic",
private_ip="10.0.0.10",
vm_name="ansumanVM"
},
vm2={
rg_name= "kartiktest",
vnet_name="kartik-vnet",
subnet_name="default",
nic_name="terraformVM-nic",
private_ip="10.0.0.20",
vm_name="terraformVM"
}
}
}
variable "username" {
default="ansuman"
}
variable "password" {
default="Password#1234!"
}
data "azurerm_shared_image_version" "example" {
name = "0.0.1"
image_name = "UbuntuwithNginxinstalled"
gallery_name = "ansumantestgallery"
resource_group_name = "ansumantest"
}
data "azurerm_resource_group" "rg-dock" {
for_each = var.VM
name = each.value["rg_name"]
}
# Fetch vm network
data "azurerm_virtual_network" "vm_network" {
for_each = var.VM
name = each.value["vnet_name"]
resource_group_name = each.value["rg_name"]
}
# Fetch vm subnet
data "azurerm_subnet" "vm_subnet" {
for_each = var.VM
name = each.value["subnet_name"]
resource_group_name = each.value["rg_name"]
virtual_network_name = each.value["vnet_name"]
}
resource "azurerm_network_interface" "ens124" {
for_each = var.VM
name = each.value["nic_name"]
location = data.azurerm_resource_group.rg-dock[each.key].location
resource_group_name = data.azurerm_resource_group.rg-dock[each.key].name
ip_configuration {
name = "internal"
subnet_id = data.azurerm_subnet.vm_subnet[each.key].id
private_ip_address_allocation = "Static"
private_ip_address = each.value["private_ip"]
}
}
# create and display an SSH key
resource "tls_private_key" "ssh" {
algorithm = "RSA"
rsa_bits = 4096
}
output "tls_private_key" {
value = tls_private_key.ssh.private_key_pem
sensitive = true
}
resource "azurerm_linux_virtual_machine" "main" {
for_each = var.VM
name = each.value["vm_name"]
resource_group_name = data.azurerm_resource_group.rg-dock[each.key].name
location = data.azurerm_resource_group.rg-dock[each.key].location
size = "standard_ds3_v2"
admin_username = var.username
admin_password = var.password
disable_password_authentication = true
network_interface_ids = [azurerm_network_interface.ens124[format("%s", each.key)].id]
source_image_id = data.azurerm_shared_image_version.example.id
computer_name = each.key
admin_ssh_key {
username = var.username
public_key = tls_private_key.ssh.public_key_openssh
}
os_disk {
name = "disk-int-dock-${each.key}"
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
}
Note: The admin username and the ssh username must be the same due the below limitation :
And the location of the VM ,Vnet ,Image and other resources must be the same.
Output:
I need a bit of help as I have a terraform script and I want to add multiple VM and change the name of the network card like node_name-NIC and do the same thing even for the other resources but is failing and i cant fine the proper way to do it.
below is the terraform script
terraform {
required_providers {
azurerm = {
// source = "hashicorp/azurerm"
version = "=1.44"
}
}
}
locals {
rsname = "testing-new-terraform-modules"
node_name = ["server1","server2"]
clustersize = 2
node_size = "Standard_B4ms"
av_set_name = "Windows-AV-Set"
vnet_name = "VNET_1"
vnet_rg = "RG_VNET_D"
gw_subnet = "SUB_GW_INT"
vm_subnet = "SUB_WIN"
image_rg = "RG__TEMPLATE"
common_tags = {
lbuildingblock = "GENERAL"
customer = "IND"
}
}
module "resource_group" {
source = "../modules/resources/azure/data-resource-group"
rsname = local.rsname
}
data "azurerm_virtual_network" "virtual_network" {
name = local.vnet_name
resource_group_name = local.vnet_rg
}
# GatewayZone subnet, for the Load Balancer frontend IP address
module "gw_subnet" {
source = "../modules/resources/azure/data-subnet"
subnet-name = local.gw_subnet
vnet-name = data.azurerm_virtual_network.virtual_network.name
rs-name = data.azurerm_virtual_network.virtual_network.resource_group_name
}
module "windows_subnet" {
source = "../modules/resources/azure/data-subnet"
// We will use the SUB_LHIND_P_APP subnet, no need to create a new subnet just for two servers
subnet-name = local.vm_subnet
rs-name = local.vnet_rg
vnet-name = local.vnet_name
}
//data "azurerm_network_security_group" "app_nsg" {
//
// name = "SUB_LHIND_D_APP_NSG"
// resource_group_name = data.azurerm_virtual_network.virtual_network.resource_group_name
//}
module "nic" {
source = "../modules/resources/azure/network-interface"
location = module.resource_group.rs_group_location
name = "${local.node_name[0]}-NIC"
nic_count = local.clustersize
resource_group = module.resource_group.rs_group_name
subnet_id = module.windows_subnet.subnet_id
tags = local.common_tags
}
module "av_set" {
source = "../modules/resources/azure/availability-set"
av_name = local.av_set_name
resource_group = module.resource_group.rs_group_name
location = module.resource_group.rs_group_location
}
module "template_image" {
source = "../modules/resources/azure/data-templates"
template_name = "WindowsServer2019"
resource_group = local.image_rg
}
module "windows" {
source = "../modules/resources/azure/windows-server"
location = module.resource_group.rs_group_location
network_interface_ids = module.nic.nic_id
node_count = local.clustersize
node_name = local.node_name
node_size = local.node_size
av_set_id = module.av_set.availability_set_id
resource_group = module.resource_group.rs_group_name
template_id = module.template_image.template_id
username = var.username
password = var.password
domain_user = var.domain_user
domain_pass = var.domain_pass
}
is failing with the below error
Error: Invalid index
on ../modules/resources/azure/network-interface/main.tf line 10, in resource "azurerm_network_interface" "nic":
10: name = var.name[count.index]
|----------------
| count.index is 0
| var.name is "SW-AZLHIND-580-NIC"
This value does not have any indices.
and the resource Network-Interface is like below
resource "azurerm_network_interface" "nic" {
count = var.nic_count
location = var.location
name = var.name[count.index]
resource_group_name = var.resource_group
tags = var.tags
// network_security_group_id = var.network_security_group_id
ip_configuration {
name = var.name[count.index]
private_ip_address_allocation = "dynamic"
subnet_id = var.subnet_id
}
}
You can use the following:
name = "{var.name}-${count.index}"
Terraform code is below
module "centos-vm-author-2" {
source = "terraform.automation.temp.com.au/temp/temp-linux-vm/azurerm"
version = "6.7.0"
location = var.resource_location
resource_group_name = var.resource_group_name_2
vm_count = "1"
tags = local.tags
size = var.vm_size
hostname_prefix = var.hostname_prefix
hostname_suffix_start_range = "491"
image_publisher = "OpenLogic"
image_offer = "Centos"
image_sku = "7_9"
subnet_id = var.auth_pub_subnet_id
admin_username = "azureadmin"
availability_set_id = azurerm_availability_set.aemfeature1authoras.id
patching_tags = local.patching_tags
ansible_vault_key = var.ansible_vault_key
log_to_loganalytics = false
ou_tags = local.ou_tags
os_disk_size = var.os_size_gb
os_disk_type = var.storage_account_type
server_access_memberships = ["CN=DSTDEVOPS,OU=DistributionGroups,OU=Groups,OU=Resources,DC=temp,DC=int"]
sudoers = ["%DSTDEVOPS"]
data_disks = [
[
{
disk_size_gb = var.disk_size_gb
storage_account_type = var.storage_account_type
caching = "ReadWrite"
create_option = "Empty"
source_resource_id = ""
write_accelerator_enabled = false
}
]
]
}
resource "null_resource" "centos-vm-author-ansible" {
provisioner "local-exec" {
command = <<EOF
ansible-playbook -i '${join(",", azurerm_network_interface.centos-vm-author-2.*.private_ip_address)},'-e ansible_user=${var.admin_username} -e "role_name=automate-author" main.yaml
EOF
}
depends_on = [
module.centos-vm-author-2
]
}
}
Basically I want to tell Ansible the Private IP onto which it should execute the role.
I am getting error like below,
Error: [0m[0m[1mReference to undeclared resource[0m
on main.tf line 236, in resource "null_resource" "centos-vm-author-ansible":
ansible-playbook -i '${join(",", [4mazurerm_network_interface.centos-vm-author-2.*.private_ip_address)},'-e ansible_user=${var.admin_username} -e "role_name=automate-author" main.yaml
A managed resource "azurerm_network_interface" "centos-vm-author-2" has not
been declared in the root module.
Sincerely appreciate any help to understand what is the issue and how to have it resolved.
P.S: The TF Module code is like below:
resource "azurerm_network_interface" "main" {
count = var.vm_count
name = "${format("${var.hostname_prefix}%04d", var.hostname_suffix_start_range + count.index, )}-nic"
location = var.location
resource_group_name = var.resource_group_name
enable_accelerated_networking = var.enable_accelerated_networking
ip_configuration {
name = "${format("${var.hostname_prefix}%04d", var.hostname_suffix_start_range + count.index, )}-ipconfig"
subnet_id = var.subnet_id
private_ip_address_allocation = var.private_ip_address_allocation
private_ip_address = var.private_ip_address
public_ip_address_id = var.enable_public_ip_address ? azurerm_public_ip.main[count.index].id : null
}
tags = var.tags
}
resource "azurerm_network_interface_backend_address_pool_association" "lbconf" {
count = var.backend_address_pool_id == null ? 0 : var.vm_count
network_interface_id = azurerm_network_interface.main[count.index].id
ip_configuration_name = azurerm_network_interface.main[count.index].ip_configuration[0].name
backend_address_pool_id = var.backend_address_pool_id
}
resource "azurerm_linux_virtual_machine" "main" {
count = var.vm_count
name = format("${var.hostname_prefix}%04d", var.hostname_suffix_start_range + count.index, )
location = var.location
resource_group_name = var.resource_group_name
admin_username = var.admin_username
admin_ssh_key {
username = var.admin_username
public_key = chomp(tls_private_key.bootstrap_private_key.public_key_openssh)
}
disable_password_authentication = var.disable_password_authentication
network_interface_ids = [azurerm_network_interface.main[count.index].id]
size = var.size
availability_set_id = var.availability_set_id
source_image_reference {
publisher = var.image_publisher
offer = var.image_offer
sku = var.image_sku
version = var.image_version
}
os_disk {
name = "${format("${var.hostname_prefix}%04d", var.hostname_suffix_start_range + count.index, )}-osdisk"
caching = "ReadWrite"
storage_account_type = var.os_disk_type
disk_size_gb = var.os_disk_size
}
dynamic "identity" {
for_each = var.identity
content {
type = identity.value["type"]
identity_ids = identity.value["type"] == "SystemAssigned" ? [] : identity.value["identity_ids"]
}
}
dynamic "plan" {
for_each = var.marketplace_image ? [1] : []
content {
name = var.image_sku
product = var.image_offer
publisher = var.image_publisher
}
}
# boot_diagnostics {
# storage_account_uri = var.boot_diagnostics_storage_uri
# }
tags = var.ou_tags == null ? merge(var.tags, var.patching_tags) : merge(var.tags, var.ou_tags, var.patching_tags)
}
To refer to your module, instead of:
azurerm_network_interface.centos-vm-author-2.*.private_ip_address
it should be:
module.centos-vm-author-2.private_ip_addresses
Would like to get some pointers on setting up a key vault with a private connection. Looking at the examples on the TF site and other sites I put this together but it crashes.
In short, it creates the KV, assigns some policies, and then creates the private link which is in turn associated with the service endpoint. Any help would be greatly appreciated.
locals {
prefix = "kv01am"
}
data "azurerm_client_config" "current" {}
resource "azurerm_key_vault" "sandbox" {
name = "${local.prefix}-KV"
location = "eastus2"
resource_group_name = "rg-hsc-uscodappname01-137941ad"
enabled_for_disk_encryption = true
tenant_id = data.azurerm_client_config.current.tenant_id
# soft_delete_enabled = true
# purge_protection_enabled = false
sku_name = "standard"
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"get",
]
secret_permissions = [
"get",
]
storage_permissions = [
"get",
]
}
network_acls {
default_action = "Deny"
bypass = "AzureServices"
}
}
resource "azurerm_private_link_service" "example" {
name = "kv-privatelink"
location = "eastus2"
resource_group_name = "rg-hsc-uscodappname01-137941ad"
nat_ip_configuration {
name = azurerm_public_ip.example.name
primary = true
subnet_id = "zzzzzzzzzzzzzzzzzzzzzzzz"
}
}
resource "azurerm_private_endpoint" "sandbox_kv" {
name = azurerm_key_vault.sandbox.name
location = "eastus2"
resource_group_name = "rg-hsc-uscodappname01-137941ad"
#subnet_id = azurerm_subnet.sandbox["PrivateLink"].id
subnet_id = "zzzzzzzzzzzzzzzz"
private_service_connection {
name = azurerm_key_vault.sandbox.name
private_connection_resource_id = azurerm_key_vault.sandbox.id
is_manual_connection = false
subresource_names = ["Vault"]
}
}
Instead of creating dns record "manually" you could have a private_dns_zone_group declared.
# ============PrivateLink==========================
resource "azurerm_private_endpoint" "pe_kv" {
name = format("pe-2%s", var.name)
location = data.azurerm_resource_group.main.location
resource_group_name = data.azurerm_resource_group.main.name
subnet_id = data.azurerm_subnet.main.id
private_dns_zone_group {
name = "privatednszonegroup"
private_dns_zone_ids = [azurerm_private_dns_zone.main.id]
}
private_service_connection {
name = format("pse-2%s", var.name)
private_connection_resource_id = azurerm_key_vault.main.id
is_manual_connection = false
subresource_names = ["Vault"]
}
}
resource "azurerm_private_dns_zone" "main" {
name = "privatelink.vaultcore.azure.net"
resource_group_name = data.azurerm_resource_group.main.name
}
This is what I ended up doing. Could not find a good way to derive the ip address for the private link endpoint so I just hard coded it, if someone has a better way to handle this that would be great, not too much literature on that subject. Also, added a section to register the A record in private DNS but beware this creates a DNS Private zone in the same subnet as the kv.
data "azurerm_resource_group" "main" {
name = var.resource_group_name
}
data "azurerm_subnet" "main" {
name = var.virtual_network_subnet_name
virtual_network_name = var.virtual_network_name
resource_group_name = var.vnet_resource_group_name
}
data "azurerm_client_config" "main" {}
resource "azurerm_key_vault" "main" {
name = var.name
location = data.azurerm_resource_group.main.location
resource_group_name = data.azurerm_resource_group.main.name
tenant_id = data.azurerm_client_config.main.tenant_id
enabled_for_deployment = var.enabled_for_deployment
enabled_for_disk_encryption = var.enabled_for_disk_encryption
enabled_for_template_deployment = var.enabled_for_template_deployment
# soft_delete_enabled = false
# purge_protection_enabled = false
sku_name = var.sku
network_acls {
default_action = "Deny"
bypass = "AzureServices"
# ip_rules = var.ip_rules
}
# ============PrivateLink==========================
resource "azurerm_private_endpoint" "pe_kv" {
name = format("pe-2%s", var.name)
location = data.azurerm_resource_group.main.location
resource_group_name = data.azurerm_resource_group.main.name
subnet_id = data.azurerm_subnet.main.id
private_service_connection {
name = format("pse-2%s", var.name)
private_connection_resource_id = azurerm_key_vault.main.id
is_manual_connection = false
subresource_names = ["Vault"]
}
}
resource "azurerm_private_dns_zone" "main" {
name = "privatelink.vaultcore.azure.net"
resource_group_name = data.azurerm_resource_group.main.name
}
resource "azurerm_private_dns_a_record" "pe_kv" {
name = var.name
zone_name = azurerm_private_dns_zone.main.name
resource_group_name = data.azurerm_resource_group.main.name
ttl = 300
records = ["1.2.3.4"]
}
output kv_private_ip {
value = ["1.2.3.4"]
}
This is how I get fqdn and private IP:
resource "azurerm_private_endpoint" "private_endpoint" {
count = var.private_link_subnet != null ? 1 : 0
name = "${var.private_link_subnet.virtual_network_name}-${var.name}"
location = var.location
resource_group_name = var.resource_group
subnet_id = var.private_link_subnet.id
private_service_connection {
is_manual_connection = false
name = "${var.private_link_subnet.virtual_network_name}-${var.name}"
private_connection_resource_id = azurerm_key_vault.vault.id
subresource_names = ["vault"]
}
lifecycle { ignore_changes = [tags] }
}
resource "null_resource" "dns_update" {
triggers = {
priv_fqdn = "${azurerm_private_endpoint.private_endpoint[0].custom_dns_configs[0].fqdn}"
priv_ip = "${azurerm_private_endpoint.private_endpoint[0].custom_dns_configs[0].ip_addresses[0]}"
}
provisioner "local-exec" {
when = destroy
command = <<EOF
echo ${self.triggers.priv_fqdn}
bash ${path.module}/dns_update.sh destroy ${self.triggers.priv_fqdn}
EOF
}
provisioner "local-exec" {
command = <<EOF
echo ${self.triggers.priv_fqdn}
echo ${self.triggers.priv_ip}
bash ${path.module}/dns_update.sh apply ${self.triggers.priv_fqdn} ${self.triggers.priv_ip}
bash ${path.module}/dns_update.sh get ${self.triggers.priv_fqdn}
EOF
}
}
then I have:
self.triggers.priv_fqdn >> szp.vaultcore.azure.net
self.triggers.priv_ip >> 10.10.8.205