Terraform: Versnel innovatie – Deel 3

Blog Terraform

Welkom op het derde en laatste deel van het blog. In deel één van het blog hebben we gewerkt met Terraform vanaf een lokale machine en in deel twee van het blog zijn we begonnen met een Azure DevOps project en hebben we twee pipelines gemaakt.

In dit deel laten we de vorige twee blogs samen komen. Dit keer rollen door gebruik te maken van van Terraform en Azure DevOps Pipeline, een virtueel netwerk uit met een virtuele machine.

Let op: Waar een leeg storage account minimale kosten heeft, zal het draaien van een virtuele machine wat meer kosten met zich meebrengen. Het is daarom belangrijk om aan het eind van het blog de spullen weer te verwijderen.

Laten we beginnen!

Installeren Azure DevOps Extensie

Binnen Azure DevOps zijn er al diverse ingebouwde taken en functies die we kunnen gebruiken. Denk bijvoorbeeld aan de Powershell taken of ARM-Templates.

Naast deze ingebouwde taken zijn er extensies die we kunnen toevoegen om bijvoorbeeld Terraform taken toe te voegen. De extensies kun je onder andere op de marketplace terug vinden. Deze extensies zijn op organisatie niveau te installeren en daarna bruikbaar binnen de projecten.

Om deze te installeren loggen we in bij Azure DevOps en gaan we naar de Azure DevOps organisatie. Daar klikken we links onder op Organization settings

Binnen de Organization Settings, klikken we in de linker balk op Extensions. daarna klikken we rechts boven op Browse Marketplace.

De marketplace zal openen in een nieuwe tabblad. Hier zoeken we op Terraform en tussen de zoekresultaten zoeken we naar de Terraform plugin van Microsoft DevLabs.

Deze installeren we binnen de organizatie door op Get it Free te klikken.

We zorgen dat de juiste DevOps organization geselecteerd is en klikken vervolgens op Install.

Zodra de installatie klaar is, klikken we op Proceed to Organization.

De plugin is nu geïnstalleerd en bruikbaar in ons project.

Voorbereiden terraform bestanden

Nu Terraform klaar is voor gebruik binnen het Project, gaan we nieuwe Terraform bestanden aanmaken. We openen Visual Studio Code en maken een nieuwe map waar we onze bestanden gaan plaatsen.

Onder de bestaande map source maken we een folder genaamd terraform aan. Binenn deze map maken we twee submappen genaamd network en virtualmachine aan.

In de map network creëren we twee bestanden genaamd network-main.tf en network-variables.tf. in het network-main.tf bestand plakken we onderstaande terraform code.

#Defineer de backend en provider
terraform {
  backend "azurerm" {
  }
}

provider "azurerm" {
    version = "~> 2.18"
    features {}
}

# Defineer een resourcegroup waar de netwerk componenten in komen. Maakt gebruik van 4 variabelen.
resource "azurerm_resource_group" "networkresourcegroup"{
    name            = var.networkresourcegroup
    location        = var.location
    tags = {
        purpose     = "Core Network"
        environment = var.environment
        version     = var.deploymentversion
    }
}

# Creëer een VNET binnen Azure. Deze refereert aan de network resource group voor de locatie.
resource "azurerm_virtual_network" "mainnetwork"{
    name                = "vnet-${var.environment}-${var.location}-001"
    location            = azurerm_resource_group.networkresourcegroup.location
    resource_group_name = azurerm_resource_group.networkresourcegroup.name
    address_space       = ["172.16.0.0/12"]
    dns_servers         = ["1.1.1.1", "1.0.0.1"]

    tags = {
        environment     = var.environment
        purpose         = "Core Network"
    }
}

# Creëer een drietal subnets en plaats deze in het VNET
resource "azurerm_subnet" "servers" {
  name                 = "snet-${var.environment}-servers"
  resource_group_name  = azurerm_resource_group.networkresourcegroup.name
  virtual_network_name = azurerm_virtual_network.mainnetwork.name
  address_prefixes       = ["172.16.0.0/24"]
}

resource "azurerm_subnet" "clients" {
  name                 = "snet-${var.environment}-clients"
  resource_group_name  = azurerm_resource_group.networkresourcegroup.name
  virtual_network_name = azurerm_virtual_network.mainnetwork.name
  address_prefixes       = ["172.16.1.0/24"]
}

