ConfigMgr uber-Hydration Kit v0.9 with Powershell DSC

Are you a ConfigMgr (or SCCM) administrator? Are you sometimes (or often, like me) in a position where you need to rebuild your ConfigMgr 2012 lab environment?

I, again, was in that situation and just came out of a very interesting discussion with a co-worker and thought to myself, why not use Desired State Configuration to do all that?

I am calling it uber-Hydration Kit, but to be fair, it’s not 100% automated yet. There are still a couple of things that need to be done manually. I will explain later on, that is why it is still at version 0.9.

Enter: Powershell Desired State Configuration

I have already used DSC a bit (here and here), but always only with either already built-in resources or those resources from the DSC Resource Kit Wave. This time I had to write my own custom DSC resource.

There are a lot of walkthroughs and how-tos and what-nots on the internet already on how to create your custom DSC resource. Here is just a quick list of articles and resources I used:

 

Writing the DSC Configuration

You can download my custom module at the end of this article. Copy the folder ‘cConfigMgr2012′ to this directory (Never copy stuff to C:\Windows\System32\WindowsPowershell!!!):

C:\Program Files\WindowsPowershell\Modules

After copying the custom DSC module to the correct path we can start writing the Configuration block in Powershell. I ended up with this Configuration, which I saved on my “soon-to-be” Site Server in C:\temp:

Source code    
$ConfigData=
@{
AllNodes =
@(
@{
NodeName= '*'
},
@{
NodeName = 'DSCServer02.do.local'
Role = 'SiteServer'                                                               #Not yet used
InstallPath = 'C:\CM12'                                                           #Where ConfigMgr will be installed
CMAdmin = 'do\SC_Admins'                                                          #User or Group that will be added to the Full Administrators Group in ConfigMgr
CMSiteCode = 'DSC'                                                                #ConfigMgr three digit Sitecode
CMSiteName = 'DSC Test Site'                                                      #ConfigMgr Site Name / Description
CMPrereqPath = "$env:windir\temp"                                                 #Where ConfigMgr will download the Prereq files to or folder where you have already put them
CMSourcePath = '\\dc01\Deployment\Source\SystemCenter2012R2\ConfigurationManager' #ConfigMgr installation sources (Computer account needs read permissions to that location)
#SQLServerInstance = 'CM12'                                                     if applicable, enter SQL Server instance name
}
);
}
configuration CM12 {
param(
)
#Import-DscResource -Module xSqlPs
Import-DscResource -Module cConfigMgr2012
Node $AllNodes.NodeName {
WindowsFeature WebServer
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'Web-Server'
#IncludeAllSubFeature = "true"
}
WindowsFeature IISConsole
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'Web-Mgmt-Console'
DependsOn = '[WindowsFeature]WebServer'
}
WindowsFeature IISBasicAuth
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'Web-Basic-Auth'
DependsOn = '[WindowsFeature]WebServer'
}
WindowsFeature IISWindowsAuth
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'Web-Windows-Auth'
DependsOn = '[WindowsFeature]WebServer'
}
WindowsFeature IISURLAuth
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'Web-Url-Auth'
DependsOn = '[WindowsFeature]WebServer'
}
WindowsFeature ASPNet45
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'Web-Asp-Net45'
DependsOn = '[WindowsFeature]WebServer'
}
WindowsFeature RDC
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'RDC'
}
WindowsFeature BITS
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'BITS'
}
WindowsFeature DotNet35
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'NET-Framework-Features'
Source = '\\dc01\sources\OSD\OSSources\W12R2\sources\sxs'
}
WindowsFeature DotNet35Core
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'NET-Framework-Core'
DependsOn = '[WindowsFeature]DotNet35'
Source = '\\dc01\sources\OSD\OSSources\W12R2\sources\sxs'
}
WindowsFeature DotNet45Features
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'NET-Framework-45-Features'
}
WindowsFeature DotNet45Core
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'NET-Framework-45-Core'
DependsOn = '[WindowsFeature]DotNet45Features'
}
WindowsFeature NetWCFHTTPActivation
{
Ensure = 'Present' # To uninstall the role, set Ensure to "Absent"
Name = 'NET-WCF-HTTP-Activation45'
DependsOn = '[WindowsFeature]DotNet45Features'
}
Package DeploymentTools #From http://powershell.org/wp/forums/topic/installing-adk-8-1-with-dsc/
{
Name = "ADK Deployment Tools"
Path = "\\dc01\deployment\Source\Prerequisites\ADK81\adksetup.exe"
ProductId = "FEA31583-30A7-0951-718C-AF75DCB003B1"
Arguments = "/quiet /features OptionId.DeploymentTools /norestart "
Ensure = "Present"
ReturnCode = 0
}
Package PreinstallationEnvironmentTools #From http://powershell.org/wp/forums/topic/installing-adk-8-1-with-dsc/
{
Name = "ADK Preinstallation Environment"
Path = "\\dc01\deployment\Source\Prerequisites\ADK81\adksetup.exe"
ProductId = "6FDE09DB-D711-593B-0823-D99D2A757227"
Arguments = "/quiet /features OptionId.WindowsPreinstallationEnvironment /norestart "
Ensure = "Present"
ReturnCode = 0
}
Package UserStateMigrationTools #From http://powershell.org/wp/forums/topic/installing-adk-8-1-with-dsc/
{
Name = "ADK Deployment Tools"
Path = "\\dc01\deployment\Source\Prerequisites\ADK81\adksetup.exe"
ProductId = "0C4384AC-02DB-B4E5-E537-EE6CF22392CF"
Arguments = "/quiet /features OptionId.UserStateMigrationTool /norestart "
Ensure = "Present"
ReturnCode = 0
}
CMPrimarySite PrimarySite
{
SiteCode = "$($Node.CMSiteCode)"
SiteName = "$($Node.CMSiteName)"
SQLServer = "$($Node.NodeName)"
PrereqPath = "$($Node.CMPrereqPath)"
SourcePath = "$($Node.CMSourcePath)"
#SQLServerInstance = "$($Node.SQLServerInstance)"
DPServer = "$($Node.NodeName)"
MPServer = "$($Node.NodeName)"
SMSProviderServer = "$($Node.NodeName)"
InstallationDirectory = "$($Node.InstallPath)"
DependsOn =  @('[Package]DeploymentTools')
}
<#
xSqlServerInstall installSqlServer
{
InstanceName = "CM12"
SourcePath = '\\dc01\Deployment\Source\SQLServer2012.en'
Features= "SQLEngine,SSMS"
SqlAdministratorCredential = $credential
SvcAccount = 'do\adobrien'
DependsOn = "[WindowsFeature]dotNet35"
}
# Disabled, because too many things missing, Collation, Static Port for Named Instance...
#>
Script AddCM12Admin # Original from http://asaconsultant.blogspot.no/2014/05/configmgr-scripting-with-powershell.html, had to change a few things around variables
{
Getscript = {
Write-verbose "In Getscript AddCM12Admin"
$CM12ModulePath = "$(Join-Path $($USING:Node.InstallPath) AdminConsole\bin\ConfigurationManager.psd1)"
$cert = Get-AuthenticodeSignature -FilePath "$CM12ModulePath" -ErrorAction SilentlyContinue
$store = new-object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher","LocalMachine")
$store.Open("MaxAllowed")
$return = ""
$return = $store.Certificates | where {$_.thumbprint -eq $cert.SignerCertificate.Thumbprint}
$store.Close()
@{Result=$return.ToString()}
}
TestScript = {
Write-verbose "In TestScript AddCM12Admin"
$CM12ModulePath = "$(Join-Path $($USING:Node.InstallPath) AdminConsole\bin\ConfigurationManager.psd1)"
$cert = Get-AuthenticodeSignature -FilePath "$CM12ModulePath" -ErrorAction SilentlyContinue
$store = new-object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher","LocalMachine")
$store.Open("MaxAllowed")
$return = $null
$return = $store.Certificates | where {$_.thumbprint -eq $cert.SignerCertificate.Thumbprint}
$store.Close()
if($return)
{
write-verbose "Return TRUE"
$true
}
else
{
write-verbose "Return FALSE"
$false
}
}
SetScript = {
Write-verbose "In SetScript AddCM12Admin"
$CM12ModulePath = "$(Join-Path $($USING:Node.InstallPath) AdminConsole\bin\ConfigurationManager.psd1)"
$cert = Get-AuthenticodeSignature -FilePath "$CM12ModulePath" -ErrorAction SilentlyContinue
$store = new-object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher","LocalMachine")
$store.Open("MaxAllowed")
Write-Verbose "Adding cert to store"
$store.Add($cert.SignerCertificate)
$store.Close()
Import-Module $CM12ModulePath
new-psdrive -Name "$($USING:Node.CMSiteCode)" -PSProvider "AdminUI.PS.Provider\CMSite" -Root "$($USING:Node.NodeName)"
Set-Location "$($USING:Node.CMSiteCode):\"
New-CMAdministrativeUser -RoleName 'Full Administrator' -SecurityScopeName All -Name "$($USING:Node.CMAdmin)"
}
}
}
}
CM12 -ConfigurationData $ConfigData -OutputPath C:\temp\CM12
#Start-DscConfiguration -Wait -ComputerName DSCTestServer.do.local -Path \\dc01\sources\Tools\scripts\Powershell_DSC\CM12 -Verbose

