Introduction

There are a number of different ways to install PI systems and maintain configurations but historically most of the PI system installations I’ve done have involved the downloading of install kits and clicking through of prompts in order to install the software. Unfortunately this process opened me up to human error and the possibility that a new configuration was not to up best practices. It also takes a lot of human hours to do large numbers of installs. So recently I’ve been on an automation kick and have started to explore options around existing automation frameworks. There are quite a few solutions out there like SCCM, Chef, Puppet, Ansible, Salt and PowerShell Desired State configuration (DSC). These configuration tools work in a similar way and there are hundreds of articles debating the relative merits of each solution and different users will require a different set of tools. The immediate benefits of automation to most system administrations are that you have an easy way to perform:

  • Consistent configuration across installed software bases
  • Version Control

 

The interesting part is that the right configuration can ensure that your PI System is configured correctly and according to best practices without having to ever look at the code after an initial install again. Likewise, if anyone accidentally (or purposefully) makes changes to the configurations, they’ll be detected and set back to best practices assuming the configuration is rerun on a schedule.

 

Whatever your internal deployment looks like it can be very useful to think of what’s deployed as infrastructure as code or infrastructure as a template. Having a configuration enforced based on the rules of a programming language or default setting can help with a number of planning, upgrading, and auditing, and risk factors common to installed infrastructure. PowerShell DSC and Chef have the added benefit of being able to integrate with Visual Studio release management and deploy to a server without a deployment agent.

 

So let’s take a look at what a configuration management solution around the PI System might look like and as a thought exercise we’ll walk through the automatic deployment and configuration of an AF Server with the use of PowerShell desired state configuration. Other configuration management tools and full stack solutions will offer different benefits, but for our purposes let’s investigate an AF server deployment.  I chose the AF server as it’s a fairly straightforward installation that one can get up and running relatively quickly with a Microsoft SQL server already available so it’s a perfect candidate for our though experiment.

 

If you’re not familiar with DSC please check out The DSC Book for a very interesting overview of DSC. There’s also some articles on Technet about and getting started. Please be sure to also grab the latest DSC Resource Kits. Most of these are experimental or community based, however, they work great for our purposes. Here is the most comprehensive list of DSC Resources I have found. Also, since DSC is moving at a fast development pace I’d recommend following the Microsoft Team Blog for the latest developments (in reference to TechNet documentation that could occasionally be out of date):


Example of DSC with the PI System

 

At its heart, DSC is just a configuration script to ensure software is configured correctly. Chef does this with recipes, Puppet with manifests, and Ansible with playbooks. With DSC It involves three main steps.

  1. Create a configuration file that specifies what you could like the software configuration to “look like”. It’s kind of like writing a document that specifies what you want done to a node/computer/server.
  2. The compilation of that script into a MOF file, which is just a differently formatted file that is machine readable for your infrastructure as code.
  3. The application of that MOF file to the computer.

 

Here is a simple complete example. We’re checking PI Perfmon service to make sure it’s startup type is set to automatic on our local node. I’ve written and deployed this on a PI System that has PI Perfmon running as a windows service:

 

#Configuration Script     
Configuration MyServiceConfig
{
    Import-DscResource -Name MSFT_xServiceResource -ModuleName xPSDesiredStateConfiguration
     
     Node localhost
     {
         xService PIPerfmon
         {
         Name = "PIPerfmon1" # ResourceName 
         StartupType = "Automatic"
         }
     }
}

#Compile the MOF
MyServiceConfig 

#Apply the MOF
Start-DscConfiguration -force -verbose -Path "$env:PROGRAMFILES\WindowsPowerShell\MyServiceConfig\"

 

 

In a more complicated deployment we’d have multiple services, packages, or other resources listed under our Node in order to do a complete installation and configuration of a PI System. So for each service, install kit, or tuning parameter we change in the installation, configuration, or management of a PI System, we’d have an entry. So you can imagine that this file may become fairly large with a lot of information imported into it.  Also note that for DSC each node/computer/server can have 1 and only 1 .mof file.

 

Our example environment deployed and configured as code (e.g. PowerShell DSC and the automatic installation of an AF Server)

For the thought experiment and for testing purposes we're going to be working in a test environment in Microsoft Azure with Windows Server 2012 R2 installed on our node. I’ve pulled the default image from the Azure gallery and will be using it. It’s entirely possible to set up the PowerShell DSC environment on premise in a push configuration, or through a pull server, but being able to spin up a machine and destroy it after testing in complete is relatively easy with Azure.

 

There are a few considerations that you’ll ultimately have to think about before you start writing your code for an infrastructure deployment that require  you to ultimately think of the end state for your configuration. This thought process can be valuable for determining things like least permissions and setting security to the exact state it should be in.

 

