Public/Deploy-SCOMAgent.ps1
|
Function Deploy-SCOMAgent { <# .DESCRIPTION Will install MOMAgent.msi on one or more target computers. Agent will need to be manually approved unless security settings allow automatic approval. .EXAMPLE # # This example will set two target computer names, prompt for a credential, then deploy the agent to the target computers. # The installation scriptblock will wait 120 seconds for the service to install and present a status of "Running" before returning the service status to the output. PS C:\> $Computers = "scorch1.contoso.com","devdb01.contoso.com" PS C:\> $MyCred = Get-Credential -UserName 'Contoso\tpaul' -Message "Enter Action credential (Local Admin on targets)" PS C:\> Deploy-SCOMAgent -USE_SETTINGS_FROM_AD 0 -USE_MANUALLY_SPECIFIED_SETTINGS 1 -MANAGEMENT_GROUP "SCOMLAB" -MANAGEMENT_SERVER_AD_NAME "MS01.CONTOSO.COM" -MANAGEMENT_SERVER_DNS "MS01.CONTOSO.COM" -ACTIONS_USE_COMPUTER_ACCOUNT 1 -NOAPM 1 -InstallCredential $MyCred -TargetComputer $Computers -WaitForServiceSeconds 120 .EXAMPLE # # This example will pipe the the array of computer names to the function. This is slower than passing the array as a parameter with '-TargetComputer'. # The installation scriptblock will wait 120 seconds for the service to install and present a status of "Running" before returning the service status to the output. PS C:\> $Computers = "scorch1.contoso.com","devdb01.contoso.com" PS C:\> $Computers | Deploy-SCOMAgent -USE_SETTINGS_FROM_AD 0 -USE_MANUALLY_SPECIFIED_SETTINGS 1 -MANAGEMENT_GROUP "SCOMLAB" -MANAGEMENT_SERVER_AD_NAME "MS01.CONTOSO.COM" -MANAGEMENT_SERVER_DNS "MS01.CONTOSO.COM" -ACTIONS_USE_COMPUTER_ACCOUNT 1 .EXAMPLE # # This example demonstrates how you would specify an alternate Default Action Account. This is a very, very rare requirement but here's an example anyway. PS C:\> $InstallCred = Get-Credential -UserName 'Contoso\tpaul' -Message "Enter Action credential (Local Admin on targets)" PS C:\> $DefaultActionCred = Get-Credential -UserName 'Contoso\svc_LowPrivAccount' -Message "Enter Default Action credential (for very, very rare low-priv scenarios)" PS C:\> Deploy-SCOMAgent -USE_SETTINGS_FROM_AD 0 -USE_MANUALLY_SPECIFIED_SETTINGS 1 -MANAGEMENT_GROUP "SCOMLAB" -MANAGEMENT_SERVER_AD_NAME "MS01.CONTOSO.COM" -MANAGEMENT_SERVER_DNS "MS01.CONTOSO.COM" -ACTIONS_USE_COMPUTER_ACCOUNT 0 -NOAPM 1 -TargetComputer "devdb01.contoso.com" -WaitForServiceSeconds 180 -DefaultActionAccountCredential $DefaultActionCred -InstallCredential $InstallCred .INPUTS Function accepts an array of computer names as piped input. .OUTPUTS Outputs objects for each installation attempt. .NOTES Author: Tyson Paul Blog: https://monitoringguys.com/2019/11/12/scomhelper/ History: 2020.05.13 - First version For additional info on manual installation parameters see: https://docs.microsoft.com/en-us/system-center/scom/manage-deploy-windows-agent-manually?view=sc-om-2019 #> [CmdletBinding(DefaultParameterSetName='Parameter Set 1', SupportsShouldProcess=$true, PositionalBinding=$false, HelpUri = 'http://www.microsoft.com/', ConfirmImpact='Medium')] Param ( # The account that will remotely execute the installation of the agent. This function relies on WSMAN therefore this credential must have rights to PSRemoting access to the target machines. This is not the Microsoft Monitoring Agent (HealthService) service credentials. Default: <current user creds> [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=0, ParameterSetName='Parameter Set 1')] [Alias("Credential")] [pscredential]$InstallCredential, # {0|1} Indicates whether the management group settings properties will be set on the command line. Use 0 if you want to set the properties at the command line. Use 1 to use the management group settings from Active Directory. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=1, ParameterSetName='Parameter Set 1')] [ValidateSet('0','1')] [string]$USE_SETTINGS_FROM_AD = 0, # {0|1} If USE_SETTINGS_FROM_AD=1, then USE_MANUALLY_SPECIFIED_SETTINGS must equal 0. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=2, ParameterSetName='Parameter Set 1')] [ValidateSet('0','1')] [string]$USE_MANUALLY_SPECIFIED_SETTINGS = 1, # Specifies the management group (name) that will manage the computer. [Parameter(Mandatory=$true, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=3, ParameterSetName='Parameter Set 1')] [string]$MANAGEMENT_GROUP, # Specifies the fully qualified domain name for the management server. To use a gateway server, enter the gateway server FQDN as MANAGEMENT_SERVER_DNS. [Parameter(Mandatory=$true, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=4, ParameterSetName='Parameter Set 1')] [ValidatePattern('(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)')] [string]$MANAGEMENT_SERVER_DNS, # Use this parameter if the computer's DNS and Active Directory names differ to set to the fully qualified Active Directory Domain Services name. [Parameter(Mandatory=$true, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=5, ParameterSetName='Parameter Set 1')] [ValidatePattern('(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)')] [string]$MANAGEMENT_SERVER_AD_NAME, # Sets the health service port number. It is rare to modify this and not advised. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=6, ParameterSetName='Parameter Set 1')] [ValidateRange(1025,65535)] [int]$SECURE_PORT = 5723, # Optional parameter. Use this parameter if you want to install the agent to a path other than the default installation path. Note that \Agent will be appended to this value. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=7, ParameterSetName='Parameter Set 1')] [System.IO.FileInfo]$INSTALLDIR, # Indicates whether the service will run as a specified user account (0) or the default 'LocalSystem' account (1). Default=1. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=8, ParameterSetName='Parameter Set 1')] [ValidateSet('0','1')] [string]$ACTIONS_USE_COMPUTER_ACCOUNT=1, # This is the Microsoft Monitoring Agent (HealthService) Default Action Account credentials if other than 'LocalSystem'. Use of an alternate credential is very rare and typically unnecessary. Use with caution. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=9, ParameterSetName='Parameter Set 1')] [pscredential]$DefaultActionAccountCredential, # {0|1} Optional parameter. Installs the Operations Manager agent without .NET Application Performance Monitoring. If you are using AVIcode 5.7, NOAPM=1 leaves the AVIcode agent in place. If you are using AVIcode 5.7 and install the Operations Manager agent by using momagent.msi without NOAPM=1, the AVIcode agent will not work correctly and an alert will be generated. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=10, ParameterSetName='Parameter Set 1')] [ValidateSet('0','1')] [string]$NOAPM=1, # One or more target computers FQDN, comma-separated. [Parameter(Mandatory=$True, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=11, ParameterSetName='Parameter Set 1')] [string[]]$TargetComputer, # The full path to the agent installation file. Default = '<SCOM Mgmt Server Install Directory>\AgentManagement\amd64\MOMAgent.msi' [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=12, ParameterSetName='Parameter Set 1')] [System.IO.FileInfo]$AgentMSIPath, # The number of seconds to wait for the service to appear after installation before returning from the remote PSSession. Default:180. [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$false, Position=13, ParameterSetName='Parameter Set 1')] [int]$WaitForServiceSeconds = 180 ) #region Begin Begin { $hash = @{} $hash.msi = $null $hash.outDir = @((Join-Path $env:Windir "Temp"),'C:\Temp') If (-NOT $AgentMSIPath){ $hash.InstallDirectory = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft Operations Manager\3.0\Setup" -Name "InstallDirectory").InstallDirectory $hash.agentPath = (Join-Path $hash.InstallDirectory 'AgentManagement\amd64\MOMAgent.msi' ) } Try{ $hash.fileObj = Get-Item -Path $hash.agentPath -ErrorAction STOP $hash.msi = [System.IO.File]::ReadAllBytes($hash.fileObj.FullName) } Catch { Throw "Failure getting MOMAgent.msi from path: [$($hash.agentPath)]! Either specify the correct path to the file or put the file at this location. " Return 1 } # amount of time to wait for HealthService service to enumerate in Services before returning from Scriptblock $hash.WaitForServiceSeconds = $WaitForServiceSeconds [bool]$hash.WhatIf = (-Not ($PSCmdlet.ShouldProcess($hash))) $Params = @{ USE_SETTINGS_FROM_AD = $USE_SETTINGS_FROM_AD USE_MANUALLY_SPECIFIED_SETTINGS = $USE_MANUALLY_SPECIFIED_SETTINGS MANAGEMENT_GROUP = $MANAGEMENT_GROUP MANAGEMENT_SERVER_DNS = $MANAGEMENT_SERVER_DNS MANAGEMENT_SERVER_AD_NAME = $MANAGEMENT_SERVER_AD_NAME SECURE_PORT = $SECURE_PORT INSTALLDIR = $INSTALLDIR ACTIONS_USE_COMPUTER_ACCOUNT = $ACTIONS_USE_COMPUTER_ACCOUNT NOAPM = $NOAPM } If ($DefaultActionAccountCredential){ $Params.ACTIONSUSER = $DefaultActionAccountCredential.GetNetworkCredential().UserName Try { $Params.ACTIONSDOMAIN = $DefaultActionAccountCredential.GetNetworkCredential().Domain }Catch{ $Params.ACTIONSDOMAIN = '' } $Params.ACTIONSPASSWORD = $DefaultActionAccountCredential.GetNetworkCredential().Password } #------------------------------------------------- #region InstallationBlock $scriptBlock = [scriptblock]{ Param ( $hash, $Params ) # Buid custom object to return. Initially assume it doesn't exist. $obj = New-Object -TypeName PSCUSTOMOBJECT $obj | Add-Member -MemberType NoteProperty -Name 'ServerName' -Value "$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)" $obj | Add-Member -MemberType NoteProperty -Name 'ServiceName' -Value 'HealthService' $obj | Add-Member -MemberType NoteProperty -Name 'Status' -Value 'NOT FOUND' $obj | Add-Member -MemberType NoteProperty -Name 'Comment' -Value '' Try{ # If service already exists, do nothing. Return. $Service = Get-Service -Name HealthService -ErrorAction Stop $obj.Status = $Service.Status.ToString() $obj.Comment = "Service already exists. No action taken." Return $obj }Catch { # Assume service doesn't appear yet. Proceed to install. } # Write the .msi to temp dir on remote target. Try { $exePath = (Join-Path $hash.outDir[0] $hash.fileObj.Name) [System.IO.File]::WriteAllBytes($exePath, $hash.msi) }Catch { Try{ $exePath = (Join-Path $hash.outDir[1] $hash.fileObj.Name) [System.IO.File]::WriteAllBytes($exePath, $hash.msi) } Catch { $obj.Comment = "Remote connection successful but failed to copy bits to path: [$($exePath)]. Service not installed. Error was $_" Return $obj } } #region Build CMD string $CMD = "msiexec.exe /i $exePath /passive /l*v $(Split-Path $exePath -Parent)\OMAgentinstall.log" ForEach ($Key in @($Params.Keys)) { switch ($Key) { # Wrap InstallDir in double quotes {($Key -match 'INSTALLDIR') -AND ($Params.$Key.LENGTH -ge 1) } {$CMD += " $($Key)=```"$($Params.$_)```""; Continue; } {$Params.$_.LENGTH -ge 1 } {$CMD += " $($Key)=$($Params.$_)"} Default {Continue} } } # it makes sense to hardcode this param=1 $CMD += " AcceptEndUserLicenseAgreement=1" #endregion Build CMD #Install Command If ($hash.WhatIf){ $obj.Status = "Running (WHATIF)" $obj.Comment = "(WHATIF) Service installed successfully. Bits were actually written to this path successfully: [$($exePath)]" Return $obj } Else { # Get down to business! Invoke-Expression $CMD #Set timer $ServiceTimer = [System.Diagnostics.Stopwatch]::StartNew() Do{ Try{ $Service = Get-Service -Name HealthService -ErrorAction Stop }Catch { # Assume service doesn't appear yet Start-Sleep -Seconds 3 } }While (($ServiceTimer.Elapsed.TotalSeconds -lt $hash.WaitForServiceSeconds) -AND ([string]($Service.Status) -ne "Running") ) $ServiceTimer.Stop() If ([bool]$Service ){ $obj.Status = [string]($Service.Status) $obj.Comment = "Service installed successfully." } Else { $obj.Comment = "Service not installed/found. Command line: `n$($CMD) `n`nTimerSeconds:$($ServiceTimer.Elapsed.TotalSeconds), WaitForServiceSeconds:$($hash.WaitForServiceSeconds), ServiceStatus:$($Service.Status), ServiceRetrievedSuccess:$([bool]$Service)." } } Return $obj } #endregion #region InstallationBlock #------------------------------------------------- } #endregion Begin #region Process Process { # ----------- Configure remote session ----------- #region ConfigureSessionRemotely $SessionConfigName = 'MaxDataSizeMB500' $ConfigureSessionBlock = [scriptblock]{ Param( $SessionConfigName ) Try { # If session option is already registered, get it $var = Get-PSSessionConfiguration -Name $SessionConfigName -ErrorAction Stop }Catch { $var = Register-PSSessionConfiguration -Name $SessionConfigName } $var = Set-PSSessionConfiguration -Name $SessionConfigName -MaximumReceivedDataSizePerCommandMB 500 -MaximumReceivedObjectSizeMB 500 } $SessionParams = @{ ComputerName = $TargetComputer } If ($InstallCredential) { $SessionParams.Credential = $InstallCredential } $Session = New-PSSession @SessionParams #Set session config on remote Try { Invoke-Command -Session $Session -ArgumentList $SessionConfigName -ScriptBlock $ConfigureSessionBlock -ErrorAction Stop } Catch { Write-Warning "WinRM Reset will typically cause an 'I/O operation has been aborted...' message." } Remove-PSSession -Session $Session #endregion ConfigureSessionRemotely #------------------------------------------------- #------------------------------------------------- #region SetMyLocalSessionConfig Try { # If session option is already registered, get it $var = (Get-PSSessionConfiguration -Name $SessionConfigName -ErrorAction Stop) }Catch { $var = Register-PSSessionConfiguration -Name $SessionConfigName } $var = Set-PSSessionConfiguration -Name $SessionConfigName -MaximumReceivedDataSizePerCommandMB 500 -MaximumReceivedObjectSizeMB 500 #endregion SetMyLocalSessionConfig #------------------------------------------------- #------------------------------------------------- #region PrepareInstallSession $SessionName = "MySCOMInstallSession" $SessionParams = @{ Name = $SessionName ComputerName = $TargetComputer ConfigurationName = $SessionConfigName } If ($InstallCredential) { $SessionParams.Credential = $InstallCredential } $Session = New-PSSession @SessionParams #endregion PrepareInstallSession #------------------------------------------------- # Execute installation $InvokeParams = @{ Session = $Session ScriptBlock = $scriptBlock ArgumentList = @($hash, $Params) Verbose = $true } $Result = Invoke-Command @InvokeParams Remove-PSSession -Session $Session Return $Result } #region Process End { } } |