Using PowerShell for automated UNIX/Linux Agent Discovery
December 6, 2012 8 Comments
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:
- Logged in as the user that will run the script, create a credential object with: $Credential = Get-Credential
- 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 = $usernameif ($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.
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
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
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!
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.
I found the problem. There was a space in the object type 🙂
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
$Credential = Get-Credential UNIXUSERNAME
$credential.Password | ConvertFrom-SecureString | Set-Content $env:c:\temp\sshpassword.txt
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.