Using PowerShell for automated UNIX/Linux Agent Discovery

PowerShell cmdlets for administration of UNIX/Linux agents were added in the System Center 2012 release of Operations Manager. There is good documentation available on the cmdlet use, but a basic discovery script might look something like this:

$SSHCredential=Get-SCXSSHCredential
$WSCredential=Get-Credential
$Pool = Get-SCOMResourcePool -DisplayName “All Management Servers Resource Pool”
$DiscResult = Invoke-SCXDiscovery -name $HostName -ResourcePool $Pool -WsManCredential $WSCredential -SshCredential $SSHcredential
$DiscResult | Install-scxagent

In this example, the Invoke-SCXDiscovery cmdlet is provided the following parameters:

  • $Hostname – the fqdn of the agent to discover
  • $Pool – the Resource Pool object used to discover and manage the agent, from Get-SCOMResourcePool
  • $WSCredential – a PSCredential object used for WSMan authentication, from Get-Credential
  • $SSHCredential – an ssh Credential object used for ssh authentication, from Get-SCXSSHCredential

If you have tried a PowerShell discovery like this, you’ll know that both Get-Credential and Get-SCXSSHCredential prompt you for credential input and don’t allow specification of passwords as command-line arguments.  This is for good reason, as plain-text scripts are a bad place to store passwords. However, this does have the effect of limiting your ability to truly automate UNIX/Linux agent discovery.  Well, with a bit of extra scripting, you can actually embed your credentials in a script in a fairly secure manner.

This article does a great job explaining how to securely write a password to a file, and then retrieve it from a script.  The steps to do this are:

  1. Logged in as the user that will run the script, create a credential object with:  $Credential = Get-Credential
  2. Write this as a secure string to a file:
    $credential.Password | ConvertFrom-SecureString | Set-Content $env:userprofile\password.txt

Now, this password can be read back into a script (but only if the script is run with the same user that wrote the password to the file), by using the following scriptlet:

$wsmanuser=”monuser”
$wsmanpassword =  Get-Content $env:userprofile\password.txt | ConvertTo-SecureString
$WSCredential = New-Object System.Management.Automation.PSCredential ($wsmanuser, $wsmanpassword)

Using this method, we can securely create the credential object to use as our WSManCredential value without a prompt, but Invoke-SCXDiscovery also needs an ssh Credential.  The ssh Credential is a bit more involved, but can be done in a similar fashion.

A function to create the ssh Credential object, using encrypted passwords stored in files is:

function Get-SCXSSHCredentialFromScript{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$True)]
[string]$UserName,
[string]$PassphraseFile,
[string]$SSHKeyFile,
[string]$SuPasswordFile,
[string]$ElevationType
)

process {
$SSHcredential=””
$scred=””
$SSHcredential = New-Object Microsoft.SystemCenter.CrossPlatform.
ClientLibrary.CredentialManagement.Core.CredentialSet
$scred = New-Object Microsoft.SystemCenter.CrossPlatform.
ClientLibrary.CredentialManagement.Core.PosixHostCredential
$scred.Usage = 2
$scred.PrincipalName = $username

if ($PassphraseFile.Length -gt 0){
$sPassphrase=Get-Content $PassphraseFile | ConvertTo-SecureString
$scred.Passphrase = $sPassphrase
}

if ($SSHKeyFile.Length -gt 0)  {
$scred.KeyFile = $SSHKeyFile
Write-Host “Validating SSH Key”
$scred.ReadAndValidateSshKey()
}

#add posixhost credential to credential set
$SSHcredential.Add($scred)

if ($elevationType.Equals(“su”)) {
$sucred = New-Object Microsoft.SystemCenter.CrossPlatform.
ClientLibrary.CredentialManagement.Core.PosixHostCredential
$sucred.Usage = 32 #su elevation
$sucred.PrincipalName = “root”
$sucred.Passphrase = Get-Content $SUPasswordFile | ConvertTo-SecureString
$SSHcredential.Add($sucred)
}

if ($elevationType.Equals(“sudo”)) {
$sudocred = New-Object Microsoft.SystemCenter.CrossPlatform.
ClientLibrary.CredentialManagement.Core.PosixHostCredential
$sudocred.Usage = 16 #su elevation
$SSHcredential.Add($sudocred)
}
Return $SSHCredential
}
}

