Tuesday, 22 December 2015

Using Azure Automation DSC to configure and deploy the new SCSM Portal (Part 1)

On Nov 10th Microsoft released a new HTML portal for System Center Service Manager 2012 R2.

The installation is fairly straight forward, however there are still some pre-requisites that need to be in place and I also thought it would be good to introduce delivering this via Azure Automation Desired State Configuration (DSC).

The main reason for looking to do this via DSC is also the ongoing maintenance.
Things happen. People can make mistakes, features and roles can be accidently removed.

DSC provides a set of configuration that;
  • should be applied to a server
  • will be applied to a server to bring it into compliance
  • will then continue to check the server for compliance
  • will attempt to remediate and bring it back into compliance should it drift.


So what do we need?

Well the new portal is an IIS site and therefore requires some windows features to be installed. These are:

  • Web-Server
  • Web-Filtering
  • Web-Basic-Auth
  • Web-Windows-Auth
  • Web-Mgmt-Console
  • Web-Mgmt-Compat
  • Web-Net-Ext45
  • Web-ASP
  • Web-Asp-Net45
  • NET-Framework-45-ASPNET
  • NET-WCF-HTTP-Activation45

These can be easily added using a single PowerShell line:
Add-WindowsFeature Web-Server,Web-Filtering,Web-Basic-Auth,Web-Windows-Auth,Web-Mgmt-Console,Web-Mgmt-Compat,Web-Net-Ext45,Web-ASP,Web-Asp-Net45,NET-Framework-45-ASPNET,NET-WCF-HTTP-Activation45


But installation, as I mentioned, is only part of the story. Making sure these features remain in place is where DSC come in.

In DSC we create a "Configuration" file which lays out how the assigned server should be configured.
For Windows features there is a specific DSC Resource we use, aptly named, WindowsFeature.

For each windows feature that we require we use a block like this:

WindowsFeature WebServer
{
Ensure = "Present"
Name = "Web-Server"
}

So basically we name the code block with something meaningful (as we may reference later) with the WindowsFeature resource, use the Name property to specify the Windows Feature to check (Use Get-WindowsFeature in PowerShell to check for the name you require) and then use the Ensure property to state whether the named feature should either be present on the server or not (our intention).

Further properties and information can be found here:
https://technet.microsoft.com/en-us/library/dn282127.aspx

So for all our Windows Feature requirements for the SCSM Portal we get:
#Install the required IIS features
WindowsFeature WebServer
{
Ensure = "Present"
Name = "Web-Server"
}
WindowsFeature WebFiltering
{
Ensure = "Present"
Name = "Web-Filtering"
}
WindowsFeature WebBasicAuth
{
Ensure = "Present"
Name = "Web-Basic-Auth"
}
WindowsFeature WebWindowsAuth
{
Ensure = "Present"
Name = "Web-Windows-Auth"
}
WindowsFeature WebMgmtConsole
{
Ensure = "Present"
Name = "Web-Mgmt-Console"
}
WindowsFeature WebMgmtCompat
{
Ensure = "Present"
Name = "Web-Mgmt-Compat"
}
WindowsFeature WebNetExt45
{
Ensure = "Present"
Name = "Web-Net-Ext45"
}
WindowsFeature WebASP
{
Ensure = "Present"
Name = "Web-Asp"
}
WindowsFeature WebASPNet45
{
Ensure = "Present"
Name = "Web-Asp-Net45"
}
WindowsFeature NETFramework45ASPNET
{
Ensure = "Present"
Name = "NET-Framework-45-ASPNET"
}
WindowsFeature NETWCFHTTPActivation45
{
Ensure = "Present"
Name = "NET-WCF-HTTP-Activation45"
}


Well, that's the Windows Features sorted.
Now please, bear with me here as so far it looks like a lot more work as opposed to that single PowerShell one liner...

Next is the installation of the SCSM Portal.

For this we utilise the DSC Package Resource and it looks something like this:

Package SCSMSSP
{
Name = "SCSM SSP"
Path = "C:\DSC\SSP\SetupWizard.exe"
ProductId = "17F5D20F-47FB-485E-8CFC-4768C3C3F460"
Arguments = "/Install:SelfServicePortal /silent /accepteula /CustomerExperienceImprovementProgram:No /EnableErrorReporting:No /SMServerName:$SCSMSDKServer /PortalWebSiteName:SCSMPortal /PortalWebSitePort:81 /PortalAccount:$Domain\$SCSMUser\$SCSMPassword"
Ensure = "Present"
DependsOn = @("[WindowsFeature]WebServer","[WindowsFeature]WebFiltering","[WindowsFeature]WebBasicAuth","[WindowsFeature]WebWindowsAuth","[WindowsFeature]WebMgmtConsole","[WindowsFeature]WebMgmtCompat","[WindowsFeature]WebNetExt45","[WindowsFeature]WebASP","[WindowsFeature]WebASPNet45","[WindowsFeature]NETFramework45ASPNET","[WindowsFeature]NETWCFHTTPActivation45")
}


