IaC Journey Pit-stop (Declarative vs Imperative)

Because everyone kept saying <insert code language> is declarative. Might not even be self explanatory for a native English speaker either, so I went searching to really understand what everyone mean by all the focus on code being declarative. Compared to imperative.

Here are two quotes trying to describe the difference in declarative and imperative code:

Declarative code describe what result you want.

Imperative code describe how you want to get the result.

The declarative approach is just saying we want a table for two, while the imperative approach to getting a table is telling the restaurant every step we take to get to the table we want.

It takes a lot more code using the imperative approach, but don’t I have to do that in order for a stupid computer to understand? How can the computer understand what to do if I only tell it what I want?

For now declarative is understandable to me as human, because I can see what I want and just walk to the table. But I don’t code for humans, the code is for computers and it seems the imperative approach is what a computer would understand.

Declarative code describes it to a human.
Imperative code describes it to a computer.

The thing is, we can’t only use declarative code. We have to have imperative code for computers, but we can hide the complex imperative code behind APIs. So when we write our declarative code, it has references to underlaying imperative code for the computers to understand. This is how a code language can be declarative or only imperative.

Here we describe how the computer should act when we push the button (btn).

But if the code language was declarative, we would just reference a button in our code.

Then someone needs to write behind the scenes, in order for a code language to have the imperative code we can reference. Or else the computer will not understand our simple declarative code.

Take for instance Bicep, a code language, for deploying infrastructure as code to Microsoft Azure. It is installed in your preferred terminal with necessary imperative code, which we can reference using simple declarative code.

This makes it so much easier for us to write the code, using short and not so complex code for deploying to Microsoft Azure.

Even if Azure has declarative code in ARM Templates (JSON), Bicep makes it even. You could call it super declarative compared to ARM Templates. But because ARM (for now) understand its own template written i JSON, the local Bicep engine will translate our Bicep code to ARM Template when you create an Azure deployment using your favorite terminal.

PowerShell Imperative vs Declarative

PowerShell is a highly used tool among Sysadmins or IT Pros, and have been available for deploying infrastructure to Azure for the longest. PowerShell for Azure was imperative in the classic days, but also during the time where we had the AzureRM module.

Here is the code necessary to create a new virtual machine in Azure using AzureRM.

$VMLocalAdminUser = "LocalAdminUser"
$VMLocalAdminSecurePassword = ConvertTo-SecureString <password> -AsPlainText -Force
$LocationName = "westus"
$ResourceGroupName = "MyResourceGroup"
$ComputerName = "MyVM"
$VMName = "MyVM"
$VMSize = "Standard_DS3"

$NetworkName = "MyNet"
$NICName = "MyNIC"
$SubnetName = "MySubnet"
$SubnetAddressPrefix = ""
$VnetAddressPrefix = ""

$SingleSubnet = New-AzureRmVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $SubnetAddressPrefix
$Vnet = New-AzureRmVirtualNetwork -Name $NetworkName -ResourceGroupName $ResourceGroupName -Location $LocationName -AddressPrefix $VnetAddressPrefix -Subnet $SingleSubnet
$NIC = New-AzureRmNetworkInterface -Name $NICName -ResourceGroupName $ResourceGroupName -Location $LocationName -SubnetId $Vnet.Subnets[0].Id

$Credential = New-Object System.Management.Automation.PSCredential ($VMLocalAdminUser, $VMLocalAdminSecurePassword);

$VirtualMachine = New-AzureRmVMConfig -VMName $VMName -VMSize $VMSize
$VirtualMachine = Set-AzureRmVMOperatingSystem -VM $VirtualMachine -Windows -ComputerName $ComputerName -Credential $Credential -ProvisionVMAgent -EnableAutoUpdate
$VirtualMachine = Add-AzureRmVMNetworkInterface -VM $VirtualMachine -Id $NIC.Id
$VirtualMachine = Set-AzureRmVMSourceImage -VM $VirtualMachine -PublisherName 'MicrosoftWindowsServer' -Offer 'WindowsServer' -Skus '2012-R2-Datacenter' -Version latest

New-AzureRmVM -ResourceGroupName $ResourceGroupName -Location $LocationName -VM $VirtualMachine -Verbose

There was a lot of parameters, variables and the need to create dependencies in the right order. Thats how Azure Classic and AzureRM would understand and be able to deploy a virtual machine.

Today we are transitioning to use the new Az-module, which has inherited declarative options, due to Azure developing Infrastructure as Code (JSON, Bicep, etc). We can now use PowerShell to tell Azure we just want a virtual machine. Without any variables, it will query you for the necessary input or you can provide it like this:

New-AzVm `
    -ResourceGroupName 'myResourceGroup' `
    -Name 'myVM' `
    -Location 'East US' `
    -VirtualNetworkName 'myVnet' `
    -SubnetName 'mySubnet' `
    -SecurityGroupName 'myNetworkSecurityGroup' `
    -PublicIpAddressName 'myPublicIpAddress' `
    -OpenPorts 80,3389

We don’t have to tell Azure to create each single component in the correct order of creation, and in the end tell Azure to connect them all togethere when creating the VM. It is developed further to be easy for us to write deployments.

There is also PowerShell DSC (Desired State Configuration), which can declaratively tell how we like the inside of Windows to be configured. Its not regular PS and has the additional behind the scene code to let us write declaratively.

Now we also find this maturing inside the AZ-module for PS by sending invoke-commands through the Azure VM agent installed on Azure VMs:

Invoke-AzVMRunCommand `
    -ResourceGroupName 'myResourceGroup' `
    -VMName 'myVM' `
    -CommandId 'RunPowerShellScript' `
    -ScriptString 'Install-WindowsFeature `
    -Name Web-Server -IncludeManagementTools'

There difference between PowerShell DSC and PowerShell with Az-module is DSC will always enforce it configuration back to desired state, if your where to make a change. Using the Az-module in PowerShell will only set it to desired state once.

PowerShell = IaC ?

Using PowerShell this way shows its declarative, when using modules that interact with declarative services like Azure Resource Manager. But it doesn’t scale very well, hence I recommend using Bicep to deploy infrastructure as code in Azure.

Why Bicep scale better and have other functions to streamline your deployments, lets discuss that in a future post. But if you deploy the same VM twice in PoweShell, the second time will fail because it already exist. Using IaC like Bicep will update the VM, more like a desired state configuration does.

I hope this helped you too. If not, feel free to reach out to me through social media and we can look at it togethere.

Creds to the video I found most useful, in which describes it this way: https://www.youtube.com/watch?v=E7Fbf7R3x6I&ab_channel=uidotdev

Leave a Reply


I am Roy Apalnes, a Microsoft Cloud Evangelist working av Sopra Steria. Main focus in Microsoft Security and Endpoint Management, with a bigger picture in mind.

Featured Posts