resource "azurerm_subnet" "AzureFirewallSubnet" {
  name                 = "snet-${var.environment}-AzureFirewallSubnet"
  resource_group_name  = azurerm_resource_group.networkresourcegroup.name
  virtual_network_name = azurerm_virtual_network.mainnetwork.name
  address_prefixes       = ["172.31.254.0/26"]
}

# Creëer een NSG met 2 voorbeeld regels
resource "azurerm_network_security_group" "nsg" {
  name                = "serversnsg"
  location            = azurerm_resource_group.networkresourcegroup.location
  resource_group_name = azurerm_resource_group.networkresourcegroup.name

  security_rule {
    name                       = "primaire-block"
    priority                   = 200
    direction                  = "Inbound"
    access                     = "Deny"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name                       = "Allow-Website"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  tags = {
    environment     = var.environment
    purpose         = "Core Network"
  }
}

# Koppel de NSG aan het 'servers' subnet
resource "azurerm_subnet_network_security_group_association" "nsgservers" {
  subnet_id                 = azurerm_subnet.servers.id
  network_security_group_id = azurerm_network_security_group.nsg.id
}

In dit bestand maken we naast een resource group, ook een VNET met subnets aan. Hier kunnen we straks een virtuele machine in gaan plaatsen. Tot slot maken we een NSG aan en zetten we poort 80 (http) open. Deze NSG koppelen we aan het gehele servers subnet.

Het network-variables.tf bestand vullen we me de volgende gegevens.

variable "location"{
    type = string
}

variable "environment"{
    type = string
}

variable "deploymentversion"{
    type = string
}

variable "networkresourcegroup"{
    type = string
}

Beide bestanden slaan we op met ctrl + s.

Aanpassen pipeline

Voordat we de bestanden comitten naar Azure Repo, gaan we de pipeline aanpassen zodat de Terraform bestanden worden uitgevoerd. Binnen Visual Studio Code openen we onze pipeline yaml file en plakken onderstaande code.

          - task: TerraformInstaller@0
            displayName: 'Installing Terraform'
            inputs:
              terraformVersion: '0.12.28'
          - task: TerraformTaskV1@0
            displayName: 'Initialise Terraform'
            inputs:
              provider: 'azurerm'
              command: 'init'
              workingDirectory: '$(System.DefaultWorkingDirectory)/source/terraform/network'
              backendServiceArm: 'Azure Development'
              backendAzureRmResourceGroupName: '$(resourcegroupName)'
              backendAzureRmStorageAccountName: '$(storageAccountname)'
              backendAzureRmContainerName: '$(storagecontainer)'
              backendAzureRmKey: '$(networkstatekey)'
          - task: TerraformTaskV1@0
            displayName: 'Plan Terraform'
            inputs:
              provider: 'azurerm'
              command: 'plan'
              workingDirectory: '$(System.DefaultWorkingDirectory)/source/terraform/network'
              commandOptions: '-var "deploymentversion=$(BuildVersion)" -var "location=$(location)" -var "environment=$(environment)" -var "networkresourcegroup=$(networkresourcegroupname)"'
              environmentServiceNameAzureRM: 'Azure Deployment'

Let ook met het plakken goed op de indentie. Zorg dat alle – task op dezelfde “diepte” staan.

Met de Terraform files hebben we extra variabelen geïntroduceerd. Deze voegen we bovenaan de pipeline toe door onderstaande variabele te kopiëren.

  - name: storagecontainer
    value: 'terraform'
  - name: networkstatekey
    value: 'terraformnetworkstate'
  - name: environment
    value: 'dev'
  - name: 'networkresourcegroupname'
    value: 'rg-dev-network'

Sla het azure-pipelines.yml bestand op met de toetsencombinatie ctrl + s. Nu gaan we net als in deel 2 van het blog, de wijzigingen comitten naar de Repository. Een kleine reminder; We klikken op links op Source Control. en vervolgens op de + tekens achter de 3 aangepasten bestanden. tot slot klikken we bovenin op de “✓” knop voor commit.

Zodra dat is gedaan klikken we onderin op 0 ↓ 1 ↑ om te synchroniseren. De pipeline zal nu gaan lopen. Als we naar de details van de Terraform plan job kijken, Kunnen we zien welke resources er aangemaakt gaan worden.

Dit doen we door Binnen Azure DevOps te klikken op Pipelines en vervolgens op onze pipeline naam.

In het overzicht klikken we op het groene vinkje achter de laatste run om details te bekijken.

Links klikken we op Plan Terraform en scrollen omlaag. Hier zien we dat er 7 resources aangemaakt zullen worden. Aangezien we nu enkel een Plan hebben gedraaid, is dit nog niet gerealiseerd.

Wat we daarnaast ook hebben gedaan door de Terraform init te draaien, is een terraform statefile aangemaakt. Deze hebben we geplaatst in het storage account welke we in Deel 2 van het Blog hebben aangemaakt en hebben. Door in de terraform container te kijken via de Azure portal, zien we daar netjes een terraformnetworkstate.tf bestand staan.

Nu gaan we nog een extra stap toevoegen aan de pipeline. We openen Visual Studio Code en plakken de volgende code achter de laatste Terraform stap.

         - task: TerraformTaskV1@0
            name: terraformApply
            displayName: 'Apply Terraform'
            inputs:
              provider: 'azurerm'
              command: 'apply'
              workingDirectory: '$(System.DefaultWorkingDirectory)/source/terraform/network'
              commandOptions: '-var "deploymentversion=$(BuildVersion)" -var "location=$(location)" -var "environment=$(environment)" -var "networkresourcegroup=$(networkresourcegroupname)"'
              environmentServiceNameAzureRM: 'Azure Development'

Let ook hierbij goed op de indentie van de code.

We slaan het document op met ctrl + s en comitten deze naar de Repository. De pipeline gaat wederom lopen. We controleren of hij goed is uitgevoerd door de Terraform Plan stap te openen.

Volgens Azure DevOps zijn er 7 resources aangemaakt. Dat gaan we controleren via de Azure Portal. Hier zien we dat de resource group aanwezig is en zien we de VNET en NSG in deze resource group.

Klikken we door op de VNET en kiezen we subnets aan de linkerzijde, dan zien we daar ook de 3 subnets welke we hebben gedefinieerd.

Toevoegen Virtual Machine

Nu hebben het een netwerk aangemaakt. De volgende stap wordt het toevoegen van een virtuele machine, het installeren van een simpele webserver en deze server te koppelen aan een eerder aangemaakt server subnet.

Het koppelen aan het subnet brengt de eerste uitdaging met zich mee. Omdat we nu met twee verschillende Terraform files aan de slag gaan, kunnen we niet zomaar referenties gebruiken. We hebben het subnet ID nodig om de server aan te koppelen. Hiervoor gaan we gebruik maken van een output.tf bestand.

We openen Visual Studio Code, en maken in de source\terraform\network een nieuw bestand genaamd network-output.tf aan. Hier plaatsen we de volgende code in.

output "network-servers-subnet-id" {
  value       = azurerm_subnet.servers.id
  description = "This is the servers subnet ID"
}

Wat we hier in aangeven, is dat we de waarde van azurerm_subnet.servers.id als output meegeven in de variabele ‘network-servers-subnet-id. azurerm_subnet is in dit stuk de resource en servers de “lokale” naam binnen terraform. Id is in dit geval een waarde die we terugkrijgen bij het aanmaken van het subnet en is tegelijkertijd de waarde die we bij de virtuele machine nodig hebben.

Vervolgens maken we de volgende twee bestanden virtualmachine-main.tf en virtualmachine-variables.tf aan onder de map source\terraform\virtualmachine . In virtualmachine-main.tf plaatsen we de volgende code.

terraform {
  backend "azurerm" {
  }
}

provider "azurerm" {
    version = "~> 2.18"
    features {}
}

# Defineer een resourcegroup waar de virtuele machine in komt te staan. Bevat 4 variabelen.
resource "azurerm_resource_group" "vmresourcegroup"{
    name            = var.vmresourcegroup
    location        = var.location
    tags = {
        environment = var.environment
        version     = var.deploymentversion
        purpose     = "blog Website"
    }
}

# Creëer een public ip-address voor de VM.
resource "azurerm_public_ip" "publicip" {
    name                         = "vm-${var.environment}-pip"
    location                     = azurerm_resource_group.vmresourcegroup.location
    resource_group_name          = azurerm_resource_group.vmresourcegroup.name
    allocation_method            = "Dynamic"

    tags = {
        environment     = var.environment
        purpose         = "blog website"
    }
}

# Creëer een Netwerk interface en koppel het publieke ip-address.

resource "azurerm_network_interface" "vm1nic" {
    name                        = "vm-${var.environment}-nic"
    location                    = azurerm_resource_group.vmresourcegroup.location
    resource_group_name         = azurerm_resource_group.vmresourcegroup.name

    ip_configuration {
        name                          = "vm${var.environment}nicconfig"
        subnet_id                     = var.subnetId
        private_ip_address_allocation = "Dynamic"
        public_ip_address_id          = azurerm_public_ip.publicip.id
    }

    tags = {
        environment     = var.environment
        purpose         = "blog Website"
    }
}

resource "azurerm_virtual_machine" "vm" {
  name                  = "vm-${var.environment}-01"
  location              = azurerm_resource_group.vmresourcegroup.location
  resource_group_name   = azurerm_resource_group.vmresourcegroup.name
  network_interface_ids = [azurerm_network_interface.vm1nic.id]
  vm_size               = "Standard_DS1_v2"

  delete_os_disk_on_termination = true

  delete_data_disks_on_termination = true

  storage_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "16.04-LTS"
    version   = "latest"
  }
  storage_os_disk {
    name              = "vm-${var.environment}-01-osdisk"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }
  os_profile {
    computer_name  = "vm-${var.environment}-01"
    admin_username = "myadminuser"
    admin_password = var.password
  }
  os_profile_linux_config {
    disable_password_authentication = false
  }
  tags = {
        environment = var.environment
        version     = var.deploymentversion
  }
}

