Managing IIS With Puppet via Powershell DSC

Use Case:

Many of us in DevOps/Cloud/Site Reliability/System Engineering have long been using Puppet to manage our Apache and Nginx configurations. However it seems like every Windows deployment I come across using IIS is either not in any form of config management, or is managed through a series of VBScript and PowerShell scripts or System Center. If your organization is big enough to afford System Center this isn’t really much of an issue for you; however for those of us managing IIS web farms without SCCM, Microsoft has extended a nice olive branch with the delivery of PowerShell Desired State Configuration.

Given that most of us work with such broad toolsets, the idea of adding yet another configuration management platform into the mix seems less than ideal. However, there is an official Puppet Forge module that allows you to leverage PowerShell DSC within your Puppet code. What I found in my journey of doing this is that, while there is some decent documentation, there are pieces that clearly seem not to be well documented that required figuring it out the hard way. In this blog post I’ll cover and distill what I’ve learned in configuration of IIS through PowerShell DSC via Puppet.

Prerequisites:

This blog post will assume that you have already installed the PowerShell DSC forge module. I will link the module and its dependencies below; if you are unsure how to install Forge modules, see the documentation on the link to the module below.

This blog post will also assume that you have already installed IIS either in your AMI/Template or via Puppet. Great, now that that’s all out of the way, let’s get started.

 

Overview Example Class:

Before diving into the weeds in each section, I wanted to provide a code sample of a single application pool, website, virtual directory, and web application, complete with the DSC dependencies:

 

class webhosting::clientname::sites::dev {
 
dsc_windowsfeature{'iis':
  dsc_ensure => 'Present',
  dsc_name => 'Web-Server',
}
dsc_windowsfeature{'aspnet45':
  dsc_ensure => 'Present',
  dsc_name => 'Web-Asp-Net45',
}
dsc_windowsfeature{'iisscriptingtools':
  dsc_ensure => 'Present',
  dsc_name => 'Web-Scripting-Tools',
}
 
#App Pool Creation
dsc_xwebapppool{'dev.example.com':
  dsc_name => 'dev.example.com',
  dsc_ensure => 'Present',
  dsc_enable32bitapponwin64 => true,
  dsc_managedruntimeversion => 'v4.0',
  dsc_managedpipelinemode => 'Integrated',
  dsc_identitytype => 'ApplicationPoolIdentity',
  dsc_state => 'Started',
  require => Dsc_windowsfeature['iis'],
}
 
#Website Creation
dsc_xwebsite{'dev.example.com':
  dsc_ensure => 'Present',
  dsc_name => 'dev.example.com',
  dsc_state => 'Started',
  dsc_physicalpath => 'D:\\www\\dev.example.com\\webroot',
  dsc_applicationpool => 'dev.example.com',
  dsc_bindinginfo => [{
  ipaddress => '*',
    protocol => 'HTTP',
    port => 80,
    hostname => 'dev.example.com',
    },
    {
    ipaddress => '*',
    protocol => 'HTTPS',
    port => 443,
    hostname => 'dev.example.com',
    certificatethumbprint => '<certificatethumbprintgoeshere>',
    certificatestorename => 'WebHosting',
    }],
 require => Dsc_xwebapppool['dev.example.com'],
  }
 
#Web Applications
dsc_xwebapplication{'AwesomeApp':
  dsc_ensure => 'Present',
  dsc_name => '/AwesomeApp',
  dsc_website => 'dev.example.com',
  dsc_webapppool => 'dev.example.com',
  dsc_physicalpath => 'D:\\www\\dev.example.com\\webroot',
  require => Dsc_xwebsite['dev.example.com'],
  }
 
#Virtual directories
dsc_xwebvirtualdirectory{'AwesomeApp':
  dsc_ensure => 'Present',
  dsc_name => 'AwesomeApp',
  dsc_website => 'dev.example.com',
  dsc_webapplication => 'AwesomeApp',
  dsc_physicalpath => 'D:\\webapplicatons\\AwesomeApp',
  require => Dsc_xwebapplication['AwesomeApp'],
  }
}

DSC Windows Feature

Starting at the top and taking this a chunk at a time, we see after the opening Puppet class statement that there are several dsc_windows_feature statements. These statements install the PowerShell DSC packages on the endpoint nodes. PowerShell DSC wrapped in Puppet assumes that you are using the pull model style of using DSC. What essentially happens on the Puppet run is that your Puppet DSC code gets converted into PowerShell DSC code and run locally on the machine. DSC module can be found at https://gallery.technet.microsoft.com/scriptcenter. Note that modules beginning with an X are considered experimental. That said, most of the experimental modules in my experience have been stable.

 

Configuring The Rest

