Terraform 101

Files

  • main.tf - resource definition, main logic
  • data.tf - data from existing resources
  • output.tf - data output
  • terraform.tf - terraform settings
  • variables.tf - variables definition
  • terraform.tfvars and *.auto.tfvars - variable initialization

Variables

  • Official documentation page
  • Can be defined in files like terraform.tfvars or *.auto.tfvars
  • You can use any name for tfvars file by using -var-file option of terraform apply
  • Variables can be passed directly to terraform apply by using -var "varaible=value" (multiple instances of -var can be used to pass multiple variables)
  • Any environment variable which starts with TF_VAR_ gets treated by Terraform like a variable

Syntax

# String variable with validation
variable "string_variable" {
    type        = string
    description = "String variable is a sequence of Unicode characters"
    validation  {
        condition = length(regexall("^(Expected|Values)", var.variable_name)) == 1
        error_message = "Variable must be set with expected value"
    }
}
# Bool variable
variable "bool_variable" {
    type        = bool
    description = "Bool variable either true or false and values can be used in conditional logic"
    default     = true
}
# Number variable
variable "number_variable" {
    type        = number
    description = "Number variable type can represent both whole and fractional"
}
# Number variable
variable "list_variable" {
    type        = list(<type>)
    description = "List variable accepts any element type as long as every element is the same type"
}
# Map variable
variable "map_variable" {
    type        = map
    description = "Map variable a collection of values where each is identified by a string label"
    default     = {
        key1    = "value"
        key2    = 43
        key3    = ("1","2","3")
        key4    = {
            subkey1 = "sample"
            subkey2 = "value"
        }
    }
}

Local variables

# Local variables
# To use local variable you have to prefix its name with local. instead of var. e.g. local.local_string_variable
locals {
  local_string_variable = "test"
}

Conditionals

# CONDITION ? TRUEVAL : FALSEVAL

# Operators:
#  - Equality: == and !=
#  - Comparison: >, <, >= and <=
#  - Logic: &&, || and unary !

# Set resource option value based on variable value 
resource "sample_resource" "this" {
    option = var.test == "value" ? 1 : 0
}

# Conditional resource creation
resource "another_sample_resource" "this" {
    count = var.create_another_sample_resource ? 1 : 0
}

Add KeyVault certificate to VMSS

$KeyVaultName = "KeyVaultName"
$CertificateName = "CertificateName"
$VMSSName = "VMSSName"
$VMSSResourceGroupName = "VMSSResourceGroupName"


$KeyVault = Get-AzKeyVault -VaultName $KeyVaultName
$KeyVaultCertificate = Get-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertificateName
$VMSS = Get-AzVmss -VMScaleSetName $VMSSName -ResourceGroupName $VMSSResourceGroupName

$SourceVaultAlreadyExist = $false
$CertificateAlreadyExist = $false

$VMSS.VirtualMachineProfile.OsProfile.Secrets | % {
    if ($_.SourceVault.Id -eq $KeyVault.ResourceId) {
        $SourceVaultAlreadyExist = $true
        $SourceVaultId = $VMSS.VirtualMachineProfile.OsProfile.Secrets.IndexOf($_)

        $_.VaultCertificates | % {
            if ($_.CertificateUrl -eq $KeyVaultCertificate.SecretId) {
                $CertificateAlreadyExist = $true
            }
        }

        if (!$CertificateAlreadyExist) {
            $Certificate = New-Object Microsoft.Azure.Management.Compute.Models.VaultCertificate
            $Certificate.CertificateStore = "My"
            $Certificate.CertificateUrl = $KeyVaultCertificate.SecretId
            $VMSS.VirtualMachineProfile.OsProfile.Secrets[$SourceVaultId].VaultCertificates.Add($Certificate)
        }
    }
}

if (!$SourceVaultAlreadyExist -and !$CertificateAlreadyExist) {
    $Secrets = New-Object System.Collections.Generic.List[Microsoft.Azure.Management.Compute.Models.VaultSecretGroup]
    $Secret = New-Object Microsoft.Azure.Management.Compute.Models.VaultSecretGroup
    $Secret.SourceVault = $KeyVault.ResourceId
    $Certificates = New-Object System.Collections.Generic.List[Microsoft.Azure.Management.Compute.Models.VaultCertificate]
    $Certificate = New-Object Microsoft.Azure.Management.Compute.Models.VaultCertificate
    $Certificate.CertificateStore = "My"
    $Certificate.CertificateUrl = $KeyVaultCertificate.SecretId
    $Certificates.Add($Certificate)
    $Secret.VaultCertificates = $Certificates
    $Secrets.Add($Secret)
    $VMSS.VirtualMachineProfile.OsProfile.Secrets = $Secrets
}

$VMSS.VirtualMachineProfile.OsProfile.Secrets | ConvertTo-Json -Depth 10
$VMSS | Update-AzVmss

Kubernetes 101

Tools to install kubernetes cluster

Useful links


Kudu zip deployment

$SiteName = "webapp"
$SitePassword = "You can get it from publish profile"
$ApiUrl = "https://$SiteName.scm.azurewebsites.de/api/zipdeploy"
$ArchivePath = "WebApp.zip"

$Base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "`$$SiteName", $SitePassword)))
$Headers = @{Authorization=("Basic {0}" -f $Base64)}
Invoke-RestMethod -Uri $ApiUrl `
                  -Headers $Headers `
                  -UserAgent "powershell/1.0" `
                  -Method POST -InFile $ArchivePath `
                  -ContentType "multipart/form-data"

Git pull vs fetch

git pull = git fetch + git merge


Find Azure resource across all subscriptions