resource "azurerm_virtual_machine_extension" "nginx" {
  name                 = "nginx"
  virtual_machine_id   = azurerm_virtual_machine.vm.id
  publisher            = "Microsoft.Azure.Extensions"
  type                 = "CustomScript"
  type_handler_version = "2.0"

  settings = <<SETTINGS
    {
        "commandToExecute": "sudo apt-get -y install nginx && exit 0"
    }
SETTINGS
}

In dit Terraform bestand definiëren we een Virtuele machine op basis van Linux. We creëren een nieuwe resource group, een publiek IP-adres en een Netwerkkaart.

Het laatste blok (azurerm_virtual_machine_extension) creëert een custom extension op de virtuele machine. we geven hier het commando op om NGIX te installeren. NGINX is een webserver die we op o.a. Linux kunnen installeren.

Meer informatie over extensies kun je terugvinden op Microsoft website.

In het bestand virtualmachine-variables.tf plaatsen we de volgende code om alle benodigde variabelen te definiëren.

variable "location"{
    type = string
}

variable "environment"{
    type = string
}

variable "deploymentversion"{
    type = string
}

variable "vmresourcegroup"{
    type = string
}

variable "subnetId"{
    type = string
}

variable "password"{
    type = string
}

Nu gaan we de pipeline weer verder aanpassen. Open azure-pipelines.yml en voeg de volgende blokken code toe onder de laatste Terraform taak van de virtuele machine.

          - task: PowerShell@2
            displayName: 'Capture variables'
            inputs:
              targetType: 'inline'
              script: |
                $json = Get-Content $(terraformApply.jsonOutputVariablesPath) | Out-String | ConvertFrom-Json
                foreach($prop in $json.psobject.properties) {Write-Host("##vso[task.setvariable variable=$($prop.Name);]$($prop.Value.value)")}
          - task: TerraformTaskV1@0
            displayName: 'Initialise Terraform VM'
            inputs:
              provider: 'azurerm'
              command: 'init'
              workingDirectory: '$(System.DefaultWorkingDirectory)/source/terraform/virtualmachine'
              backendServiceArm: 'Azure Development'
              backendAzureRmResourceGroupName: '$(resourcegroupName)'
              backendAzureRmStorageAccountName: '$(storageAccountname)'
              backendAzureRmContainerName: '$(storagecontainer)'
              backendAzureRmKey: '$(vmstatekey)'
          - task: TerraformTaskV1@0
            displayName: 'Plan Terraform Virtual Machine'
            inputs:
              provider: 'azurerm'
              command: 'plan'
              workingDirectory: '$(System.DefaultWorkingDirectory)/source/terraform/virtualmachine'
              commandOptions: '-var "password=$(password)" -var "subnetId=$(network-servers-subnet-id)" -var "deploymentversion=$(BuildVersion)" -var "location=$(location)" -var "environment=$(environment)" -var "vmresourcegroup=$(vmresourcegroup)"'
              environmentServiceNameAzureRM: 'Azure Development'
          - task: TerraformTaskV1@0
            displayName: 'Apply Terraform Virtual Machine'
            inputs:
              provider: 'azurerm'
              command: 'apply'
              workingDirectory: '$(System.DefaultWorkingDirectory)/source/terraform/virtualmachine'
              commandOptions: '-var "password=$(password)" -var "subnetId=$(network-servers-subnet-id)" -var "deploymentversion=$(BuildVersion)" -var "location=$(location)" -var "environment=$(environment)" -var "vmresourcegroup=$(vmresourcegroup)"'
              environmentServiceNameAzureRM: 'Azure Development'

