Authoring Azure Automation Azure Stack Configuration Testing

Azure Stack – Azure Stack Development Kit Meets Terraform

Bildergebnis für azure stack terraform

In this post I would like to write about my experiences using Terraform on Azure Stack Development Kit (ASDK). What is Terraform?

Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.

Configuration files describe to Terraform the components needed to run a single application or your entire datacenter. Terraform generates an execution plan describing what it will do to reach the desired state, and then executes it to build the described infrastructure. As the configuration changes, Terraform is able to determine what changed and create incremental execution plans which can be applied.

The infrastructure Terraform can manage includes low-level components such as compute instances, storage, and networking, as well as high-level components such as DNS entries, SaaS features, etc.

There are plenty of reason why Terraform is cool and should be one of the DevOps tool you need in your toolbox. I am not going to teach you Terraform or Azure Stack there are plenty of good sources on the internet. Instead my goal is to show you, how infrastructure as code (IaC) using Terraform works on Azure Stack. Actually before I am showing you how it works, I will deploy a Windows server VM on ASDK, which will be the Terraform deployment server. Then we need to setup an Azure Active Directory SPN that will be used to authenticated against Azure Active Directory and finally gaining access to Azure Stack. Remember, we have Azure Stack Development Toolkit deployed and joined to our Azure Active Directory tenant (see previous posts about ASDK).

I downloaded the Windows Server 2016 Datacenter – BYOL template from the Azure Stack Marketplace….

VM00

…next we start the VM deployment as we would do it on Azure…

VM01

…the summary page shows this…

VM02

…and after some time the deployment succeeded…

VM03

Next log into the VM and download Terrafom, which is available in different flavours. Actually Terraform is just one .exe (for Windows) so unzip the Zip file and extract the exe into a default Windows OS path…

VM04

Next, try to run terraform either in the command window or in a PowerShell window…

VM05

The next step is to create an Azure Active Directory Service Principal Name (AAD SPN). Navigate to your AAD tenant and create an application…

VM07

…make sure you write down your application ID and the key. If you are not familiar with AAD SPN creation, please check the docs here. Next go to the Azure Stack tenant portal, login with the user account I used AS_TestUser01@stefanroth.net. Go to Subscriptions and give the created AAD SPN application Contributor permission on the subscription…

VM08

…now we are all set.

Just a recap, first we deployed a Windows server on ASDK. Then we deployed the Terraform application (single terraform.exe!) into a common OS path so we can start it right in the console / PowerShell window. Next, we created an AAD SPN in Azure and gave it permission on our tenant subscription on Azure Stack. Congratulation, we are ready to write our first Terraform script!

I have created three files…

VM12

First there is a variables.tf file which instantiate the variables or provides any default values…

VM09

…the second file is terraform.tfvars which contains the actual values for the variables. You could actually use this to override values from the variables.tf file if needed. The variables define the arm_endpoint, subscription_id, client_id, client_secret, tenant_id. What are those values?

  • arm_endpoint => API so contact for deploying the resources on Azure Stack. In case of ASDK it is https://management.local.azurestack.external
  • subcription_id =>  The tenant subscription you want to deploy the resource on Azure Stack
  • client_id => ID of the AAD SPN application created in Azure aka application ID
  • client_secret => Key generated when you create the AAD SPN in Azure
  • tenant_id => ID of the Azure Active Directory tenant in Azure

….terraform.tfvars looks like this…

vm10

The last file resourcegroup.tf contains the actual declaration of the resource we want to deploy. In this case it is a resource group.

The first block initiates the Azure Stack provider and assigns the variables from the other files (variables.tf / terraform.tfvars) . The second block defines the resource to deploy and its properties…

VM11

I will not teach you Terraform in this post, I assume you know a little bit about infrastructure as code (IaC) and some principles how such technologies work. If not I urge you to start reading the Terraform documentation to get started with it.

The simplest configuration would be to pack all variables and resource declaration in one single file and execute that specific file. This monolithic approach is probably not what we want, so I split the files apart to have a more granular and easier to maintain solution. Terraform will merge these files together to a single code and then execute it. To test, apply and delete the configuration we need to know about three simple modes. First there is plan, which tells you what will be done without touching the infrastructure. Then there is the apply, which actually executes the code. Finally the destroy mode will delete the declared resource.

Let’s see it in action!

On our Terraform deployment server, we open a PowerShell console and navigate to the directory where our configuration files are stored. In my case C:\Users\Stefan\Desktop\Sample. Then we start Terraform by executing terraform init command. This will download the necessary providers and create some state files. The next command terraform plan will tell us what will be done…

VM13

…if we agree to that plan, we type terraform apply and the declared resource group will be provisioned…

VM14

…let’s check the Azure Stack tenant portal…

VM15

…and as we expected the resource group has been created!

In order to see what will happen, if we change just the tag value and re-apply the configuration, we need to change the configuration…