This script, which will create the MOF file for you, is made up of two parts, the Configuration Data and the Configuration block itself.

DSC Configuration Data

The Configuration Data block you can see in above script is actually a hashtable with a lot of properties in it. There are a few rules about Configuration Data (http://technet.microsoft.com/en-us/library/dn249925.aspx), but the property names are mostly up to you.

You need to define all of those parameters in above Configuration Data block in order for the resource be able to complete and install your ConfigMgr site.

Why did I use the Configuration Data block? Because I like the idea of keeping my Configuration clean and fully reusable. Separate the “What” from “Where” (http://blogs.msdn.com/b/powershell/archive/2014/01/09/continuous-deployment-using-dsc-with-minimal-change.aspx)

Prerequisites

In order for this configuration to run, you need to have the following things in place:

 

The custom resource in my Module is called CMPrimarySite and will take a couple of parameters, which are all used or shown in my example. As I usually don’t recommend uninstalling ConfigMgr, this is, at this time, a one-way street, no way to uninstall ConfigMgr via this way.

image

The whole ADK part of this Configuration is from http://powershell.org/wp/forums/topic/installing-adk-8-1-with-dsc/, full credit to that guy, although I guess that you could do that part better with less reboots. That’s something for the next version.

If you have configured your Local Configuration Manager (LCM) to automatically reboot, then you don’t have to do it yourself, which is quite handy.

I will talk you through the process:

  • first run:
    • DSC will install all needed Windows Features and Roles
    • DSC will install Windows ADK 8.1
    • image
    • DSC will tell you to reboot the Server (I’m doing a push and haven’t configured the Local Configuration Manager (LCM) to automatically restart)
  • second run:
    • log back on and run Start-DSCConfiguration again
    • DSC will now install ConfigMgr
      • image
        • during installation the prereqs will also be downloaded
    • ConfigMgr restarts the WMI service, which will cause the DSC to error out, setup will continue though up to the end Sad smile
      • monitor C:\ConfigMgrSetup.log for more information on setup progress
      • image
  • third run (DSC needs to run one more time):
    • DSC will add the user or group configured in “CMAdmin” to the Full Administrators role in ConfigMgr. This needs to be done, because ConfigMgr has just been installed in the context of “SYSTEM”.

 

After DSC is done installing you are able to log on to your ConfigMgr Site.

I’m looking forward to all your feedback. There’s still a bit to do on my side, but I believe this can be a big step towards the next generation of hydration kits.

Restarting WMI in DSC

Why does DSC error out when ConfigMgr setup restarts WMI?
DSC heavily relies on the WMI service. For example, if you want DSC to re-read all your DSC modules/resources, restart the WMI service (or use debug mode in Powershell 5). Naturally, if you go and bounce that service, DSC will be a bit upset. In our case though, that doesn’t matter. DSC has already kicked off our ConfigMgr setup and that will run up until the end.

This just means that you either have to wait some time for DSC to re-run and re-apply the last step (adding the user) or kick start DSC manually by running (Start-DSCConfiguration) one more time.

Remember, DSC is usually nothing you kick off and stare at for as long as it is running. The -Wait parameter is really just for troubleshooting (in my opinion). DSC is meant to be something which you run against a large number of machines, unattended, a set-and-forget thing.

Open ToDos

This is on my todo list:

  • implement better SQL install handling
    • the SQL resource right now is just not good enough to be used for this use case.
  • implement better error handling
  • implement better output during installation
  • implement better reboot experience

Credits

I have, throughout the module and resource used a couple of other sources that are, of course, mentioned in there. I would also like to thank Powershell MVP Trevor Sullivan for his support in creating this module. I, again, have learnt quite a bit about how to make my scripts better, easier readable and more robust.

Summary

DSC will make it easier for me to deploy a ConfigMgr lab now, but I have also seen that there are still some limitations, either on my side or on DSC’s side, both very much possible! I will continue working on this module and will update this article with new findings. Until then, have fun!

Download the custom module and my example configuration here:
cConfigMgr20121.zip (0 downloads)

-David

email

Written by , Posted .
  • 4c74356b41

    Hello, David. How did you handle ADK 8.1 installation? I’m the person whose post you are referring to. When I was testing this DSC wouldn’t let me install ADK 8.1 without 3 restarts when using 3 different configurations instead of one (just like in the post).