De powershell taak ‘capture variables’ zorgt ervoor dat de output van terraform omgezet wordt naar een variable die bruikbaar is binnen Azure DevOps. Het aangemaakte network-output.tf bestand wordt gebruikt door de Terraform Apply taak. Deze taak hebben we de naam ‘terraformApply’ gegeven. Hierin refereren we in de powershell code.

Deze Powershell code pakt alle properties in de output en gebruikt de optie Write-host om een Variable in de pipeline aan te maken of te vullen. In dit geval is dat de eerder aangemaakte azurerm_subnet.servers.id. Deze variabelen gebruiken we bij de virtuele machines om aan te geven wat het subnet id is. Dit doen we door -var “subnetId=$(network-servers-subnet-id)” te gebruiken als argument.

Voor meer informatie over variabelen kun je terecht op de Microsoft Docs website.

Bovenin de azure-pipelines.yml plaatsen we weer twee extra variabelen. Kopieer onderstaande code en plak deze onderaan de overige variabelen.

  - name: vmstatekey
    value: 'terraformvmstate'
  - name: vmresourcegroup
    value: 'rg-dev-virtualmachines'

We geven ook een wachtwoord mee aan de virtuele machine en doen dit door de variabele password te gebruiken. Deze kunnen we bovenin het bestand bij de andere variabele plaatsen, maar dan is het wachtwoord als tekst zichtbaar.

