• 4 min read

Turning on Windows features using Powershell DSC extension and Azure CLI

This post shows a simple way of installing IIS on Azure VMs using Powershell DSC and Azure CLI

DevOps engineers who want to use custom script extensions on Azure have a variety of techniques available, such as using Desired State Configuration (DSC) or custom Powershell scripts. But what options do you have if you want to perform a simple task such as installing IIS Server on existing Windows Virtual Machines in an Azure Resource Group? You have a variety of possible approaches varying in complexity – such as using ARM templates, Azure Automation DSC and other methods. But, in some cases, a script may be a better approach – especially instances where you’re not setting up a production level infrastructure that needs to be maintained and updated.

For this latter scenario, DSC automation using a third party approach such as Chef, Puppet – or internal Azure tools, e.g. Azure automation, may serve your needs better. These solutions employ a server-based pull model for managing the desired configuration state of installed software on an ongoing basis.

My scenario is quite straightforward, and virtually a one-time configuration task that I’m looking to accomplish with minimum of fuss. Here’s the scenario:

  • A few virtual machines are deployed in an Azure Resource Group, using a CLI script.
  • Virtual machines are part of a three-tier architecture that includes web tier, business tier and a database tier.
  • Network Security Group (NSG) rules are provisioned by the script that allow/deny communication across tiers based on prerequisites.
  • Web tier virtual machines need an IIS server to allow testing and validating access to business tier via HTTP requests.
  • Virtual machines are provisioned following a recommended naming convention for Azure resources that allows easy access via automation.​

NOTE: Though the scenario in this post is limited to installation of IIS infrastructure on a set of virtual machines, with this approach it's possible to accomplish several other WMF based tasks such as setting up a domain controller, a DNS server, a SQL server, a failover cluster (WSFC) and so on. Similarly DSC extensions can be used on Linux based virtual machines.

Now that we understand the scenario, let’s look at how we can accomplish this without traversing through flaming hoops! Below is a quick screenshot of my Azure Resource Group:

VMsInRG

From the screenshot above, a couple of points may be noted:

  • Virtual machines follow a naming pattern e.g. bc-svc2-vm1, bc-svc2-vm2 and so on.
  • Resource group is named bc-dev-rg.

Here are the steps I followed to install IIS on the virtual machines shown in the above screenshot:

  • Create a Powershell file with the following content and save it as IISConfig.ps1:
Configuration ConfigureWeb
{ 
 node ("localhost")
 { 
  WindowsFeature InstallWebServer 
  { 
   Ensure = "Present"
   Name = "Web-Server" 
  } 
 } 
}
  • Package and publish the module to a publically accessible blob container URL using the following steps:
    • Open a Powershell x861 command window in administrative mode.
    • Login to your Azure account and select your subscription.
      Login-AzureRmAccount
      Select-AzureRmSubscription –SubscriptionId my-subscription-id
    • Publish2 the script to your storage account. Following command packages the script into  IISConfig.zip file and publishes to the storage container under given storage account.
Publish-AzureRmVMDscConfiguration -ConfigurationPath .IISConfig.ps1 -ContainerName mycontainer -StorageAccountName mystorageaccount -ResourceGroupName myresourcegroup
  • Create a text file with the following content and save it as TurnOnIIS.cmd:
@ECHO OFF

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Set up variables.
:: Change these variables to match your deployment.
SET APP_NAME=bc
SET ENVIRONMENT=dev
SET RESOURCE_GROUP=%APP_NAME%-%ENVIRONMENT%-rg

:: Number of Virtual Machines (VMs) to configure. Set according to your scenario.
SET NUM_VMS=3

:: Loop through all the VMs and call subroutine that installs IIS on each VM.
:: Loop counter and the service tier name are passed as parameters.
FOR /L %%I IN (1,1,%NUM_VMS%) DO CALL :ConfigureIIS %%I svc2

GOTO :eof

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Subroutine that configures IIS

:ConfigureIIS
SET TIER_NAME=%2
SET VM_NAME=%APP_NAME%-%TIER_NAME%-vm%1

ECHO Turning on IIS configuration for: %VM_NAME% under resource group: %RESOURCE_GROUP%

:: Following assumes that you have
::  1. Logged into your Azure subscription using "azure login"
::  2. Set the active subscription using "azure account set "

CALL azure vm extension set --resource-group %RESOURCE_GROUP% --vm-name %VM_NAME% ^
 --name DSC --publisher-name Microsoft.Powershell --version 2.9 ^
 --public-config "{"ModulesUrl": "https://mystorage.blob.core.windows.net/scripts/IISConfig.ps1.zip", "ConfigurationFunction": "IISConfig.ps1ConfigureWeb" }"
 
GOTO :eof

Basically all we’re doing is:

  1. Iterating through the VMs based on the given naming convention
  2. Calling a subroutine ConfigureIIS on each VM
  3. Invoking azure vm extension set cmdlet on each VM passing in the following parameters:
--resource-group      Name of the resource group    
--vm-name             Name of the virtual machine    
--name                Type of the custom script extension    
--publisher-name      Name of the extension publisher    
--version             Version of the extension    
--public-config       JSON text specifying 
                      ModuleUrl    Packaged configuration location
                      ConfigurationFunction    Name of the configuration function in the script including path
  • Open a command prompt, CD to the folder containing the command script and run it.

Similar approach could be used to turn on or off any of the Windows features on your virtual machines . For details on writing a DSC configuration to manipulate multiple Windows features simultaneously, please check this article.


1 Do not use x64 build of Powershell since the target virtual machines use the x86 build of Powershell by default. Failure to comply with this leads to:

  • Failure of publishing process to Azure storage container with a vague message such as Resource group not found though the resource group does exists!
  • Failure of unzipping process on the target virtual machines which is really hard to debug since the error message is least helpful!

2 An alternative method involves creating the package locally and uploading it to a GitHub account. In this case you substitute the storage container URL with a publically accessible GitHub URL. Below is the modified command to use in this case:

Publish-AzureRmVMDscConfiguration -ConfigurationPath .IISConfig.ps1 -OutputArchivePath IISConfig.zip