crazy_crash_30

I know this will seem nit-picky, but coming from a developer background I tend to look at everything through the eyes of programming.  That goes for infrastructure too, which is ironic that it’s all turning into software, with virtualization, cloud services, SDN’s and IaaS and so on.  So, one thing that short-circuits my OCD tripwire is seeing scripts that hard-code paths and other configuration settings.  Not only can these things change from one environment to the next, they almost always do!

Rarely have I seen a Configuration Manager site where the site server is installed to %ProgramFiles%\….  In fact, of the last 32 sites I’ve worked with, NONE were even installed on the C: drive.  That’s not a good or bad thing, it just “is”.  So, when I see someone post a code snippet that does something like this…

$pth = "C:\Program Files\Microsoft System Center\Configuration Manager"

...or...

$sitecode = "ABC"
$sitedb = "CM_ABC"

I start slowly shredding a phone book with my teeth, as my dog looks on in horror.  What a mess.  So please, at least give this a try?

For any references you need to make to the SCCM installation folder root, or the console folder (e.g. to fetch the SCCM .PSD1 cmdlet goodies), or connect to the SMS Provider, look no further than your friendly neighborhood registry.  Yes!  The Registry!  That odd little beast that you’ve made fun of since Windows 95.  It contains a lot of great stuff actually.

Things to keep in mind:

  • PowerShell queries against the Registry can be done locally, or remotely (as long as permissions, firewalls and services are not getting in the way)
  • PowerShell queries against the Registry are typically faster than connecting to databases and poking around, especially for very VERY small result sets (like a site code)

Getting the SCCM Installation Path

<#
.EXAMPLE
  Get-CmSiteInstallPath -ComputerName "cm01.contoso.com"
#>

function Get-CmSiteInstallPath {
  param (
    [parameter(Mandatory=$False)] [string] $ComputerName = ""
  )
  if ($ComputerName -ne "") {
    $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ComputerName)
    $regKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\SMS\setup")
    $regKey.GetValue("Installation Directory")
  }
  else {
    $x = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\setup"
    $x.'Installation Directory'
  }
}

Getting the SCCM Site Code

This information can be fetched from several locations in the Registry.  For example, HKLM\SOFTWARE\Microsoft\SMS\DP, or HKLM\SOFTWARE\Microsoft\SMS\Identification.

<#
.EXAMPLE
  Get-CmSiteCode -ComputerName "cm01.contoso.com"
#>

function Get-CmSiteCode {
  param (
    [parameter(Mandatory=$False)] [string] $ComputerName = ""
  )
  if ($ComputerName -ne "") {
    $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ComputerName)
    $regKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\SMS\Identification")
    $regKey.GetValue("Site Code")
  }
  else {
    $x = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\Identification"
    $x.'Site Code'
  }
}

Getting the SQL Server instance

Note: This is a bit interesting.  If the value “Service Name” is “MSSQLSERVER” it denotes a default SQL instance.  If it has any other value, it’s a named instance.  Also, you can fetch the Database Name and SQL Server Name values from HKLM\SOFTWARE\Microsoft\SMS\MP, but that branch doesn’t include a value for identifying the SQL instance.

<#
.EXAMPLE
  Get-CmSiteDBInstance -ComputerName "cm01.contoso.com"
#>

function Get-CmSiteDBInstance {
  param (
    [parameter(Mandatory=$False)] [string] $ComputerName = ""
  )
  if ($ComputerName -ne "") {
    $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ComputerName)
    $regKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\SMS\SQL Server")
    $regKey.GetValue("Service Name")
  }
  else {
    $x = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\SQL Server"
    $x.'Service Name'
  }
}

Getting the SQL Server SCCM Database Name

<#
.EXAMPLE
  Get-CmSiteDBName -ComputerName "cm01.contoso.com"
#>

function Get-CmSiteDBName {
  param (
    [parameter(Mandatory=$False)] [string] $ComputerName = ""
  )
  if ($ComputerName -ne "") {
    $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ComputerName)
    $regKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\SMS\SQL Server")
    $regKey.GetValue("Database Name")
  }
  else {
    $x = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\SQL Server"
    $x.'Database Name'
  }
}

Getting the SQL Service Ports

<#
.EXAMPLE
  Get-CmSiteDBPort -ComputerName "cm01.contoso.com" -PortName Broker
#>

function Get-CmSiteDBPort {
  param (
    [parameter(Mandatory=$False)] [string] $ComputerName = "",
    [parameter(Mandatory=$False)] 
    [ValidateSet('Service','Broker')]
    [string] $PortName = 'Service'
  )
  if ($PortName -eq 'Service') {
    $port = 'Port'
  }
  else {
    $port = 'SSBPort'
  }
  if ($ComputerName -ne "") {
    $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ComputerName)
    $regKey = $Reg.OpenSubKey("SOFTWARE\Microsoft\SMS\SQL Server")
    $regKey.GetValue($port)
  }
  else {
    $x = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\SQL Server"
    $x.'$port'
  }
}

Summary

Ignoring the possibility that you could easily combine these into an aggregate function with the result target mapped as another [parameter], and branching the execution using an array of key/values, or a switch() block, blah blah blah, the main point you shouldn’t get distracted from is that paths, versions, locations, connections, etc. are typically footprint values on a given machine.  By “footprint value”, I mean that they are defined when the pertinent software is installed, making it’s “footprint” on the computer.  Runtime is a different animal entirely.  In any case, the main point I’m trying to get across is NOT to hard-code things if you can obtain them at runtime.

 

Advertisements

One thought on “Stop Hard-Coding! #SCCM

  1. Thanks for a great article; I already agree. I have been using the SMS_ADMIN_UI_PATH environment variable to import the PowerShell module and accepting the site code as a param.

    Import-Module ($env:SMS_ADMIN_UI_PATH.Substring(0,$env:SMS_ADMIN_UI_PATH.Length – 5) + ‘\ConfigurationManager.psd1’)

    I think will update my scripts to use this registry method.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s