In plaats daarvan maken we een beveiligde variabele aan. Het systeem zorgt ervoor dat beveiligde variabelen niet zichtbaar zijn binnen Azure DevOps voor de gebruikers. Zowel in het overzicht van de variabelen als in de pipeline taak resultaten.

We Openen de Azure DevOps website, Gaan naar Pipelines en bewerken onze pipeline.

Binnen de pipeline klikken we rechts boven op variables.

We klikken op New Variable en vullen bij Name ‘password’ in. Onder value vullen we een eigen verzonnen wachtwoord in en vinken tot slot Keep this value secret aan.

Klik op Save om de nieuwe variabele op te slaan en in gebruik te nemen.

Nu we alles hebben klaarstaan keren we terug naar Visual Studio Code en zorgen we dat alle bestanden opgeslagen worden en comitten we deze naar de Azure DevOps repository.

Zoals ingesteld gaat de pipeline weer draaien. Hier kunnen we bij de stap Apply Terraform in de gaten houden waar we mee bezig zijn. Het aanmaken van een Virtuele machine en de extensie zal wat langer duren dat we bij de resource groepen en virtuele netwerken hebben gezien.

Zodra de pipeline klaar is keren we terug naar de Azure Portal. Hier openen we de nieuwe resourcegroep rg-dev-virtualmachines en controleren we of daar inderdaad een virtuele machine staat.

We testen of ook de NGINX website het doet. We klikken op de virtuele machine naam vm-dev-01. Op de volgende pagina zien we rechts boven het publieke IP-address dat is toegewezen. Klik achter op IP-adres op de Copy to Clipboard.

We openen een nieuw tabblad in de browser en plakken daar het gekopieerde IP-Adres. Als alles goed is gegaan, zien we hier de standaard pagina van NGINX.

Dat ziet er goed uit! Het netwerk en virtuele machine worden nu door Terraform middels een pipeline uitgerold.

Aanpassen release-pipeline

Net als in het tweede deel van het Blog gaan gebruik maken van een “ontwikkel” en “productie” omgeving. De “Productie” omgeving wordt aangemaakt/aangepast zodra we een Pull-request uitvoeren van de develop branch naar de master branch.

De pipeline is al zo ingesteld dat ook de terraform bestanden mee worden genomen als Artifact. We hoeven nu dus alleen de release pipeline aan te passen met de terraform stappen. Naast de Terraform stappen hebben we een paar extra variabelen nodig.

Binnen Azure DevOps gaan we naar pipelines en klikken we op releases. bij onze release pipeline klikken we op Edit om deze aan te passen.

