In this blog post we will cover Azure Terraform automation building from scratch Azure standard locally redundant storage (LRS), cloud service, virtual network and Windows server 2012 R2 virtual machines from a example Terraform script.
Terraform is an excellent third party tool for deploying infrastructure as code to various platforms including Azure, Amazon AWS and VMware using simple, easy readable scripts.
This is a step by step guide showing how to use Azure Terraform:
- Install Terraform
- Login to Azure using a published settings file
- Run Terraform script against your Azure cloud subscription
- Automate the creation of Storage, Cloud Service, Virtual Network and Virtual Machine using Azure Terraform automation
Install Azure Terraform
Download Terraform for Windows 64 bit and unzip to a directory
https://www.terraform.io/downloads.html
Set the path in the command line.
SET PATH=%PATH%;c:\terraform\terraform_0.6.11_windows_amd64
Login to Azure using a published settings file
Download your publish settings file from you Azure subscription
https://manage.windowsazure.com/publishsettings
Download to directory where .tf file will be run for example in this case the working directory is C:\terraform\Azure\script>
Run Azure Terraform script against your Azure cloud subscription
Directory structure below shows both create.tf file and publish setting file in path c:\terraform\azure\script
Using the following syntax from the terraform site in your create.tf file to connect to the Microsoft Azure service:
# Configure the Azure Provider provider "azure" { settings_file = "${file("paygcredentials.publishsettings ")}" }
Resulted in error
C:\terraform\Azure>terraform plan ←[31mError loading config: Error parsing C:\terraform\Azure\create.tf: At 4:82: literal not terminated←[0m←[0m
It wasn`t obvious however this was taken from the Terraform site:
“settings_file – Deprecated: please use publish_settings instead.”
create.tf file changed to:
# Connect to the Azure Provider provider "azure" { publish_settings = "${file("paygcredentials.publishsettings")}" }
Run the following command again confirmed connection to Azure using the published settings file:
terraform plan Refreshing Terraform state prior to plan... No changes. Infrastructure is up-to-date. This means that Terraform could not detect any differences between your configuration and the real physical resources that exist. As a result, Terraform doesn't need to do anything.
Azure automation script to create locally redundant storage (LRS), Azure Cloud Service, Virtual Network and Virtual Machine
Directory structure below shows path c:\terraform\azure\script
Containing files
- create.tf (Azure Terraform Script)
- publishsettings
Contents of terraform file create.tf as follows
create.tf
# Specify Azure Credentials to Azure Cloud provider "azure" { publish_settings = "${file("paygcredentials.publishsettings")}" } variable "server_count" { default = 1 } # Create a Standard LRS Cloud Service resource "azure_storage_service" "azurestoragelrs" { name = "azurestoragelrs" location = "North Europe" description = "azurestoragelrs" account_type = "Standard_LRS" } # Create Cloud Service resource "azure_hosted_service" "azure-cloud-service" { name = "frontendlb" location = "North Europe" ephemeral_contents = false description = "cloudservice created for Azure" label = "frontendlb" } # Create Azure Virtual Network (vNet) resource "azure_virtual_network" "default" { name = "vNet01" address_space = ["10.0.0.1/24"] location = "North Europe" subnet { name = "Subnet1" address_prefix = "10.0.0.1/25" } } # Create Azure Virtual Machine Instance resource "azure_instance" "windows 2012 instance" { name = "${format("web%02d", count.index + 1)}" hosted_service_name = "frontendlb" # Deploys to existing cloud service frontendlb image = "Windows Server 2012 R2 Datacenter, January 2016" size = "Basic_A0" storage_service_name = "azurestoragelrs" location = "North Europe" username = "cloudinspired" password = "CloudInspired983!@*" virtual_network = "vNet01" subnet = "subnet1" time_zone = "Europe/London" count = "${var.server_count}" endpoint { name = "RDP" protocol = "tcp" public_port = 3389 private_port = 3389 } }
Run the following to plan (test) the Azure automation task in Terraform
Azure Terraform Plan
$ terraform plan There are warnings and/or errors related to your configuration. Please fix these before continuing. Warnings: * azure_instance.windows 2012 instance: windows 2012 instance: resource name can only contain letters, numbers, dashes, and underscores. This will be an error in Terraform 0.4 No errors found. Continuing with 1 warning(s). Refreshing Terraform state prior to plan... The Terraform execution plan has been generated and is shown below. Resources are shown in alphabetical order for quick scanning. Green resources will be created (or destroyed and then created if an existing resource exists), yellow resources are being changed in-place, and red resources will be destroyed. Note: You didn't specify an "-out" parameter to save this plan, so when "apply" is called, Terraform can't guarantee this is what will execute. + azure_hosted_service.azure-cloud-service default_certificate_thumbprint: "" => "<computed>" description: "" => "cloudservice created for Azure" ephemeral_contents: "" => "0" label: "" => "frontendlb" location: "" => "North Europe" name: "" => "frontendlb" status: "" => "<computed>" url: "" => "<computed>" + azure_instance.windows 2012 instance automatic_updates: "" => "0" description: "" => "<computed>" endpoint.#: "" => "1" endpoint.1814039778.name: "" => "RDP" endpoint.1814039778.private_port: "" => "3389" endpoint.1814039778.protocol: "" => "tcp" endpoint.1814039778.public_port: "" => "3389" has_dedicated_service: "" => "<computed>" hosted_service_name: "" => "frontendlb" image: "" => "Windows Server 2012 R2 Datacenter, January 2016" ip_address: "" => "<computed>" location: "" => "North Europe" name: "" => "web01" password: "" => "CloudInspired983!@*" security_group: "" => "<computed>" size: "" => "Basic_A0" storage_service_name: "" => "azurestoragelrs" subnet: "" => "subnet1" time_zone: "" => "Europe/London" username: "" => "cloudinspired" vip_address: "" => "<computed>" virtual_network: "" => "vNet01" + azure_storage_service.azurestoragelrs account_type: "" => "Standard_LRS" description: "" => "azurestoragelrs" label: "" => "Made by Terraform." location: "" => "North Europe" name: "" => "azurestoragelrs" primary_key: "" => "<computed>" secondary_key: "" => "<computed>" url: "" => "<computed>" + azure_virtual_network.default address_space.#: "" => "1" address_space.0: "" => "10.0.0.1/24" location: "" => "North Europe" name: "" => "vNet01" subnet.#: "" => "1" subnet.236954125.address_prefix: "" => "10.0.0.1/25" subnet.236954125.name: "" => "Subnet1" subnet.236954125.security_group: "" => "" Plan: 4 to add, 0 to change, 0 to destroy.
Run the following to Apply the Azure automation task in Terraform. The Apply command will start connecting to the Azure Subscription and begin automating the environment as specified in the script.
Azure Terraform Apply
$ terraform apply azure_storage_service.azurestoragelrs: Creating... account_type: "" => "Standard_LRS" description: "" => "azurestoragelrs" label: "" => "Made by Terraform." location: "" => "North Europe" name: "" => "azurestoragelrs" primary_key: "" => "<computed>" secondary_key: "" => "<computed>" url: "" => "<computed>" azure_hosted_service.azure-cloud-service: Creating... default_certificate_thumbprint: "" => "<computed>" description: "" => "cloudservice created for Azure" ephemeral_contents: "" => "0" label: "" => "frontendlb" location: "" => "North Europe" name: "" => "frontendlb" status: "" => "<computed>" url: "" => "<computed>" azure_virtual_network.default: Creating... address_space.#: "" => "1" address_space.0: "" => "10.0.0.1/24" location: "" => "North Europe" name: "" => "vNet01" subnet.#: "" => "1" subnet.236954125.address_prefix: "" => "10.0.0.1/25" subnet.236954125.name: "" => "Subnet1" subnet.236954125.security_group: "" => "" azure_instance.windows 2012 instance: Creating... automatic_updates: "" => "0" description: "" => "<computed>" endpoint.#: "" => "1" endpoint.1814039778.name: "" => "RDP" endpoint.1814039778.private_port: "" => "3389" endpoint.1814039778.protocol: "" => "tcp" endpoint.1814039778.public_port: "" => "3389" has_dedicated_service: "" => "<computed>" hosted_service_name: "" => "frontendlb" image: "" => "Windows Server 2012 R2 Datacenter, January 2016" ip_address: "" => "<computed>" location: "" => "North Europe" name: "" => "web01" password: "" => "CloudInspired983!@*" security_group: "" => "<computed>" size: "" => "Basic_A0" storage_service_name: "" => "azurestoragelrs" subnet: "" => "subnet1" time_zone: "" => "Europe/London" username: "" => "cloudinspired" vip_address: "" => "<computed>" virtual_network: "" => "vNet01" azure_hosted_service.azure-cloud-service: Creation complete azure_storage_service.azurestoragelrs: Creation complete azure_virtual_network.default: Creation complete azure_instance.windows 2012 instance: Creation complete Apply complete! Resources: 4 added, 0 changed, 0 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: terraform.tfstate
Azure Storage created in the portal.
Azure Cloud Service created in the portal.
Azure Virtual Network created in the portal.
Azure Virtual Machine being provisioned in the portal.
Azure Virtual Machine fully created and powered up in the portal.
Azure Virtual Machine shows status and public IP to use for Windows remote desktop.
Azure Virtual Machine connected using Windows remote desktop.
Server Count, As specified in the script:
variable "server_count" { default = 1 }
Increasing this number will build and automate multiple VMs from the image.
However, at the time of writing this post there is a bug in Terraform creating multiple VMs under a single cloud service using
Azure Terraform.
If the server count is increased. An error will occur:
Error creating instance terraform: Error response from Azure. Code: ConflictError, Message: The specified deployment slot Production is occupied.
This is currently being fixed and merged in
https://github.com/hashicorp/terraform/issues/3568
but at time of writing has not been merged into Terraform.
A fix for this bug is awaiting to be reviewed, accepted and merged https://github.com/hashicorp/terraform/pull/4199