With this function defined, it can be used like this:

PS C:\Users\Administrator> $SSHCredential=Get-SCXSSHCredentialFromScript -username:monuser -PassphraseFile:$env:userprofile\password.txt –ElevationType:sudo

PS C:\Users\Administrator> $SSHCredential
SshUserName      : monuser
SshElevationType : sudo
Credentials      : {, }
Count            : 2
IsSSHKey         : False
Usage            : 0

Of course, this needs to be run in an OpsMgr shell, or the script needs to be prefaced with:

Import-Module “C:\Program Files\System Center 2012\Operations Manager\Powershell\OperationsManager\OperationsManager”
New-SCOMManagementGroupConnection localhost;

So, now we have snippets to create WSMan and ssh Credential objects, using an ecrypted password stored in a file.  Building on this, we can define a function to invoke UNIX/Linux discovery and install the agent:

function DiscoverSCXAgents{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$True)]
[string]$Hostname
)
$PassphraseFile=”$env:userprofile\password.txt”
$SSHCredential=Get-SCXSSHCredentialFromScript -username:monuser -PassphraseFile:$PassphraseFile –ElevationType:sudo
$wsmanuser=”monuser”
$wsmanpassword =  Get-Content $PassphraseFile | ConvertTo-SecureString
$WSCredential = New-Object System.Management.Automation.PSCredential ($wsmanuser, $wsmanpassword)
$Pool = Get-SCOMResourcePool -DisplayName “All Management Servers Resource Pool”
Write-Output “Attempting discovery of $Hostname”
$DiscResult = Invoke-SCXDiscovery -name $HostName -ResourcePool $Pool -WsManCredential $WSCredential -SshCredential $SSHcredential
$DiscResult | fl Succeeded, ErrorData
$DiscResult | Install-scxagent
}

Testing out the new function:

DiscoverSCXAgents “lnx-db-007.contoso.com”

Attempting discovery of lnx-db-007.contoso.com

Name            AgentVersion  ManagementPackPlatformIdentifier  Id                                  
—-            ————  ——————————–  —                                  
lnx-db-007.c… 1.4.0-906     Microsoft.Linux.SLES.11           c5944ea1-e4f4-1908-ea15-d5be6ba7d14e

 

And with that, we can use an Orchestrator runbook to call our discovery script and fully automate UNIX/Linux agent discovery.

About Kristopher Bash
Kris is a Senior Program Manager at Microsoft, working on UNIX and Linux management features in Microsoft System Center. Prior to joining Microsoft, Kris worked in systems management, server administration, and IT operations for nearly 15 years.

