if clauses in terraform 0.12 - azure

I am new to Terraform, using Azure...I am trying to build a module "compute" where I can deploy a single vm or a vm set.
For a single VM I need a network interface, a security group association, and a azurerm_linux_virtual_machine. For a vm set I need to provision only azurerm_linux_virtual_machine_scale_set. Is it possible to pass a boolean variable to this module to select which resources get executed?
I've checked this post but apparently there is no such thing.
Should I simply divide the module into compute/vm and compute/scale_set and actually have two modules, one for single vms and one for vm sets? Not sure if this will be a pain to maintain in the future.

Yes, you can use a boolean to select what is built. Generally speaking, you can use the count control:
resource "azurerm_linux_virtual_machine" "vm" {
count = var.single_only ? 1 : 0
... (rest of config)
}
resource "azurerm_linux_virtual_machine_scale_set" "vm_set" {
count = var.single_only ? 0 : 1
... (rest of config)
}
(see the end of this section on count resources)

Yes, it's possible and you don't need to divide the module.
As the statements in the posted link:
You can accomplish the resource creating selection by using the count parameter and conditional expression.
In this case, you can declare two bool variables like this:
variable "create_vm" {
description = "If set to true, it will create vm"
type = bool
}
variable "create_vmss" {
description = "If set to true, it will create vmss"
type = bool
}
and define the resource azurerm_linux_virtual_machine and azurerm_linux_virtual_machine_scale_set in the same VM module.
resource "azurerm_linux_virtual_machine" "example" {
count = var.create_vm ? 1 : 0
name = "example-machine"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
size = "Standard_F2"
...
resource "azurerm_linux_virtual_machine_scale_set" "example" {
count = var.create_vmss ? 1 : 0
name = "example-vmss"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
sku = "Standard_F2"
instances = 1
admin_username = "adminuser"
....
Then call the submodule like this,
module "vm" {
source = "./modules/vm"
create_vm = true
create_vmss = false
...
}
Hope this helps.

Related

Terraform how to skip argument for 1 workspace

Is it possible to skip 1 argument for 1 workspace in terraform?
resource "azurerm_application_gateway" "appgw" {
name = var.appgw
resource_group_name = var.resource_group
location = var.location
**zones = var.aks_zones**
sku {
name = var.app_gateway_sku
tier = var.app_gateway_tier
}
I am setting a DR environment in a region where availability zones are not supported, so for the script to pass the "Zones" argument needs to be skipped for one workspace only. Is this possible?
To fix this, I added an availability_zone = "No-Zone" argument to my AppGW IP address block.
Since the azurerm_application_gateway resource's zones variable is Optional (source), you can set the default value for your aks_zones variable to null:
variable "aks_zones" {
default = null
}
This way, you can skip specifying the aks_zones variable for your one workspace while setting a value for the other workspaces.

Terraform count length of variables

we have a standard naming convention within azure, but in order to sometimes be able to make an exception, it must be possible to provide a name yourself when calling the module
How can be indicated within count which variable Var.Log Name or Local.ComponetName should be used and how can we pass this to the name of the resource
resource "azurerm_log_analytics_workspace" "LOG" {
count = length(var.LOG_Name) == "" ? length(local.ComponentNames) : null
name = var.LOG_Name[count.index] == "" ? local.ComponentNames[count.index] : null
resource_group_name = element(var.resourcegroup_name[*], count.index)
location = var.location
sku = var.LOG_Sku
retention_in_days = var.LOG_RetentionPeriod
}
What you are actually looking for are loops. Within loops you can reference the name of the resource and in case there is no such resource available it won't create them, which seems to be what you tried to indicate when mentioning null.
Here is a great link regarding loops in terraform that thoroughly explains the different types of loops and how to use them: https://blog.gruntwork.io/terraform-tips-tricks-loops-if-statements-and-gotchas-f739bbae55f9

Terraform: Conditional creation of a resource based on a variable in .tfvars

I have resources defined in .tf files that are generic to several applications. I populate many of the fields via a .tfvars file. I need to omit some of the resources entirely based on variables in the .tfvars.
For example if I have a resource like:
resource "cloudflare_record" "record" {
zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
name = "${var.subdomain}"
value = "${var.origin_server}"
type = "CNAME"
ttl = 1
proxied = true
}
But then I declare something like cloudflare = false in my .tfvars file I'd like to be able to do something like this:
if var.cloudflare {
resource "cloudflare_record" "record" {
zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
name = "${var.subdomain}"
value = "${var.origin_server}"
type = "CNAME"
ttl = 1
proxied = true
}
}
I've looked at dynamic blocks but that looks like you can only use those to edit fields and blocks within a resource. I need to be able to ignore an entire resource.
Add a count parameter with a ternary conditional using the variable declared in .tfvars like this:
resource "cloudflare_record" "record" {
count = var.cloudflare ? 1 : 0
zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
name = "${var.subdomain}"
value = "${var.origin_server}"
type = "CNAME"
ttl = 1
proxied = true
}
In this example var.cloudflare is a boolean declared in the .tfvars file. If it is true a count of 1 record will be created. If it is false a count of 0 record will be created.
After the count apply the resource becomes a group, so later in the reference use 0-index of the group:
cloudflare_record.record[0].some_field
Expanding on #Joel Guerra's answer, after you use count to determine whether to deploy the resource or not, you can use the one() function to refer to the resource without an index (i.e. without having to use [0]).
For example, after defining the resource like below
resource "cloudflare_record" "record" {
count = var.cloudflare ? 1 : 0
}
Define a local variable like below
locals {
cloudflare_record_somefield = one(cloudflare_record.record[*].some_field)
}
Now instead of cloudflare_record.record[0].some_field, you can use
local.cloudflare_record_somefield
If the count is 0 (e.g. var.cloudflare is false and the resource wasn't created) then local.cloudflare_record_somefield would return null (instead of returning an error when indexing using [0]).
Reference: https://developer.hashicorp.com/terraform/language/functions/one
An issue i'm seeing this with is if the resource your trying to create is already using a for_each then you can't use both count and for_each in the resource. I'm still trying to find an answer on this will update if I find something better.

Creating two VMs in the same resource group without Terraform wanting to destoy the first one

I'm trying to deploy two virtual machines within the same resource group to our Azure platform with Terraform. After successfully creating the first one Terraform then wants to destroy the first one to create the second one after I've changed the second VM name and Azure tag.
I've been following the Terraform guide: https://www.terraform.io/docs/providers/azurerm/r/virtual_machine.html
resource "azurerm_virtual_machine" "main" {
location = "${var.location}"
name = "${var.vm_name}"
network_interface_ids = ["${azurerm_network_interface.main.id}"]
resource_group_name = "${var.resourcegroup_vm}"
vm_size = "${var.vm_size}"
tags {
application = "${var.tag}"
}
I expected Terraform to just create the second VM after changing its variable name and tag. Not wanting to destory the first one because of the name and tag change.
Terraform is based on HCL (Hashicorp Configuration Language), which is the format *.tf files are written in. It is a declarative language (as opposed to imperative), which means that you describe the desired state you want your infrastructure to be and Terraform will figure out what changes are needed to take it to that point.
If you first create an instance and then change its name you are telling Terraform that you no longer want your instance to have the old name but the new one.
To deploy a number of instances you can use the count attribute. You could then use interpolation to get names and tags based in the counter, something similar to this:
resource "azurerm_virtual_machine" "main" {
location = "${var.location}"
name = "${var.vm_name}-${count.index + 1}"
network_interface_ids = ["${azurerm_network_interface.main.id}"]
resource_group_name = "${var.resourcegroup_vm}"
vm_size = "${var.vm_size}"
tags {
application = "${var.tag}-${count.index + 1}"
}
count = 2
}
Note the attached -${count.index + 1} to name and the application tag.

Terraform retrieve CIDR/Prefix from existing VNETs/subnets

In Terraform, I want to create a route table for an existing subnet. To achieve the desired end result, I need to pull the CIDR/Prefix for the VNET. The VNET CIDR value is not known beforehand, the only values I know before launch is the VNET's name and Resource Group.
I would like to take the VNET CIDR/Prefix and insert it as a destination in the route table.
data "azurerm_virtual_network" "vnet" {
name = "${var.vnet_name}"
resource_group_name = "${var.vnet_rg}"
}
module "routetable" {
source = "modules/routetable"
route_table_name = "${var.route_table_name}"
resource_group_name =
"${data.azurerm_resource_group.vnet.name}"
location = "eastus"
route_prefixes = ["0.0.0.0/0", "${EXISTING_VNET_CIDR_HERE}"]
route_nexthop_types = ["VirtualAppliance", "VirtualAppliance"]
route_names = ["route1", "route2"]
}
just use data you are getting from the vnet:
${data.azurerm_virtual_network.vnet.address_spaces}
the only issue - assress_spaces is an array (i think its called list in terraforms terms).

Resources