Beyond this point, the rest of the Puppet code you will see will be prefixed with dsc_. Essentially, to write DSC code in Puppet you will use the same values outlined in the DSC documentation, just prefixed with the dsc_ and using the Ruby hash rockets => instead of =. See the example below:

DSC Example:

    xWebsite DefaultSite
        {
            Ensure          = "Present"
            Name            = "Default Web Site"
            State           = "Stopped"
            PhysicalPath    = "C:\inetpub\wwwroot"
        }

Puppet Example:

     dsc_xWebsite{'DefaultSite':
            dsc_ensure       => 'Present',
            dsc_name         => 'Default Web Site',
            dsc_state        => 'Stopped',
            dsc_physicalpath => 'C:\\inetpub\\wwwroot',
        }

At this point, you can configure your applications specific to your environments. Much like any other Puppet code there are obvious dependencies (can’t create a website that depends on an application pool unless the pool already exists). For this you can use the normal Puppet meta parameters such as before and require statements to resolve dependencies. For IIS-specific DSC parameters see the readme for Microsoft’s XWebAdministration module here:  https://github.com/PowerShell/xWebAdministration

 

Don’t forget to prefix the parameters in the readme with dsc_ when converting to Puppet code. Happy configuring and Puppeting! Until next time, may your servers always be up and your coffee mugs never empty!

VS Code!

Ever since graduating from a Systems Admin role to DevOps one of the most frequently used tools in my toolbag is the text/code editor. Like many people I started on Notepad++ which by its own right is still a legitimately good editor. However working back and forth between Mac, Linux, and Windows desktop environments I gravitated towards Sublime Text and stayed there for a long time. I’ve recently begun studying for the MCSA Linux on Azure certification and have begun poking at Visual Studio more and in doing so discovered VS Code. VS code is a beautiful open source editor with a ton of plugins and a nice UI with excellent choices in syntax highlighting. Here’s some of the selling points that caused me to jump over to it:

 

  • Dark Themes
  • Cross Platform Support (Linux, Mac, and Windows)
  • options to add “code” to your path
  • automatic detection of files in a git repo and change tracking
  • useful diff screens
  • ability to launch terminal from within the editor window
  • Chef Support
  • Puppet Support
  • Powershell Support
  • Folder tree management
  • Syntax highlighting that’s helpful without being color vomit
  • Integrations with Azure WebApps PaaS
  • Key bindings for Vim, Sublime and other familiar editors

 

Here’s a Link to download VS Code: https://code.visualstudio.com/

 

 

Uploading Hyper-V VHDs to Azure

I recently had a project I was working on in which some inherited Azure VMs were missing the Azure agent and nobody knew the passwords for them. After a quick support call to MS it became apparent I would have to delete the VMs and preserve the disks, download disks, load into hyper-v and manually reset the password. In doing this I learned a few gotchas such as not being able to convert the VHD into a bootable Azure disk unless uploading through the Azure Powershell CLI. Here’s my quick how to upload disks guide.

Installing Azure Powershell

First we will need to install the Azure Powershell Module. This can be accomplished by running Powershell as administrator and entering the following:

Install-Module Azure

When prompted press choose A for yes to all. 

Note: If there is any conflict you may need to add the -AllowClobber to the end of the command above.

 

Login To Azure and Get Publish Settings File

First you will need to log into Azure by entering the following in Powershell

Add-AzureAccount

At this point you will be prompted to log into Azure. 

 

The next step will be to get an Azure Publishing Settings file. You can do this by entering the cmdlet below and then importing the file with the following cmdlet:

Get-AzurePublishSettingsFile

Import-AzurePublishSettingsFile -PublishSettingsFile “<path to file>”

 

Select Your Subscription and view Storage Accounts

At this step we will choose which subscription to use (if you have more than one) and list storage accounts so that we know where to upload the disks to. 

Warning: If you attempt to upload the VHD through the web GUI instead of using this method it will be created as a block blob not a page blob which prevents you from being able to convert it to a bootable disk for use in the gallery. The only way to do this correctly at the time of this writing is through the Powershell API.

Get-AzureSubscription

Select-AzureSubscription -SubscriptionId <enter yours here>

Get-AzureStorageAccount 

 

Uploading The Azure VHD and Converting It

At this point we are setup for the part we’ve all been waiting for. Make sure your VHD is not thin provisioned and that the VM has the Azure Agent installed and has been sys prepped (if using as a template).

Add-AzureVhd -LocalFilePath “<file path to your VHD>” -Destination “<URL of storage location with your filename after the last />”

This will create an MD5 hash and upload the disk. 

 

To convert the disk we will want to run the following:

Add-AzureDisk -Diskname ‘<name your disk something relevant>’ -MediaLocation ‘<URL where your disk lives in azure storage>’ -Label ‘<label>’ -OS <Choose Windows or Linux>