8 Responses to Using PowerShell for automated UNIX/Linux Agent Discovery

  1. Garion B says:

    Kris,
    I can’t tell you how much I appreciate this post and it has helped me greatly in my endeavors to fully automate the Linux installs using Orchestrator and SCOM. With that said, I am still having difficulty getting this to work with Orchestrator. Here is the script I am using and this works fine when I run it from Powershell directly;

    {
    $ErrorActionPreference = “Stop”
    try
    {

    Import-module OperationsManager

    function Get-SCXSSHCredentialFromScript{
    [CmdletBinding()]
    param
    (
    [Parameter(Mandatory=$True)]
    [string]$UserName,
    [string]$Passphrase,
    #[string]$SSHKeyFile,
    [string]$SuPassword,
    [string]$ElevationType
    )

    process {
    $SSHcredential=””
    $scred=””
    $SSHcredential = New-Object Microsoft.SystemCenter.CrossPlatform.ClientLibrary.CredentialManagement.Core.CredentialSet
    $scred = New-Object Microsoft.SystemCenter.CrossPlatform.ClientLibrary.CredentialManagement.Core.PosixHostCredential
    $scred.Usage = 2

    $scred.PrincipalName = $username

    if ($Passphrase.Length -gt 0){
    $sPassphrase=ConvertTo-SecureString “$Passphrase” -AsPlainText -Force
    $scred.Passphrase = $sPassphrase
    }

    if ($SSHKeyFile.Length -gt 0)
    {
    $scred.KeyFile = $SSHKeyFile
    Write-Host “Validating SSH Key”
    $scred.ReadAndValidateSshKey()
    }

    #add posixhost credential to credential set
    $SSHcredential.Add($scred)

    if ($elevationType.Equals(“su”))
    {
    $sSUPassword=ConvertTo-SecureString “$SUPassword” -AsPlainText -Force
    $sucred = New-Object Microsoft.SystemCenter.CrossPlatform.ClientLibrary.CredentialManagement.Core.PosixHostCredential
    $sucred.Usage = 32 #su elevation
    $sucred.PrincipalName = “root”
    $sucred.Passphrase = $SUPassword
    $SSHcredential.Add($sucred)
    }

    if ($elevationType.Equals(“sudo”))
    {
    $sudocred = New-Object Microsoft.SystemCenter.CrossPlatform.ClientLibrary.CredentialManagement.Core.PosixHostCredential
    $sudocred.Usage = 16 #su elevation
    $SSHcredential.Add($sudocred)
    }
    Return $SSHCredential
    }
    }

    $username = “”
    $password = “”
    $secstr = New-Object -TypeName System.Security.SecureString
    $password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
    $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
    $PrimaryMgmtServer = “”
    $sshcredential = Get-SCXSSHCredentialFromScript -username: -Passphrase: –ElevationType:sudo
    New-SCOMManagementGroupConnection -ComputerName $PrimaryMgmtServer
    $mypool = Get-SCOMResourcePool “LinuxPool”
    Invoke-SCXDiscovery -Name “\`d.T.~Ed/{4DEB0A89-36FF-402E-9EB5-798E8556E912}.{25F33803-055F-4CBD-B836-E801C108AE6B}\`d.T.~Ed/” -ResourcePool $mypool -WSManCredential $cred -SshCredential $sshcredential | Install-SCXAgent
    }
    catch
    {
    Throw $_.Exception
    }

    }| Out-file c:\err1Script.ps1

    When I put the SAME script into a .NET script activity in Orchestrator, the script runs but the install never happens and I never receive any errors. This is my last step towards a fully automated install, so any help would be appreciated. Even better would be if you had a working example. Thanks for all your hard work with the XPlat monitoring!

    Garion

    • Kristopher Bash says:

      I suspect the issue has to do with the version of the PowerShell host. The UNIX/Linux Cmdlets require PowerShell V3 (v4 CLR), but the .NET activity uses CLR v2. There are two potential workarounds: Something like this:
      http://karlprosser.com/coder/2012/04/16/calling-powershell-v3-from-orchestrator-2012/

      or saving the script as a .ps1 in the file system and calling PowerShell.exe as a “Run Program” instead of a .NET script. If you go with the Run Program route, the parameters would be something like:
      Program Path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
      Parameters: D:\scriptlibrary\discoverscxagent.ps1 {Computer Name variable}
      Working folder: D:\ScriptLibrary

      • Garion says:

        Kris, thanks for your detailed explanation. I apologize as I had thought I had replied to this thread. I just wanted to let you know that utilizing method #2, “run program” method, worked for me. I am not trying to figure out how to call the runbook via webservice. Thanks as always for your help!

  2. Csa says:

    This is a very good example and i tried to implement it. unfortunately i found some problem on the SSH credential part:
    New-Object : Cannot find type [Microsoft.SystemCenter.CrossPlatform.]: make sure the assembly containing this type is
    loaded.

  3. Csa says:

    I still have a question. please correct me if I’m wrong but before using the get-scxssh… we need to create the same password file for the unix user:

    $Credential = Get-Credential
    $credential.Password | ConvertFrom-SecureString | Set-Content $env:c:\temp\sshpassword.txt

    without this it is not possible to use the UNIX user for authentication on the linux machine

    • Csa says:

      $Credential = Get-Credential UNIXUSERNAME
      $credential.Password | ConvertFrom-SecureString | Set-Content $env:c:\temp\sshpassword.txt

  4. Csa says:

    sorry for the many replies, but i still have a problem:
    Succeeded : False
    ErrorData : Microsoft.SystemCenter.CrossPlatform.ClientLibrary.MPAbstractions.WSManAuthenticationErrorException: The agent responded to the request but the WSMan connection failed due to:
    Access is Denied.

Leave a reply to Csa Cancel reply