Get-AzSubscription | % {
  Select-AzSubscription $_
  Get-AzResource | Where-Object {$_.Name -match "<resource name>"} | Select-Object *
}

Load Azure PowersShell on the fly

Sometimes installing modules on the system is not an option. E.g. build servers or any kind of machines where you want to run non-standard cmdlet but you don't want to (or not allowed to) install anything.

In order to do it you need three files.

load.ps1 - does the heavy lifting. Just look at the script. Everything works pretty straight forward.

$modulesPath = ".\modules"

# Cleanup
Remove-Module Az*
Remove-Item -Path $modulesPath -Force -Recurse

# Download nuget.exe in case you don't have it
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"

# Restore packages from PowerShell Gallery
& ".\nuget.exe" "restore" "packages.config" "-packagesdirectory" $modulesPath

# Remove version portion from each package folder (otherwise system will not recognize modules)
Get-ChildItem $modulesPath -Directory | % { 
    Rename-Item -Path $_.FullName -NewName $($_.Name -replace "\.\d+\.\d+\.\d+","") 
}

# Add our module folder to the system modules path
$env:PSModulePath += ";$(Resolve-Path -Path $modulesPath)"

# Import Az module
Import-Module Az

nuget.config - defines package source. By default nuget.exe will use nuget.org or any other repository defined on a user/system level. The chances that it already has PowerShell Gallery in it is pretty low (at least in the environments I work with).

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
    <add key="powershellgallery.com" value="https://www.powershellgallery.com/api/v2/" protocolVersion="2" />
</packageSources>
<config>
    <add key="signatureValidationMode" value="Accept" />
</config>
</configuration>

packages.config - holds the list of packages. In our case we're getting Az 2.8.0 but it can be adjusted to download virtually any number/kind of modules (don't forget to add Import-Module statements for any other module(s) you add).

<?xml version="1.0" encoding="utf-8"?>
<packages>
    <package id="Az" version="2.8.0" />
    <package id="Az.Accounts" version="1.6.4" />
    <package id="Az.Advisor" version="1.0.1" />
    <package id="Az.Aks" version="1.0.2" />
    <package id="Az.AnalysisServices" version="1.1.1" />
    <package id="Az.ApiManagement" version="1.3.2" />
    <package id="Az.ApplicationInsights" version="1.0.2" />
    <package id="Az.Automation" version="1.3.4" />
    <package id="Az.Batch" version="1.1.2" />
    <package id="Az.Billing" version="1.0.1" />
    <package id="Az.Cdn" version="1.3.1" />
    <package id="Az.CognitiveServices" version="1.2.1" />
    <package id="Az.Compute" version="2.7.0" />
    <package id="Az.ContainerInstance" version="1.0.1" />
    <package id="Az.ContainerRegistry" version="1.1.0" />
    <package id="Az.DataFactory" version="1.4.0" />
    <package id="Az.DataLakeAnalytics" version="1.0.1" />
    <package id="Az.DataLakeStore" version="1.2.3" />
    <package id="Az.DeploymentManager" version="1.0.1" />
    <package id="Az.DevTestLabs" version="1.0.0" />
    <package id="Az.Dns" version="1.1.1" />
    <package id="Az.EventGrid" version="1.2.2" />
    <package id="Az.EventHub" version="1.4.0" />
    <package id="Az.FrontDoor" version="1.1.1" />
    <package id="Az.HDInsight" version="2.0.2" />
    <package id="Az.HealthcareApis" version="1.0.0" />
    <package id="Az.IotHub" version="1.3.1" />
    <package id="Az.KeyVault" version="1.3.1" />
    <package id="Az.LogicApp" version="1.3.1" />
    <package id="Az.MachineLearning" version="1.1.1" />
    <package id="Az.ManagedServices" version="1.0.1" />
    <package id="Az.MarketplaceOrdering" version="1.0.1" />
    <package id="Az.Media" version="1.1.0" />
    <package id="Az.Monitor" version="1.4.0" />
    <package id="Az.Network" version="1.15.0" />
    <package id="Az.NotificationHubs" version="1.1.0" />
    <package id="Az.OperationalInsights" version="1.3.3" />
    <package id="Az.PolicyInsights" version="1.1.3" />
    <package id="Az.PowerBIEmbedded" version="1.1.0" />
    <package id="Az.RecoveryServices" version="1.4.5" />
    <package id="Az.RedisCache" version="1.1.1" />
    <package id="Az.Relay" version="1.0.2" />
    <package id="Az.Resources" version="1.7.0" />
    <package id="Az.ServiceBus" version="1.4.0" />
    <package id="Az.ServiceFabric" version="1.2.0" />
    <package id="Az.SignalR" version="1.1.0" />
    <package id="Az.Sql" version="1.15.0" />
    <package id="Az.Storage" version="1.8.0" />
    <package id="Az.StorageSync" version="1.2.1" />
    <package id="Az.StreamAnalytics" version="1.0.0" />
    <package id="Az.TrafficManager" version="1.0.2" />
    <package id="Az.Websites" version="1.5.0" />
</packages>

Git cheatsheet

Update existing commit

git commit --amend --no-edit
git push --force 

Remove all local changes

git clean -fdx

Mirror repositories

git clone --mirror <source.repository>
cd source.repository.git
git push --mirror <target.repository>

Update submodules

git submodule update --init --recursive --progress

Remove last commit

git reset --soft HEAD~1
git reset --hard HEAD~1

Reset file

git checkout -- <path>

Merge changes as a single commit

git merge --squash <source branch>
git commit --message "merge commit message"

Get remote changes and apply local changes

git stash
git pull --rebase origin <source branch>
git stash pop