Things to have in place:

 

  1. Windows Server 2012 R2 on the target server for DSC deployment (domain joined).
  2. PS Version 4.0+ on your development environment (I’m using 5.0.10105.0 locally)
  3. Since we’re doing the test in Azure, I’ll be using Azure Cmdlets version 0.9.4  in my development environment. Note that this isn’t a requirement as DSC can be used both on premise and in the cloud.
  4. The extracted install kits on the server or in an accessible file share to the serve that will be configured by DSC.
    1.       AF Server 2014.
    2.       AF Client 2014

 

One thing I did do is modify the silent.ini file for the extract AF Server 2014 in order to use the local .\sqlexpress installed SQL Server upon silent installation. Also, I did ADDLOCAL=ALL.

 

Then, let’s think about the best practices around running an AF Server and write our code to follow that installation. The example here is a SQL Express and AF Server on the same node, communicating with the rest of a PI System on other servers.

 

  1. Use windows service accounts to run the AF Server
  2. Configure a windows service accounts to run the AF Server (and grant it least permissions on the SQL Server)

 

Here is our target configuration checklist that we’ll do automatically with PowerShell DSC. In this infrastructure configuration, the AF Server and SQL Server (Microsoft SQL Express) are installed on the same standalone node.

 

  1. Enable Windows Roles and Features required for an AF Server
  2. Open Windows Firewall Ports required for communication to and from the AF Server
  3. Install AF Server and AF Client
  4. Set the AF Server Service accounts

 

Just for brevity and to demonstrate only OSIsoft products, I’m going to install PI system products onto a machine already running SQL 2014 Express. If you wanted to install SQL express in the same script, the PowerShell DSC resource xSqlPs will allow you to bundle it all together. https://github.com/PowerShell/xSqlPs

To make this example fully run, we need 3 pieces of code:

 

The DSC Configuration which explicitly enumerates the setup, the environmental data which tells the configuration which nodes to deploy to, the pushing of the code up to Azure.

 

This can be simplified for on premise installations where you run the DSC configuration locally, or set up a pull server much like Chef or other configuration managers all you to do.

 

Here is the code for the DSC configuration piece:

 

###################### DSC Full Environment Configuration ######################
Configuration PIDSCConfiguration
{
  param
  (
  [String]$nodeName = 'localhost',
  [PSCredential]$Credential, #(An Administrator Cred).This is passed in via a Get-Credential Hash Table, at a runtime prompt
  [PSCredential]$AFCredential #(AF Server Service Account).This is passed in via a Get-Credential Hash Table, at a runtime prompt
  )
   
  Import-DscResource -Module xNetworking  
  Import-DscResource -Module xPSDesiredStateConfiguration
   
  #Setup for all machines assuming you split it into PI Data archive, AF Server etc further down.
  #Windows features. These cannot be duplicated in the Node by Roles section.
  Node $AllNodes.nodename
  { 
  ###################### Windows Roles and Features ######################
  # Just make sure the .NET framework is installed.
  # Install .NET Framework 3.5
  WindowsFeature NETFrameworkFeatures 
  {
  Ensure = "Present"
  Name = "NET-Framework-Core"
  IncludeAllSubFeature = $true
  }  

  # Install .NET Framework 4.5
  WindowsFeature NETFramework45Features
  {
  Ensure = "Present"
  Name = "NET-Framework-45-Features"
  IncludeAllSubFeature = $true
  }  
  } 
   
  #Setup only if the role is "AFServer" 
  Node $AllNodes.Where{$_.Role -eq "AF Server"}.NodeName
  {  
  ###################### Firewall Rules ######################
  #Firewall TCP Rules, these can be more restricted based on IP, other rules etc.
  xFirewall Firewall 
  { 
  Name = "PIAFRules" 
  DisplayName = "PI AF Server Access" 
  DisplayGroup = "PI AF Firewall Rule Group" 
  Ensure = "Present" 
  Access = "Allow" 
  State = "Enabled" 
  Profile = ("Domain", "Private", "Public") 
  Direction = "InBound" 
  LocalPort = ("5457", "5459") 
  Protocol = "TCP" 
  Description = "Allow Remote Access to the AF Server"  
  } 

  ###################### Move Install Kits ######################
  #Move installkits from Fileshare \\FileShare\OSISoft_InstallKits for now to machines you are installing on 
  #Move Sysinternals Suite over to Server
  #Note DSC Runs as SYSTEM so you need to add the Machine$ to the souce folder security tab for it to have access
  File DirectoryCopy
  {
  Ensure = "Present"  
  Type = "Directory"
  Recurse = $true 
  SourcePath = "\\XXXXXXXXX\OSISoft_InstallKits" #These are coming from another computer domain, but a network fileshare would work as well
  DestinationPath = "C:\OSISoft_InstallKits"  
  Force = $true  
  }

  ###################### Install Software ######################
  #Note that the machine already has a SQL Express installation on it.
  #It's possible to add this to the DSC script via the xSqlPs resource, omitted here for clarity
  #The Unzipped clients are also present, with PowerShell you can unzip them in the script as well
  #Install PI AF Server 2014 R2, AF Client
  #Modified Silent.Ini file to include .\SQLExpress and ADDLOCAL=ALL for the AFServer_x64.msi
  xPackage PIAFServer
  {
  Ensure = "Present" 
  Path = "C:\OSISoft_InstallKits\PI-AF-Server_2014-R2_\AFServer_2.6.1.6238\setup.exe" #Already Unzipped 
  Arguments = "-f C:\OSISoft_InstallKits\PI-AF-Server_2014-R2_\AFServer_2.6.1.6238\silent.ini"
  Name = "PI AF Server (x64) 2014 R2"
  ProductId = "" 
  DependsOn = @('[WindowsFeature]NETFrameworkFeatures','[WindowsFeature]NETFramework45Features') #Ex: Make sure .Net 3.5, 4.5 is installed first
  RunAsCredential = $Credential
  Credential = $Credential
  } 

  #Install PI AF Client 
  xPackage PIAFClient
  {
  Ensure = "Present" 
  Path = "C:\OSISoft_InstallKits\PI-AF-Client_2014-R2_\AFClient_2.6.1.6238\setup.exe" #Already Unzipped 
  Arguments = "-f C:\OSISoft_InstallKits\PI-AF-Client_2014-R2_\AFClient_2.6.1.6238\silent.ini"
  Name = "PI AF Client (x64) 2014 R2"
  ProductId = "" 
  DependsOn = '[xPackage]PIAFServer'
  RunAsCredential = $Credential
  Credential = $Credential
  } 
   
  ###################### Set Service Accounts ######################
  #Set Service PI AF SERVER to domain\afserviceaccount
  #Service to change credentials. xService DSC resource to create a new service.
  Service PIAFServerService
  {
  Name = 'AFService'
  DisplayName = 'PI AF Server 2.x Application Service'
  Ensure = 'Present'
  StartupType = 'Automatic'
  DependsOn = '[xPackage]PIAFServer'
  State = 'Running'
  Credential = $AFCredential
  }

  }
   
}

 