We beginnen met het toevoegen van de extra variabelen. Deze voegen we toe door bovenin op Variables te klikken.

In dit scherm voegen we de volgende variabelen met bijpassende waarde toe;

  • environment; prd
  • storagecontainer; terraform
  • networkstatekey; terraformnetworkstate
  • vmstatekey; terraformvmstate
  • networkresourcegroupname; rg-$(environment)-network
  • vmresourcegroup; rg-$(environment)-virtualmachines
  • password; **een eigen wachtwoord**

Achter de variabele password klikken we op het slotje om deze privé te maken. tot slot klikken we bovenin op Save.

Nu we de variabelen hebben aangemaakt, gaan we extra stappen toevoegen aan de pipeline. We klikken bovenin op Tasks en vervolgens achter Agent Job op het Plus teken. Daarna zoeken we op Terraform en klikken we op Add achter de taak Terraform tool installer.

Als dat gedaan is klikken we zes keer op de Add knop achter de taak Terraform om deze toe te voegen. Tot slot zoeken we op Powershell en klikken op Add achter de taak Powershell.

Nu gaan we de taken verder instellen. We klikken op de taak Install Terraform 0.12.3 en wijzigen het veld Version naar 0.12.28. hierbij wordt automatisch de display name aangepast.

Vervolgens gaan we naar de tweede taak en stellen deze in. geef een displayname op en voer bij Configuration directory het volgende in;

$(System.DefaultWorkingDirectory)/Deployment/drop/terraform/network

We scrollen iets naar onder toe en selecteren bij de subscription onze Azure subscription welke we gebruiken. Achter resource group, storage account, container en key vullen we respectievelijk $(resourcegroup), $(storageaccount), $(storagecontainer) en $(networkstatekey) in.

Deze taak is ingesteld. we klikken nu op de volgende Terraform taak en vullen de benodigde velden in. We geven de taak een Display name en selecteren bij Command voor de optie Plan.

Bij Configuration directory vullen we hetzelfde pad in als bij de vorige Terraform stap. In het veld Additional command arguments” vullen de het volgende in;

-var "deploymentversion=$(Release.ReleaseName)" -var "location=$(location)" -var "environment=$(environment)" -var "networkresourcegroup=$(networkresourcegroupname)

Bij Azure subscription selecteren we wederom onze azure subscription.

We gaan door naar de volgende taak. Deze vullen we bijna identiek in als de vorige (Plan) taak, met uitzondering van de Display Name en dat bij Command kiezen voor Validate and Apply

Ook in de ze pipeline moeten we het subnet ID meegeven aan de terraform taak voor de virtuele machine. In de “Validate and Apply” taak scrollen we naar beneden en klappen we Output Variables uit.

In het veld reference name vullen we de naam output in. Noteer of kopieer de tweede variable die daar onder staat. output.jsonOutputVariablesPath

Vervolgens slepen we de toegevoegde Powershell taak naar boven zodat hij na de apply network Terraform taak komt te staan. We geven deze een Display name en selecteren onder Type voor Inline. In het kader Script vullen we de volgende code in.

$json = Get-Content $env:output | Out-String | ConvertFrom-Json

foreach($prop in $json.psobject.properties) {Write-Host("##vso[task.setvariable variable=$($prop.Name);]$($prop.Value.value)")}

We scrollen iets naar onder en openen Environment Variables. Hier voegen we een variable toe door op Add te klikken. Achter name vullen we output in en onder value het eerder genoteerde (output.jsonOutputVariablesPath)

De volgende taak wordt wederom een “Init” taak van Terraform. Deze vullen we in net als de vorige taken, alleen bij Configuration Directoy vullen we het volgende in;

$(System.DefaultWorkingDirectory)/Deployment/drop/terraform/virtualmachine

We scrollen iets naar onder en vullen de overige velden in net als bij de init Network taak. De uitzondering hier is dat achter het veld Key we nu $(vmstatekey) invullen.

En we gaan door naar de volgende taak. Deze is voor het command Plan. Bij configuration directory vullen we dezelfde waarde in als bij de vorige taak en bij command selecteren we plan.

Daarna voeren we bij Additional command arguments de onderstaande code in en bij Azure subscription kiezen we weer onze Azure subscription.

-var "password=$(password)" -var "subnetId=$(network-servers-subnet-id)" -var "deploymentversion=$(Release.ReleaseName)" -var "location=$(location)" -var "environment=$(environment)" -var "vmresourcegroup=$(vmresourcegroup)"

