Azure Terraform Automation

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:

  1. Install Terraform
  2. Login to Azure using a published settings file
  3. Run Terraform script against your Azure cloud subscription
  4. 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

directory

 

 

 

 

 

 

Set the path in the command line.

SET PATH=%PATH%;c:\terraform\terraform_0.6.11_windows_amd64

help

 

 

 

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>

publishsettings

 

 

 

 

 

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

directory2

 

 

 

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 storage lrs

 

 

 

Azure Cloud Service created in the portal.
terraformcloudservice

 

 

Azure Virtual Network created in the portal.

terraformnetwork

 

 

Azure Virtual Machine being provisioned in the portal.

Azure Terraform

Azure Virtual Machine fully created and powered up in the portal.

terraformvirtualmachine2

 

Azure Virtual Machine shows status and public IP to use for Windows remote desktop.

terraformazurestatus

 

 

 

 

 

 

 

 

Azure Virtual Machine connected using Windows remote desktop.

terraformvirtualmachinerdp

 

 

 

 

 

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

 

 

About cloudinspired

Cloud Inspired authors have over 30 years experience within the IT industry, providing expertise and knowledge on infrastructure, hybrid, public and private clouds platforms. Detailed easy to follow technical videos, training and tutorial guides are provided by subject matter experts covering various technologies including Azure, IaaS, SaaS, PaaS and Microsoft 365. This website focuses mainly on the Microsoft 365 and Azure Cloud platform and provides easy to follow step by step technical guides, diagrams, cloud certifications and tutorials. The aim is to deliver articles and videos on Microsoft 365 and Azure Cloud from start to finish on many different Azure services and certifications, building and increasing the viewers knowledge in a short, logical, easy to understand format quickly getting to the point of the subject matter! Check out the YouTube channel for a full list of published Cloud Inspired videos and lets get inspired about Cloud!

View all posts by cloudinspired →

Leave a Reply

Your email address will not be published. Required fields are marked *