What is Terraform

Terraform is an open-source infrastructure as code software tool that enables you to safely and predictably create, change, and improve infrastructure as described on their website.

Creating a Service Principal on Azure

If you want to automate some of the workloads on Azure, then you will need Service Principal (SP) accounts. We will use an SP for our automation.

Because only way for Terraform to work on Azure is to connecting it, we will connect Terraform with Azure via a Service Principal. Let’s create our SP.

You will need your subscription ID for this SP. You can get your subscription ID after logging into Azure-CLI or Azure portal and using CLI with below command:

az account show --subscription <subscription_name> --query id

After getting your subscription ID, we can create our SP with below command:

az ad sp create-for-rbac - name <service_principal_name> - role Contributor - scopes /subscriptions/<subscription_id>

Now, after creating our SP, the information about our SP will be on the STDOUT like below:

{
  "appId": "someappid",
  "displayName": "sp-diplay-name",
  "password": "superstrongpassword",
  "tenant": "tenantid"
}

Configuring Terraform

First we need to configure Azure connection information for Terraform. This can be done in multiple ways. I prefer to create environment variables and then pass these variables to Terraform in runtime. The other way is to write connection information in the providers.tf file. (Not recommending it)

File structure

Our file structure will be like below:

├── .connection.env
├── main.tf
├── outputs.tf
├── providers.tf
└── variables.tf

Creating providers.tf file

Terraform will know which provider to use from our providers.tf file. Let’s create a file named providers.tf with below configuration

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=2.84.0"
    }
  }
  required_version = ">= 1.1.3"
}

provider "azurerm" {
  features {}
}

Creating variables.tf file

We will provide any variables to Terraform with variables.tf file. In this case, we are providing only our resource group location.

variable "resource_group_location" {
  default     = "West Europe"
  description = "Location of the resource group."
}

Creating outputs.tf file

We will get any output generated from Terraform with outputs.tf file. In this case we are getting our VM’s resource group name, public IP and private key of the SSH-key.

output "resource_group_name" {
value = azurerm_resource_group.rg.name
}

output "public_ip_address" {
value = azurerm_linux_virtual_machine.my_terraform_vm.public_ip_address
}

output "tls_private_key" {
value = tls_private_key.secureadmin_ssh.private_key_pem
sensitive = true

Creating main.tf file

Terraform will run based on main.tf file. We will add our VM configuration as well as Azure resource configurations to this file.

resource "azurerm_resource_group" "rg" {
  location = var.resource_group_location
  name     = "my-first-terraform-RG"
}

# Create virtual network
resource "azurerm_virtual_network" "my_terraform_network" {
  name                = "my-first-terraform-network"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

# Create subnet
resource "azurerm_subnet" "my_terraform_subnet" {
  name                 = "my-first-terraform-subnet"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.my_terraform_network.name
  address_prefixes     = ["10.0.1.0/24"]
}

# Create public IPs
resource "azurerm_public_ip" "my_terraform_public_ip" {
  name                = "my-first-terraform-PublicIP"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = "Dynamic"
}

# Create Network Security Group and rule
resource "azurerm_network_security_group" "my_terraform_nsg" {
  name                = "my-first-terraform-NetworkSecurityGroup"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
# Note that this rule will allow all external connections from internet to SSH port
  
  security_rule {
    name                       = "SSH"
    priority                   = 200
    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" "my_terraform_nic" {
  name                = "my-first-terraform-myNIC"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "my-first-terraform-nic-configuration"
    subnet_id                     = azurerm_subnet.my_terraform_subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.my_terraform_public_ip.id
  }
}

# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "my-nsg-assoc" {
  network_interface_id      = azurerm_network_interface.my_terraform_nic.id
  network_security_group_id = azurerm_network_security_group.my_terraform_nsg.id
}

# Create (and display) an SSH key
resource "tls_private_key" "secureadmin_ssh" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

# Create virtual machine
resource "azurerm_linux_virtual_machine" "my_terraform_vm" {
  name                  = "my-first-terraform-VM"
  location              = azurerm_resource_group.rg.location
  resource_group_name   = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.my_terraform_nic.id]
  size                  = "Standard_DS1_v2"

  os_disk {
    name                 = "my-first-terraform-OsDisk"
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-LTS"
    version   = "latest"
  }

  computer_name                   = "my-first-terraform-vm"
  admin_username                  = "secureadmin"
  disable_password_authentication = true

  admin_ssh_key {
    username   = "secureadmin"
    public_key = tls_private_key.secureadmin_ssh.public_key_openssh
  }
}

About Author

Leave a Reply