So again, we provide a name for the Package Resource code block (SCSMSSP) and a more friendly name for the Name property.
The Path property denotes where the installation file resides.
The ProductId property is the unique GUID for the application to allow DSC to check for it's presence.
The Arguments property is used for any command line to be passed to control the installation.
Again, the Ensure property denotes if the software should be installed (present) or not installed (absent).

The DependsOn property allows us to specify various resources that DSC should first ensure are compliant before attempting to run this resource.

It's easy to specify a dependant resource, just wrap the DSC Resource type name in square brackets and then specify the name of that feature.
So WindowsFeature WebASPNet45 becomes [WindowsFeature]WebASPNet45.

One thing to note is that if you need to specify multiple dependencies then you need to specify them as a string formatted as an array.
Do this by putting each feature formatted as just discussed in quotation marks " " and separating with a comma. Finally enclose it as an array - @( ).

So if we required two features, WebMgmtCompat and WebASP we would have the following:

DependsOn = @("[WindowsFeature]WebMgmtCompat","[WindowsFeature]WebASP")

So we're almost there...

For those that were paying attention, you may have noticed that the arguments property string contained some variables.
This allows us to prompt when compiling the DSC configuration for various settings to prevent hard coding data and allow for reuse.
Just like normal PowerShell we put these at the beginning in a Param block.

Param(
[Parameter(Mandatory=$true)]
[string] $SCSMSDKServer,
[Parameter(Mandatory=$true)]
[string] $Domain
)
$SCSMSDKCred = Get-AutomationPSCredential -Name "SCSMSDKCredentialAsset"
$SCSMUser=$SCSMSDKCred.UserName
$SCSMPassword = $SCSMSDKCred.GetNetworkCredential().Password

I'm also going to leverage the Credentials Asset feature of Azure Automation.
This allows you to securely store username and password combo's within Azure Automation as an asset to then be reused in Runbooks and DSC. Again, allowing us to avoid hardcoding sensitive data and not having to prompt for them at each compilation.

I'm using the Get-AutomationPSCredential command to retrieve it and then split the PSCredential object down to username and password.

However, because I'm utilising this for the command line arguments for the executable installation I can't use it as a PSCredential and have to supply a non encrypted password, hence the use of the .GetNetworkCredential().Password to pull the unencrypted password out of the object.

N.B. This has the side effect of storing the password in clear text within the MOF that gets compiled and pushed down to the DSC node.

Because of this, it also means that compilation of the configuration can not be done via the Azure Portal and only by PowerShell as you will need to add configuration to the compilation task to tell it to allow the use of plain text passwords. More on this later.


The final DSC Configuration script looks like this:


Configuration SCSMPortal
{
param(
[Parameter(Mandatory=$true)]
[string] $SCSMSDKServer,
[Parameter(Mandatory=$true)]
[string] $Domain
)
$SCSMSDKCred = Get-AutomationPSCredential -Name "SCSMSDKCredentialAsset"
$SCSMUser=$SCSMSDKCred.UserName
$SCSMPassword = $SCSMSDKCred.GetNetworkCredential().Password
Node "WebServer"
{
#Install the required IIS features
WindowsFeature WebServer
{
Ensure = "Present"
Name = "Web-Server"
}
WindowsFeature WebFiltering
{
Ensure = "Present"
Name = "Web-Filtering"
}
WindowsFeature WebBasicAuth
{
Ensure = "Present"
Name = "Web-Basic-Auth"
}
WindowsFeature WebWindowsAuth
{
Ensure = "Present"
Name = "Web-Windows-Auth"
}
WindowsFeature WebMgmtConsole
{
Ensure = "Present"
Name = "Web-Mgmt-Console"
}
WindowsFeature WebMgmtCompat
{
Ensure = "Present"
Name = "Web-Mgmt-Compat"
}
WindowsFeature WebNetExt45
{
Ensure = "Present"
Name = "Web-Net-Ext45"
}
WindowsFeature WebASP
{
Ensure = "Present"
Name = "Web-Asp"
}
WindowsFeature WebASPNet45
{
Ensure = "Present"
Name = "Web-Asp-Net45"
}
WindowsFeature NETFramework45ASPNET
{
Ensure = "Present"
Name = "NET-Framework-45-ASPNET"
}
WindowsFeature NETWCFHTTPActivation45
{
Ensure = "Present"
Name = "NET-WCF-HTTP-Activation45"
}
Package SCSMSSP
{
Name = "SCSM SSP"
Path = "C:\DSC\SSP\SetupWizard.exe"
ProductId = "17F5D20F-47FB-485E-8CFC-4768C3C3F460"
Arguments = "/Install:SelfServicePortal /silent /accepteula /CustomerExperienceImprovementProgram:No /EnableErrorReporting:No /SMServerName:$SCSMSDKServer /PortalWebSiteName:SCSMPortal /PortalWebSitePort:81 /PortalAccount:$Domain\$SCSMUser\$SCSMPassword"
Ensure = "Present"
LogPath = "C:\DSC\SSP\SSP_Setup.log"
DependsOn = @("[WindowsFeature]WebServer","[WindowsFeature]WebFiltering","[WindowsFeature]WebBasicAuth","[WindowsFeature]WebWindowsAuth","[WindowsFeature]WebMgmtConsole","[WindowsFeature]WebMgmtCompat","[WindowsFeature]WebNetExt45","[WindowsFeature]WebASP","[WindowsFeature]WebASPNet45","[WindowsFeature]NETFramework45ASPNET","[WindowsFeature]NETWCFHTTPActivation45")
}
}
}