Here is the code for the environmental data:

 

@{
  #Node Specific Data
  AllNodes = @(
  #All servers need the following 
  @{ 
  NodeName = "*" 
  PSDSCAllowPlainTextPassword = $true
  }, 
  #Individual Servers
  @{ 
  NodeName = "TestAF" 
  Role = "AF Server" 
  }
  ); 
}

 

Below is the code to enable DSC and push the configuration and environmental data piece up to Azure. This would be the equivalent of doing a knife execute command in chef. Using PowerShell DSC in an Azure context, we install an extension on the VM and then pull the configuration from storage. In an on premise environment, this data could either be saved on the IIS Pull server, or stored locally on each machine in a push configuration:

 

#$Credential uses the Administrator account first in order to install files
#$AFServiceAccount uses the Domain\AFServiceAccount to run the AF Server 
$configurationArguments = @{ Credential = Get-Credential ; AFCredential = Get-Credential }

#Enable DSC on the VM and Set the configuration
$context = New-AzureStorageContext -StorageAccountName XXXXXXXXX -StorageAccountKey XXXXXXXXX

#The Path to my Configuration DSC Script, published to the Storage Context
Publish-AzureVMDscConfiguration -ConfigurationPath "C:\Program Files\WindowsPowerShell\scripts\PIDSCConfiguration.ps1" -StorageContext $context -Force

$vm = Get-AzureVM -Name "TestAF" -ServiceName "XXXXXXXXX" 

Set-AzureVMDscExtension -VM $vm -ConfigurationArchive "PIDSCConfiguration.ps1.zip" -ConfigurationName "PIDSCConfiguration" -ConfigurationDataPath "C:\Program Files\WindowsPowerShell\scripts\PIDSCEnvironmentalData.psd1" -StorageContext $context -ConfigurationArgument $configurationArguments | Update-AzureVM

#To Check the Status of your DSC Configuration
#Get-AzureVMDscExtension -VM $vm
#Get-AzureVMDscExtensionStatus -ServiceName XXXXXXXXX -Name XXXXXXXX
#(Get-AzureVMDscExtensionStatus -ServiceName XXXXXXXX -Name XXXXXXXX).Dscconfigurationlog

Now, after a few reboots and checks, your infrastructure is deployed as code!:

 

AF.PNG

 

Conclusion

Whew, so that may have seemed like a lot of work just to install an AF Server! But, once you have the code in place you have the ability to quickly reinstall everything in the event of disaster recovery. Also, whether you’re using PowerShell DSC, Chef, Puppet, Ansible, Salt or another flavor of configuration management software and infrastructure as code, being explicit with your code and exactly what you want installed with a PI system will make infrastructure management more accessible and less prone to error.

 

The code is also available at github:

https://github.com/epennaz/pidsc.git