Vm16

…terraform plan command will tell us that only that specific value will be changed…

VM17

…and terraform apply will perform the action…

VM18

…the result in the tenant portal will show us the tag has been updated…

VM19

…finally terraform destroy will delete the resource group…

VM20

This is a very simple test I have done with Azure Stack and I honestly expected it to work as it did. I was wondering what happens if take a more complicated example, like deploying a Linux VM with subnets, IP, storage account etc.? On the Terraform Azure Stack provider page there is an example I wanted to try out. I just needed to modify the resource group location to “local” instead of a fancy region. So I created a file VM.tf , copy and paste the code below into a VM.tf file…

provider "azurestack" {
arm_endpoint    = "${var.arm_endpoint}"
subscription_id = "${var.subscription_id}"
client_id       = "${var.client_id}"
client_secret   = "${var.client_secret}"
tenant_id       = "${var.tenant_id}"
}

resource "azurestack_resource_group" "test" {
name     = "acctestrg"
location = "local"
}

resource "azurestack_virtual_network" "test" {
name                = "acctvn"
address_space       = ["10.0.0.0/16"]
location            = "${azurestack_resource_group.test.location}"
resource_group_name = "${azurestack_resource_group.test.name}"
}

resource "azurestack_subnet" "test" {
name                 = "acctsub"
resource_group_name  = "${azurestack_resource_group.test.name}"
virtual_network_name = "${azurestack_virtual_network.test.name}"
address_prefix       = "10.0.2.0/24"
}

resource "azurestack_network_interface" "test" {
name                = "acctni"
location            = "${azurestack_resource_group.test.location}"
resource_group_name = "${azurestack_resource_group.test.name}"

ip_configuration {
name                          = "testconfiguration1"
subnet_id                     = "${azurestack_subnet.test.id}"
private_ip_address_allocation = "dynamic"
}
}

resource "azurestack_storage_account" "test" {
name                     = "accsa"
resource_group_name      = "${azurestack_resource_group.test.name}"
location                 = "${azurestack_resource_group.test.location}"
account_tier             = "Standard"
account_replication_type = "LRS"

tags {
environment = "staging"
}
}

resource "azurestack_storage_container" "test" {
name                  = "vhds"
resource_group_name   = "${azurestack_resource_group.test.name}"
storage_account_name  = "${azurestack_storage_account.test.name}"
container_access_type = "private"
}

resource "azurestack_virtual_machine" "test" {
name                  = "acctvm"
location              = "${azurestack_resource_group.test.location}"
resource_group_name   = "${azurestack_resource_group.test.name}"
network_interface_ids = ["${azurestack_network_interface.test.id}"]
vm_size               = "Standard_F2"

# 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 = "Canonical"
offer     = "UbuntuServer"
sku       = "16.04-LTS"
version   = "latest"
}

storage_os_disk {
name          = "myosdisk1"
vhd_uri       = "${azurestack_storage_account.test.primary_blob_endpoint}${azurestack_storage_container.test.name}/myosdisk1.vhd"
caching       = "ReadWrite"
create_option = "FromImage"
}

# Optional data disks
storage_data_disk {
name          = "datadisk0"
vhd_uri       = "${azurestack_storage_account.test.primary_blob_endpoint}${azurestack_storage_container.test.name}/datadisk0.vhd"
disk_size_gb  = "1023"
create_option = "Empty"
lun           = 0
}

os_profile {
computer_name  = "hostname"
admin_username = "testadmin"
admin_password = "Password1234!"
}

os_profile_linux_config {
disable_password_authentication = false
}

tags {
environment = "staging"
}
}

…then I copied the variables.tf and terraform.tfvars files from the first test into the same directory C:\Users\Stefan\Desktop\TF_VM . Of course you have to make sure, that you downloaded the appropriate server image from the Azure Marketplace. Next I executed terraform apply…

VM21

…and after a couple of minutes…

VM22

…everything was deployed without any errors…

VM23

…after playing around with the VM, I deleted the resources by typing terraform destroy

VM26

Conclusion:

Terraform is definitely a technology we need to know and have to learn. In my basic tests I haven’t experience any issues or strange behaviours. Be aware, Terraform is very powerful and has many advanced features like using modules in your code. It will unleash its strength when it comes to multi cloud / hybrid cloud scenarios through its multi provider plugins. There is a large community online, where you can find support and samples how to solve your problems. In terms of ASDK it is an ideal playground to get started with it and get some first hands-on. If you have an Azure subscription, then you can start right away because Terraform is baked into the Azure platform! I definitely recommend having an eye on Terraform.

One Reply to “Azure Stack – Azure Stack Development Kit Meets Terraform

  1. Thanks for this walk-thru! Did you try the same thing lately? Cannot get the connection to work – not with an application SPN, not with a certificate, not with manual login with username and password (az login) – the message is always that the subscription cannot be found.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.