Notice the Node reference?
The DSC Resources used are wrapped within a Node <Name> section.
This isn't mandatory, if you're only using this to configure a single server, but as a side effect of using Plain Text password and Azure Automation DSC this is required.

N.B. Thanks to @bgelens for pointing me at that requirement as I couldn't get it to work!!!


So I mentioned you can't just import and compile this within the Azure Portal.
You can still import it via the console, so go ahead and open your Azure Automation account | Click DSC Configurations | Add a Configuration | Browse to your Config File | Click OK.


PowerShell code to compile the DSC Configuration:

$ConfigData = @{
AllNodes = @(
@{
NodeName = "*"
PSDscAllowPlainTextPassword = $True
}
)
}
$Parameters = @{
"SCSMSDKSERVER" = "PONSCSM04"
"DOMAIN" = "PowerON"
}
$ResourceGroup="xxxxx"
$AccountName="xxxxx"
$ConfigurationName="xxxxx"
Login-AzureRmAccount
Start-AzureRmAutomationDscCompilationJob -ResourceGroupName $ResourceGroup -AutomationAccountName $AccountName -ConfigurationName $ConfigurationName -ConfigurationData $ConfigData -Parameters $Parameters

Main thing is the PSDscAllowPlainTextPassword. This flags the config to allow us to store the password in plain text within the MOF that gets downloaded to the DSC node, which we're doing by pushing the plain text password from the PSCredentialObject into a variable. 
(N.B. See update note below)

Make sure you replace the $ResourceGroup, $AccountName, $ConfigurationName with relevant variables as well as the Prameters (SCSMSDKServer & Domain).

All that is left is to assign this configuration to a node and sit back and watch the SCSM Portal install.
I've not covered that piece in this post, but will have a "part 2" soon, along with a quick video showing the process end to end.

Now read Part 2 - http://www.systemcenter.ninja/2015/12/using-azure-automation-dsc-to-configure_23.html


** Updated 23/12/2015 **
Minor revision to installation command as I realised it wasn't using the RTW command line.  Replaced SDKServerName with SMServerName and added PortalWebSiteName switches.

**Updated 23/12/2015 **
Joe from the Azure Automation team left a comment regarding the transfer of the MOF file is fully encrypted, and I must admit I didn't realise that WMF5 also encrypted the MOF locally so any worries of plain text passwords isn't really a worry at all!

4 comments:

Joe Levy said...

Hey, I'm from the Azure Automation team. First of all, great blog post!

"This has the side effect of storing the password in clear text within the MOF that gets compiled and pushed down to the DSC node" -- Just want to let you know that Azure Automation DSC stores all MOFs encrypted behind the scenes, and when the MOF is transferred to the node HTTPS is used. Once the node receives the MOF, in WMF 5 RTM, the node will encrypt the MOF locally as well. So you don't have to worry about the credentials actually being visible at any point to anyone :)

One other thing I noticed is your configuration data doesn't have any node names in it, so the ALL references ("*") isn't actually applied to any nodes. So allow plain text password isn't going to actually be allowed for any node statements in the configuration. I think you need to have "Webserver" in the config data for it to work they way you are trying to have it work.

If you want to contact me, I'm on Twitter at @jodoglevy.

Steve Beaumont said...

Thanks Joe, I didn't know that WMF5 encrypts the MOF locally and I probably should have pointed out that https is used for the transfer, I've added an update referencing these points.

The configuration data works and compiles fine using "*" as the node reference and since I only have the one or might want it to apply to multiples If I add I didn't specifically name it. Conversely, not adding a named node or * or trying to compile via the portal fails...

Regards,
Steve

Joe Levy said...

Hey Steve,

On this part:
"The configuration data works and compiles fine using "*" as the node reference and since I only have the one or might want it to apply to multiples If I add I didn't specifically name it. Conversely, not adding a named node or * or trying to compile via the portal fails..."

We had someone test this again to make sure, and that just won't work. Unfortunately, right now you need the node names in the configuration data for PSAllowPplainTextPasswords to work for them, even if you use *. The * only applies to other named nodes in the configuration data. The Azure Automation DSC documentation mentions this as well: https://azure.microsoft.com/en-us/documentation/articles/automation-dsc-compile/

Birdal said...

Hi Steve,
I need your advice/experiences about resource pool in SCOM. We will deploy SCOM 2016 and an experts, Stoyan Chakalov, wrote in the forum that we need for each area one Resource Pool. I am confused about it. Can you give me please your comments related to the following discussion (section Resource Pools)?

https://social.technet.microsoft.com/Forums/en-US/a9e94d5d-ec31-4f71-999d-73e3befb796d/sql-server-reporting-services-and-scom-report-server-confusing?forum=operationsmanagerdeployment

Best Regards
Birdal