En de laatste taak is wederom een Apply and Validate taak. Vul bij deze taak dezelfde gegevens in als de vorige plan taak, met als uitzondering de Display Name en Command welke we op Apply and Validate zetten. Tot slot klikken we op “Save” om de pipeline op te slaan.

Nu alle taken zijn aangemaakt klikken we bovenin op Save om de pipeline op te slaan. In het vorige blog hebben we de Pipeline zo ingesteld dat deze automatisch start zodra er een Pull-Request (PR) naar Master wordt uitgevoerd.

We klikken links op Repos en vervolgens op Pull Requests. In het midden van het scherm klikken we op New pull request.

We stellen de PR in op “develop into master” door dit bovenin te selecteren. Bij Titel geven we een naam om van de PR en we klikken op Add commit messages om alle onze commits in de omschrijving te plaatsen.

We scrollen iets naar onder en klikken op Create om het aanmaken van de PR te voltooien.

Azure DevOps controleert of er conflicten optreden bij het samenvoegen van de branches. Zodra deze op no merge conflicts staat klikken we op Complete.

In het nieuwe venster klikken we op Complete Merge waarna deze voltooid is.

De release-pipeline zal ik dit geval automatisch starten. Net als de vorige keren kunnen we meekijken. Dit doen we door onder pipelines te klikken op Releases. Vervolgens kun je klikken op de “stages” knop achter de laatste release.

Afhankelijk in welke status deze zich bevindt, heeft deze een andere kleur en naam. in mijn geval is hij klaar en is de status deployment succeeded.

Elke stap kan aangeklikt worden om de individuele status in te zien.

De belangrijkste test is natuurlijk om te kijken of de resources daadwerkelijk zijn aangemaakt binnen Azure. Ook deze keer is dit netjes het geval.

Door weer het IP-Adres te kopiëren en te plakken in de browser, kunnen we controleren of ook de NGINX website werkt.

Als we vervolgens de storage account openen van de “productie” omgeving en in de terraform container kijken, zien we hier twee state files terug.

Deze stateifile gebruikt Terraform om de staat van de beheerde infrastructuur bij te houden. Een van de belangrijkste doelen van de state-file is om een “link” te leggen tussen je configuratie en de daadwerkelijke Azure resources.

Dat was het! We kunnen nu een stukje infrastructuur uitrollen via Infrastructure as Code door gebruik te maken van Azure DevOps Repositories, Pipelines, Powershell, ARM-Templates en Terraform!

Hopelijk heeft dit wat inzicht gegeven in de mogelijkheid en je eventueel een startpunt gegeven in het werken met Terraform en/of Azure DevOps.

Wil je meer te weten komen over Azure DevOps, lees deze blog of schrijf je in voor onze Azure DevOps Discovery workshop (voor starters). Ben je al bekend met Azure DevOps, dan kan je je inschrijven voor de Azure Boards Discovery workshop. Ben je benieuwd naar de andere onderwerpen waarover we schrijven, neem een kijkje in de kennisbank.

Opruimen van resources

Zoals eerder vermeld zitten er wat meer kosten aan het draaien van de Virtuele machines. We kunnen de omgeving eenvoudig opruimen door binnen een resource group te klikken op Delete Resource Group

Vervolgens typ je de naam in van de resource group en klik je op Delete

Verwijder eerst de resource groepen met daarin de virtuele machines voordat je de groep verwijderd met het VNET. Andersom zal niet lukken omdat de netwerkkaart van de virtuele machine hier gebruik van maakt.

Wil je je virtuele machines nog niet verwijderen, maar toch de kosten drukken. probeer dan door onderstaande Terraform code toe te voegen aan de oplossing, Auto shutdown in te stellen zodat de machines dagelijks uitgezet worden.

resource "azurerm_dev_test_global_vm_shutdown_schedule" "vmshutdown" {
  virtual_machine_id = azurerm_virtual_machine.vm.id
  location           = azurerm_resource_group.vmresourcegroup.location
  enabled            = true

  daily_recurrence_time = "2200"
  timezone              = "W. Europe Standard Time"

  notification_settings {
    enabled         = false
  }
}

Auteur: Léon Boers

Léon Boers is Microsoft cloud consultant bij 3fifty.

× Stel jouw vraag direct via Whatsapp Available from 09:00 to 17:00