In the two previous posts, I introduced PowerShell Desired State Configuration (DSC) and the kinds of use cases that its suited to. Post #2 covered setup and configuration of the DSC Server and how target nodes retrieve their configuration. This post will now cover the nuts and bolts of the actual Powershell configuration file (script) that defines the Desired State. I’ve tackled this by picking a variety of common scenarios that IT pros come across, and for each show an example of the configuration script. In addition, as a “catch-all”, I also show a couple of “Script” resources, which basically allow you to implement your own custom DSC resource directly in the configuration file. (ie. without building a custom DSC Resource module, and distributing it as necessary).
These are the scenarios covered in this article:
⦁ Roles and Features – ensure that specific Windows roles and features are present or absent on the node
⦁ Firewall settings – disable/enable the windows firewall
⦁ Timezone – ensure the node’s timezone setting is set to a specific zone
⦁ Remote Access Configuration – ensure the node can be accessed from a Remote Desktop Connection
⦁ Ensure pre-requisite updates, support packs or modules (MSU, MSI, EXE based distributables) are present on the node.
⦁ Ensure SQL Server and specific features are available (eg. client tools, management studio)
⦁ Associate file extensions with specific file types and executables – to ensure the shell will launch the correct executable for an open command
⦁ Confguration state of IE Enhanced Security Configuration (IE ESC) for users and administrators
⦁ Configuration of IIS Web hosting, app pools, web sites, and so on.
Before diving into the DSCResources themselves, an explanation of how to determine what configuration applies to what node. There are several ways to do this – we’re basically just performing some kind of a logic switch to determine if a block of the script is included or not for a given node. The nodes are defined in our environmentConfig.psd1 data file:
@{ AllNodes = @( @{ NodeName = "S1"; Role = "PullServer" }, @{ NodeName = "APPSRV1"; Role = "AppServer" }, @{ NodeName = "SQLSRV1"; Role = "SQLServer" } @{ NodeName = "APPSQL1"; Role = "SQLServer, AppServer" } ); NonNodeData = @{ PackagePath = "\\s1\dscsmb"; TempPath = "c:\windows\temp" } }
The above defines four server configurations – note that the above works well for PUSH configuration mode, where we know what the Node name is, but we can also use for PULL configuration, but instead of considering NodeName to be that of a specific Server, you should consider it that of a specific server type. When building PULL configurations, we would rename the server type to use a GUID, and then assign that GUID to all the machine’s Local Configuration Management Config, so they know what configuration type to request during the pull operations.
Now we have an array of nodes (called AllNodes), each of which has a NodeName and a Role. The NodeName will determine the configuration’s output filename, the role we will use in our logic flow to output the right kind of configuration for that role type. In the main powershell configuration script, when defining a specific Node, we can now use the AllNodes array to repeat the Node output for each Node based on a filter we define. In this case, I want to define three different sections – one that will produce output relevant to all role types, one for SQLServers and one for AppServers. Note that APPSQL1 will be a combination of the all three (ie. it matches All nodes, SQLServer and AppServer so all three sections will apply).
The switch is carried out using the Node section, filtering the nodes and selecting their Name to build the configuration:
Node $AllNodes.NodeName {}
Selects all Nodes (no filter)
Node $AllNodes.Where { $_Role -contains "AppServer"}.NodeName
Selects nodes where the Role property contains the phrase AppServer
Roles and Features
These are simply a matter of identifying the relevant feature and adding to the correct node (All, SQL or AppServer). This is an easy one, because there is a built in DSC Resource for managing Windows Features, called unsurprisingly, WindowsFeature. For example, to ensure that IIS is present on the target server, the configuration requires
WindowsFeature IIS { Ensure = "Present" Name = "Web-Server" }
By default, this will leave the Feature implementation to decide what sub-features will be installed, but you can also control this, by using the keyword “IncludeAllSubFeature”, and setting to $True or $False, as per below, where HealthAndDiags is a SubFeature of IIS.
WindowsFeature IIS { Ensure = "Present" Name = "Web-Server" } WindowsFeature HealthAndDiags { Ensure = "Present" Name="Web-Health" IncludeAllSubFeature = $True }
You can then specify subfeatures individually to ensure those are enabled or disabled.
To discover what features exist on the server, use the powershell command Get-WindowsFeature. (Pipe this to OGV to get a grid view instead for ease of viewing):
Firewall settings
The xNetworking module, available from the powershell gallery, implements a DSCResource called xFirewall. This gives full control over the Windows Firewall with Advanced Security feature. This allows fine grained control over the specific rules and policies within the firewall. An alternative approach is to use the netsh advfilewall command within a DSC Script resource.
Timezone
The simplest way to ensure a timezone is set is to use the tzutil command line – this has a get (/g) parameter, so we can build a Get, Test and Set function, and use the built in DSC Script resource to ensure the timezone is set. The test function will be called to see if the timezone is already what we want, and if it is, the set will be skipped.
Script SetTimezone { GetScript = { return @{ Timezone = ("{0}" -f (tzutil.exe /g)) } } TestScript = { return (tzutil.exe /g) -ieq "GMT Standard Time" } SetScript = { tzutil.exe /s "GMT Standard Time" } }
Remote access Configuration
Another DSC module is available from the Powershell gallery called xRemoteDesktopAdmin. Note that the x prefix denotes “Experimental”. In the gallery the naming convention prefixes “x” or “c” are used to indicate experimental or community versions (or if no prefix, it’s a production release). Typically modules stay in experimental for some time, and newer versions are usually released as experimental first. This does not cause problems, because the modules are downloaded at the authoring stage and from there pulled to the target machines, rather than target machines having a dependency on the gallery. The xRemoteDesktopAdmin resource has been written specifically for setting the remote desktop availability on or off – the UserAuthentication setting matches the checkbox “Allow connections only from computers running Remote Desktop with Network Level Authentication (recommended)”.
xRemoteDesktopAdmin EnableRDP { Ensure = "Present" UserAuthentication = "NonSecure" }
Support packs (SQLXML, MSSQL, Report Viewer for example)
These support packs and modules are all distributed as MSU, MSI or EXE installables. The same strategy is used as per SQL Server install below, without the complexity of having to mount the ISO image.
- Ensure the executable is saved to \\s1\\dscsmb
- Add a File resource in the configuration to ensure the source is pulled to the target
- Add a script resource to test if the support pack is already installed, and a SetScript to execute the installer in the event it’s not already there.
SQL Server, Client Tools
The strategy for deploying SQL Server to one of the target machines is to use the File Dsc resource and the Script Dsc resource. To author the deployment, we first need to create a silent install parameters file. This can be done by stepping through the SQL Server installation, selecting all the required components and parameters, up to the point where the installer prompts to click Install to carry out the installation. At this point, the configuration .ini file has been produced (the path is displayed in the installer UI). This .ini file can then be used by the target machines to execute the installation silently.
- Create the silent install .ini file
- Download the SQL Server installation .iso file, copy this and the .ini file to \\s1\dscsmb (as specified in the environmentConfig.psd1 file)
- Add two File resources to the configuration to copy both the iso and the ini file to the target server when a pull request is made:
# copy the sqlserver iso File SQLServerIso { SourcePath = $ConfigurationData.NonNodeData.PackagePath+"\en_sql_server_2012_standard_edition_with_service_pack_2_x64_dvd_4692562.iso" DestinationPath = $ConfigurationData.NonNodeData.TempPath+"\SQLServer.iso" Type = "File" Ensure = "Present" #Credential = $Credential } # copy the ini file to the temp folder File SQLServerIniFile { SourcePath = $ConfigurationData.NonNodeData.PackagePath+"\ConfigurationFile.ini" DestinationPath = $ConfigurationData.NonNodeData.TempPath Type = "File" Ensure = "Present" #Credential = $Credential DependsOn = "[File]SQLServerIso" }
- Add a script resource to carry out the install:
- A GetScript to return true if any instances of SQL Server can be found on the machine using the Get-WMI win32-service powershell cmdlet.
- A TestScript to do the same as a
- A SetScript to mount the .iso image from the temp file and execute the installer
Script InstallSQLServer { GetScript = { $sqlInstances = gwmi win32_service -computerName localhost | ? { $_.Name -match "mssql*" -and $_.PathName -match "sqlservr.exe" } | % { $_.Caption } $res = $sqlInstances -ne $null -and $sqlInstances -gt 0 $vals = @{ Installed = $res; InstanceCount = $sqlInstances.count } $vals } SetScript = { $imagePath = "c:\windows\temp\SQLServer.iso"; # mount the iso $setupDriveLetter = (Mount-DiskImage -ImagePath "$imagePath" -PassThru | Get-Volume).DriveLetter if ($setupDriveLetter -eq $null) { throw "Could not mount SQL install iso at $imagePath" } Write-Verbose "Drive letter for iso is: $setupDriveLetter" $setupDriveLetter = $setupDriveLetter + ":" # run the installer using the ini file $cmd = "$setupDriveLetter\Setup.exe /ConfigurationFile=c:\windows\temp\ConfigurationFile.ini /IAcceptSQLServerLicenseTerms=True /SQLSVCPASSWORD=P2ssw0rd /AGTSVCPASSWORD=P2ssw0rd /SAPWD=P2ssw0rd" Write-Verbose "Running SQL Install - check %programfiles%\Microsoft SQL Server\120\Setup Bootstrap\Log\ for logs..." Invoke-Expression $cmd | Write-Verbose } TestScript = { $sqlInstances = gwmi win32_service -computerName localhost | ? { $_.Name -match "mssql*" -and $_.PathName -match "sqlservr.exe" } | % { $_.Caption } $res = $sqlInstances -ne $null -and $sqlInstances -gt 0 if ($res) { Write-Verbose "SQL Server is already installed" } else { Write-Verbose "SQL Server is not installed" } $res } }
File type associations
There are a couple of ways to achieve this – the simplest is to use the DOS commands Assoc and Ftype within a DSC script resource, similar to how tzutil was used to set and get the timezone – for example, to set .htm to point to internet explorer:
GetScript = { return @{ Assoc = ("{0}" -f (cmd /c "assoc .htm")) } } TestScript = { return (cmd /c "assoc .htm") -ieq ".htm=htmlfile" } SetScript = { cmd /c 'Ftype htmlfile=”%ProgramFiles%\Internet Explorer\IEXPLORE.EXE” %1'}
Security settings
Another powershell gallery module called xSystemSecurity that can be used to carry out enabling/disabling of IE Enhanced Security Configuration for both users and administrators.
xIEEsc DisableIEESCUsers { IsEnabled = $false UserRole = "Users" } xIEEsc DisableIEESCAdmin { IsEnabled = $false UserRole = "Administrators" }
IIS Web hosting configuration
The module called WebAdministration (https://technet.microsoft.com/en-us/library/ee790599.aspx) can be used to fine grain control all of the settings within IIS, including app-pools, sites, etc. Each set of controllable assets is provided with a Get and Set to enable building of a DSC Script resource. Most also have New- and Remove- commandlets, which enable you to provision new sites or app-pools directly.
Further Reading/Resources
Sample Script files used for research into these three blog posts:
Powershell Gallery – also contains links to download WMF5.0 for Windows 8.1/2012
https://www.powershellgallery.com/
Technet – overview of DSC (2013)
https://technet.microsoft.com/en-us/library/dn249912.aspx
Powershell Magazine – various DSC related articles that are often useful and interesting
http://www.powershellmagazine.com/tag/dsc/