Azure VM Creation using For_Each loop via Terraform - azure

I am trying to create multiple Azure VM using for_each via terrform , I am able to create 2 NIC card but while defining NIC id in zurerm_windows_virtual_machine block , both VM are picking same NIC card (last one , index 1) and result is only VM is getting created and other got failed .
what would be logic for (network_interface_ids = azurerm_network_interface.az_nic[*].id) put that 1st vm will pick 1st NIC and second do so .
#---------------creating Network Interface for Windows VM's---------------
resource "azurerm_network_interface" "az_nic" {
count = length(var.vm_names)
name = "${var.vm_names[count.index]}_nic"
location = var.location
resource_group_name = data.azurerm_resource_group.Resource_group.name
ip_configuration {
name = var.vm_names[count.index]
subnet_id = data.azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_windows_virtual_machine" "myvm" {
for_each = toset(var.vm_names)
name = each.value
resource_group_name = data.azurerm_resource_group.Resource_group.name
location = var.location
size = "Standard_D2s_v3"
admin_username = "abc"
admin_password = "uejehrikch123"
network_interface_ids = azurerm_network_interface.az_nic[*].id
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2016-Datacenter"
version = "latest"
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}

You can add the count parameter in the resource "azurerm_windows_virtual_machine" instead of mixing the count and for_each.
Suppose you have
variable "vm_names" {
default = ["vm1", "vm2"]
}
then you can change the resource .tf file like this:
resource "azurerm_windows_virtual_machine" "myvm" {
count = length(var.vm_names)
name = element(var.vm_names,count.index)
resource_group_name = data.azurerm_resource_group.Resource_group.name
location = var.location
size = "Standard_D2s_v3"
admin_username = "abc"
admin_password = "uejehrikch123"
network_interface_ids = [element(azurerm_network_interface.az_nic.*.id, count.index)]
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2016-Datacenter"
version = "latest"
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}

Related

Need help to create Terraform code where user has an option to create Azure VM from Marketplace image or specify a custom VHD from Storage Account

We have a very specific requirement where some of the Vendors provides their images from Azure Marketplace and some just provide the .vhd
I need to build a terraform code where user should have an option to either create a VM based out of Azure Marketplace image, or he should be able provide source_uri of the VHD to create a VM.
For now I have the codes ready to create a VM from .vdh file,
resource "azurerm_virtual_machine" "this" {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
vm_size = var.size
network_interface_ids = [azurerm_network_interface.this.id]
delete_os_disk_on_termination = true
delete_data_disks_on_termination = true
tags = var.tags
availability_set_id = var.availability_set_id == "" ? null : var.availability_set_id
resource "azurerm_managed_disk" "os" {
name = var.os_disk_name
location = "${var.location}"
resource_group_name = var.resource_group_name
os_type = "Linux"
storage_account_type = "Standard_LRS"
create_option = "Import"
storage_account_id = var.storage_account_id
source_uri = var.source_uri
disk_size_gb = var.disk_size_gb
}
# attach the managed disk, created from the imported vhd.
storage_os_disk {
name = join("", [var.name, "-", var.os_disk_name])
os_type = "Linux"
managed_disk_id = azurerm_managed_disk.os.id
managed_disk_type = "Standard_LRS"
caching = "ReadWrite"
create_option = "Attach"
}
os_profile_linux_config {
disable_password_authentication = false
}
}
The default option should be spin up a VM from Azure Marketplace. Can this be archived via variables
You can check the list of VM's image based on publishers available in the Azure Marketplace.
az vm image list --output table --all --publisher center-for-internet-security-inc.
I am taking the below image from Azure MarketPlace as a reference:
You can find your images based on offer, SKU, Publisher on your requirement. Refer to this MS Document for more info.
You can use this terraform code to create Azure VM from Marketplace image:
main.tf
provider "azurerm" {
features{}
}
data "azurerm_resource_group" "main" {
name = "${var.resource_group_name}"
}
resource "azurerm_virtual_network" "main" {
name = "${var.prefix}-network"
address_space = ["10.0.0.0/16"]
location = data.azurerm_resource_group.main.location
resource_group_name = data.azurerm_resource_group.main.name
}
resource "azurerm_subnet" "internal" {
name = "internal"
resource_group_name = data.azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_network_interface" "main" {
name = "${var.prefix}-nic"
location = data.azurerm_resource_group.main.location
resource_group_name = data.azurerm_resource_group.main.name
ip_configuration {
name = "testconfiguration1"
subnet_id = azurerm_subnet.internal.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "main" {
name = "${var.prefix}-vm"
location = data.azurerm_resource_group.main.location
resource_group_name = data.azurerm_resource_group.main.name
network_interface_ids = [azurerm_network_interface.main.id]
admin_username = "adminuser"
vm_size = "Standard_DS1_v2"
# Uncomment this line to delete the OS disk automatically when deleting the VM
# delete_os_disk_on_termination = true
# Uncomment this line to delete the data disks automatically when deleting the VM
# delete_data_disks_on_termination = true
storage_image_reference {
publisher = "${var.publisher}"
offer = "${var.offer}"
sku = "${var.sku}"
version = "${var.version1}"
}
plan {
publisher = "${var.publisher}"
product = "${var.offer}"
name = "cis-ubuntu2004-l1""
}
storage_os_disk {
name = "myosdisk1"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "hostname"
admin_username = "testadmin"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
tags = {
environment = "staging"
}
}
variable.tf
variable "resource_group_name" {
default = "v-XXXXX-XXXXX"
}
variable "prefix" {
default = "tfvmex"
}
variable "publisher" {
default="center-for-internet-security-inc"
}
variable "offer" {
default = "cis-ubuntu-linux-2004-l1"
}
variable "sku" {
default = "cis-ubuntu2004-l1povw"
}
variable "version1" {
default="1.1.9"
}
Due to somepolicy appiled in my subscription so i am not able to test it but yes you can test in your enviorment.
You can refer this document for the same requirement.

How to use selective zone(s) while deploying azure vm through terraform

I am using the below while deploying a virtual machine to specific zone in region eastus
resource "azurerm_linux_virtual_machine" "vm" {
.........
.........
zones = [1]
}
but terraform validate says
An argument named "zones" is not expected here. Did you mean "zone"?
You should prefer for_each and set to count and index. you can use this transformer code to create a virtual Machine in 3 Availability Zone. You can set the number of Zones according to your needs
provider "azurerm" {
features {}
}
data "azurerm_resource_group" "example" {
name = "XXXXXXXXX"
}
resource "azurerm_virtual_network" "example" {
name = "example-network"
address_space = ["10.0.0.0/16"]
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
}
resource "azurerm_subnet" "example" {
name = "internal"
resource_group_name = data.azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_network_interface" "example" {
#count=3
name = "example-nic"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.example.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_linux_virtual_machine" "example" {
#count=3
name = "example-machine"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
size = "Standard_F2"
for_each = local.zones
zone = each.value
admin_username = "adminuser"
network_interface_ids = [
azurerm_network_interface.example.id,
]
admin_ssh_key {
username = "adminuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
}
locals {
zones = toset(["1","2","3"])
}

How to create a 'count.index' to scale the HDDs via terraform for Azure Provider

What are you looking to accomplish?
To scale up or down the quantity of 'Resource VMs' in direct relation with their necessary 'Resource OS_disk'. For example, if we require to provision '5, 10, or 20' VMs we will require also to match the same '5, 10, or 20' OS Disks.
Currently, I know how to use count.index (see bellow code snippet) for the resource VM, but I don't know how to implement it for the resource OS_Disk, meaning that if we require to provision '200 VMs' I can define those '200 VMs' by setting that amount via the parameter count from the resource VM code block (see bellow code snippet), but I can't for the '200' OS_disk (disclaimer, once again because I don't know how to)
Do you have a code example to share?
resource "azurerm_network_interface" "network_card_resource" {
count = 1
name = "network_card_${count.index}"
location = "removed"
resource_group_name = removed
ip_configuration {
name = "internal"
subnet_id = removed.outputs.removed.subnet.removed.main.subnets["removed-XX.XX.XX.XX_XX"].id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_windows_virtual_machine" "vm_resource" {
count = 10
name = "vm_name_${count.index}"
resource_group_name = removed
location = "removed"
size = "Standard_D4_v3"
admin_username = var.admin_username
admin_password = var.admin_password
network_interface_ids = [
element(azurerm_network_interface.network_card_resource.*.id, count.index)
]
os_disk {
count = 10
name = "disk_name_${count.index}"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
resource_group_name = removed
disk_size_gb = 180
}
The above code failed to build the 10 OS disks via the count.index,
Is there an approach to tackle this?
It is not possible to add 10 OS disk for a particular VM , you can have only 1 OS disk Per VM and other than that you can attach data disks (2 disks for 1 VM VCPU).
So , as a solution you can use for_each instead of count for better functionality of the code and then you can use the below script to create the VM's with additional data disks as per your requirement:
In the below code I am creating 3 VM's with 1 OS disk and 5 Data disk each:
provider "azurerm"{
features{}
}
data "azurerm_resource_group" "example"{
name="ansumantest"
}
resource "azurerm_virtual_network" "example" {
name = "example-network"
address_space = ["10.0.0.0/16"]
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
}
resource "azurerm_subnet" "test_subnet" {
name = "VM-subnet"
resource_group_name = data.azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.0.0/24"]
}
resource "azurerm_public_ip" "myterraformpublicip" {
for_each = toset(var.instances)
name = "myPublicIP-${each.key}"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
allocation_method = "Dynamic"
}
resource "azurerm_network_interface" "myterraformnic" {
for_each= toset(var.instances)
name = "myNIC-${each.key}"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
ip_configuration {
name = "myNicConfiguration"
subnet_id = azurerm_subnet.test_subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.myterraformpublicip[each.key].id
}
}
variable "instances" {
default = ["vm-test-1", "vm-test-2", "vm-test-3"]
}
variable "nb_disks_per_instance" {
default = 5
}
locals {
vm_datadiskdisk_count_map = { for k in toset(var.instances) : k => var.nb_disks_per_instance }
luns = { for k in local.datadisk_lun_map : k.datadisk_name => k.lun }
datadisk_lun_map = flatten([
for vm_name, count in local.vm_datadiskdisk_count_map : [
for i in range(count) : {
datadisk_name = format("datadisk_%s_disk%02d", vm_name, i)
lun = i
}
]
])
}
resource "azurerm_windows_virtual_machine" "example" {
for_each = toset(var.instances)
name = "example-machine${each.key}"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
size = "Standard_F2"
admin_username = "adminuser"
admin_password = "P#$$w0rd1234!"
network_interface_ids = [azurerm_network_interface.myterraformnic[format("%s", each.key)].id]
os_disk {
name = "${each.key}-OSDISK"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
disk_size_gb = 180
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2016-Datacenter"
version = "latest"
}
}
resource "azurerm_managed_disk" "example" {
for_each = toset([for j in local.datadisk_lun_map : j.datadisk_name])
name = each.key
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
storage_account_type = "Standard_LRS"
create_option = "Empty"
disk_size_gb = 10
}
resource "azurerm_virtual_machine_data_disk_attachment" "example" {
for_each = toset([for j in local.datadisk_lun_map : j.datadisk_name])
managed_disk_id = azurerm_managed_disk.example[each.key].id
virtual_machine_id = azurerm_windows_virtual_machine.example[element(split("_", each.key), 1)].id
lun = lookup(local.luns, each.key)
caching = "ReadWrite"
}
Output:
Matching the OS Disk with count is not required by providing another count inside the OS_disk Block :
For example, I created 2 VMs using terraform then its by default that 2 OS disk will also be created like below :
resource "azurerm_linux_virtual_machine" "myterraformvm" {
count = 2
name = "testpoc0${count.index + 1}"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
#network_interface_ids = azurerm_network_interface.myterraformnic.*.id
network_interface_ids = [element(azurerm_network_interface.myterraformnic.*.id, count.index + 1)]
size = "Standard_DS1_v2"
os_disk {
## count == not required here again as this is a child block inside the main VM resource block , so as many VM's will be created OS disk will also be created that many
name = "OsDisk${count.index + 1}"
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
computer_name = "testpoc0${count.index}"
admin_username = "azureuser"
disable_password_authentication = true
admin_ssh_key {
username = "azureuser"
public_key = file("~/.ssh/id_rsa.pub")
}
}

Azure Multi regions deployement error for count.index many times

I am trying to have the simplest terraform script to deply 6 linux vm's in 6 different Azure Regions, However, my concept doesnt seem to work, and I am not sure how to go from there.
I get this error at multiple places:
Error: Reference to "count" in non-counted context
on main.tf line 4, in resource "azurerm_resource_group" "mygroup":
4: location = "${var.regions[count.index]}"
The "count" object can be used only in "resource" and "data" blocks, and only
when the "count" argument is set.
If I just put a string in place of the specified location, I just get more errors like this, at other places.
I wrote some terraform code and tried to validate it using terraform executable.
My variables.tf file:
variable “regions” {
description = “Regions to deploy”
type = list
default = [“canadaeast”, “eastus”, “japaneast”, “ukwest”, “southeastasia”, “germanynorth”]
}
variable “name” {
description = “name”
type = list
default = [“albert”, “ben”, “carol”, “denis”, “eric”, “frank”]
}
and my main.tf file:
# Create a resource group
resource "azurerm_resource_group" "mygroup" {
name = "RG-MyVMs"
location = "${var.regions[count.index]}"
}
# Create virtual network
resource "azurerm_virtual_network" "mynetwork" {
name = "myVnet-${var.name[count.index]}"
address_space = ["10.0.0.0/16"]
location = "${var.regions[count.index]}"
resource_group_name = "${azurerm_resource_group.mygroup.name}"
}
# Create subnet
resource "azurerm_subnet" "mysubnet" {
name = "mySubnet-${var.name[count.index]}"
resource_group_name = "${azurerm_resource_group.mygroup.name}"
virtual_network_name = "${azurerm_virtual_network.mynetwork.name}"
address_prefix = "10.0.1.0/24"
}
# Create public IPs
resource "azurerm_public_ip" "mypublicip" {
name = "myPublicIP"
location = "${var.regions[count.index]}"
resource_group_name = "${azurerm_resource_group.mygroup.name}"
allocation_method = "Dynamic"
}
# Create Network Security Group and rule
resource "azurerm_network_security_group" "mynsg" {
name = "myNetworkSecurityGroup"
location = "${var.regions[count.index]}"
resource_group_name = "${azurerm_resource_group.mygroup.name}"
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
# Create network interface
resource "azurerm_network_interface" "mynic" {
name = "myNIC"
location = "${var.regions[count.index]}"
resource_group_name = "${azurerm_resource_group.mygroup.name}"
network_security_group_id = "${azurerm_network_security_group.mynsg.id}"
ip_configuration {
name = "myNicConfiguration"
subnet_id = "${azurerm_subnet.mysubnet.id}"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = "${azurerm_public_ip.mypublicip.id}"
}
}
# Create virtual machine
resource "azurerm_virtual_machine" "myvm" {
count = "${length(var.regions)}"
name = "${var.name[count.index]}"
location = "${var.regions[count.index]}"
resource_group_name = "${azurerm_resource_group.mygroup.name}"
network_interface_ids = ["${azurerm_network_interface.mynic.id}"]
vm_size = "Standard_B1ls"
storage_os_disk {
name = "myOsDisk"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04.0-LTS"
version = "latest"
}
os_profile {
computer_name = "${var.name[count.index]}"
admin_username = "admin"
admin_password = ""
}
os_profile_linux_config {
disable_password_authentication = false
}
}
I am expected to achieve my goals and validate this code before trying to deploy.

How to use Conditional Attributes in Terraform

I wrote a script to create Azure VM using terraform. Script is flexible to take the OS as input. Now there are some attributes in azurerm_virtual_machine resource block which are specific to OS.
How to use a condition like if windows use os_profile_windows_config{} or if OS is linux use os_profile_linux_config{}.
Normal conditional statements didn't work as this attributes are not taking any values directly by using = .
resource "azurerm_virtual_machine" "vmdeploy" {
count = "${var.count_of_VMs}"
name = "${var.vm_name}-${count.index}"
resource_group_name = "${azurerm_resource_group.deployrg.name}"
availability_set_id = "${azurerm_availability_set.avset.id}"
location = "${azurerm_resource_group.deployrg.location}"
network_interface_ids = ["${element(azurerm_network_interface.nic.*.id, count.index)}"]
vm_size = "Standard_DS1_v2"
storage_image_reference{
publisher = "${var.OS_Image_Publisher}"
offer = "${var.OS_Image_Offer}"
sku = "${var.OS_Image_Sku}"
version = "latest"
}
storage_os_disk{
name = "${var.vm_name}-${count.index}-osdisk"
caching = "ReadWrite"
managed_disk_type = "Standard_LRS"
create_option = "FromImage"
}
storage_data_disk {
name = "${element(azurerm_managed_disk.mdisk.*.name, count.index)}"
managed_disk_id = "${element(azurerm_managed_disk.mdisk.*.id, count.index)}"
create_option = "Attach"
lun = 1
disk_size_gb = "${element(azurerm_managed_disk.mdisk.*.disk_size_gb, count.index)}"
}
os_profile {
computer_name = "${var.vm_name}-${count.index}"
admin_username = "XXXXXXXXXXXX"
admin_password = "XXXXXXXXXXXX"
}
os_profile_windows_config {
}
}
I am trying to find a way to use condition here to use respective config attribute based on OS version which is provided as input.
This is not possible pre terraform-0.12. This is possible with dynamic blocks in version 0.12.
provider "azurerm" {
version = "~>2.19.0"
features {}
}
variable "image" {
type = map
default = {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
}
locals {
_is_microsoft = substr(lookup(var.image, "publisher", "value_not_declared"), 0, 9) == "Microsoft" ? true : false
is_linux = ! local._is_microsoft ? { empty = true } : {}
is_windows = local._is_microsoft ? { empty = true } : {}
}
resource "azurerm_resource_group" "rg" {
name = "rg"
location = "east us"
}
resource "azurerm_virtual_network" "rg" {
name = "-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_subnet" "subnet" {
name = "subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.rg.name
address_prefixes = ["10.10.10.0/24"]
}
resource "azurerm_network_interface" "rg" {
name = "nic"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "ipconfig"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "rg" {
name = "-vm"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.rg.id]
vm_size = "Standard_D2_v3"
storage_image_reference {
publisher = lookup(var.image, "publisher")
offer = lookup(var.image, "offer")
sku = lookup(var.image, "sku")
version = lookup(var.image, "version")
}
storage_os_disk {
name = "disk1"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "computer"
admin_username = "localadmin"
admin_password = "donotuserthispassword123!"
}
dynamic "os_profile_linux_config" {
for_each = local.is_linux
content {
disable_password_authentication = false
}
}
dynamic "os_profile_windows_config" {
for_each = local.is_windows
content {
provision_vm_agent = true
}
}
}
Note the azurerm team decided this was not a
good
idea and split out the resources into two different types. Use
azurerm_linux_virtual_machine or azurerm_windows_virtual_machine.
Build a module with a single interface then use a count to pick which
resource to build either windows or linux.

Resources