PowerShell Example: Create and Install an Agent
Goal
Provide an example PowerShell script that invokes the REST API and that performs a number of tasks, including:
- Creates credentials
- Creates Windows and Linux patch groups
- Creates an agent policy with Windows and Linux tasks
- Pushes an agent to all machines in a machine group
- Invokes a task on an agent machine
Example Script
cls ################################################################################# # # DISCLAIMER: EXAMPLE ONLY # # Execute this example at your own risk. The console, target machines and # databases should be backed up prior to executing this example. Ivanti does # not warrant that the functions contained in this example will be # uninterrupted or free of error. The entire risk as to the results and # performance of this example is assumed by the person executing the example. # Ivanti is not responsible for any damage caused by this example. # ################################################################################# # $isPS2 = $PSVersionTable.PSVersion.Major -eq 2 if($isPS2) { [void][Reflection.Assembly]::LoadWithPartialName("System.Security") } else { Add-Type -AssemblyName "System.Security" > $null } # credentials for remote invokation of REST APIs $remoteConsoleUsername = "remoteConsoleUsername" $remoteConsolePassword = ConvertTo-SecureString "remoteConsolePassword" -AsPlainText -Force $script:remoteConsoleCredential = New-Object -TypeName "PSCredential" -ArgumentList @($remoteConsoleUsername, $remoteConsolePassword) # helper URIs for ISeC REST API $consoleName = "consoleName" $Uris = @{ Agents = "https://$($consoleName):3121/st/console/api/v1.0/agents" AgentDeployment = "https://$($consoleName):3121/st/console/api/v1.0/agents/deployment" CertificateConsole = "https://$($consoleName):3121/st/console/api/v1.0/configuration/certificate" Credentials = "https://$($consoleName):3121/st/console/api/v1.0/credentials" LinuxPatchDeploymentConfigurations = "https://$($consoleName):3121/st/console/api/v1.0/linux/patch/deploymentconfigurations" LinuxPatchGroups = "https://$($consoleName):3121/st/console/api/v1.0/linux/patch/groups" LinuxPatchScanConfigurations = "https://$($consoleName):3121/st/console/api/v1.0/linux/patch/scanconfigurations" MachineGroups = "https://$($consoleName):3121/st/console/api/v1.0/machinegroups" PatchGroups = "https://$($consoleName):3121/st/console/api/v1.0/patch/groups" Policies = "https://$($consoleName):3121/st/console/api/v1.0/policies" SessionCredentials = "https://$($consoleName):3121/st/console/api/v1.0/sessioncredentials" } #encrypts an array of bytes using RSA and the console certificate function Encrypt-RSAConsoleCert { param ( [Parameter(Mandatory=$True, Position = 0)] [Byte[]]$ToEncrypt ) try { $certResponse = Invoke-RestMethod $Uris.CertificateConsole -Method Get -Verbose -Credential $script:remoteConsoleCredential [Byte[]] $rawBytes = ([Convert]::FromBase64String($certResponse.derEncoded)) $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @(,$rawBytes) $rsaPublicKey = $cert.PublicKey.Key; $encryptedKey = $rsaPublicKey.Encrypt($ToEncrypt, $True); return $encryptedKey } finally { $cert.Dispose(); } } # creates the body for creating credentials function Create-CredentialRequest { param ( [Parameter(Mandatory=$True, Position=0)] [String]$FriendlyName, [Parameter(Mandatory=$True, Position=1)] [String]$UserName, [Parameter(Mandatory=$True, Position=2)] [ValidateNotNull()] [SecureString]$Password ) $body = @{ "userName" = $UserName; "name" = $FriendlyName; } $bstr = [IntPtr]::Zero; try { # Create an AES 128 Session key. $algorithm = [System.Security.Cryptography.Xml.EncryptedXml]::XmlEncAES128Url $aes = [System.Security.Cryptography.SymmetricAlgorithm]::Create($algorithm); $keyBytes = $aes.Key; # Encrypt the session key with the console cert $encryptedKey = Encrypt-RSAConsoleCert -ToEncrypt $keyBytes $session = @{ "algorithmIdentifier" = $algorithm; "encryptedKey" = [Convert]::ToBase64String($encryptedKey); "iv" = [Convert]::ToBase64String($aes.IV); } # Encrypt the password with the Session key. $cryptoTransform = $aes.CreateEncryptor(); # Copy the BSTR contents to a byte array, excluding the trailing string terminator. $size = [System.Text.Encoding]::Unicode.GetMaxByteCount($Password.Length - 1); $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password) $clearTextPasswordArray = New-Object Byte[] $size [System.Runtime.InteropServices.Marshal]::Copy($bstr, $clearTextPasswordArray, 0, $size) $cipherText = $cryptoTransform.TransformFinalBlock($clearTextPasswordArray, 0 , $size) $passwordJson = @{ "cipherText" = $cipherText; "protectionMode" = "SessionKey"; "sessionKey" = $session } } finally { # Ensure All sensitive byte arrays are cleared and all crypto keys/handles are disposed. if ($clearTextPasswordArray -ne $null) { [Array]::Clear($clearTextPasswordArray, 0, $size) } if ($keyBytes -ne $null) { [Array]::Clear($keyBytes, 0, $keyBytes.Length); } if ($bstr -ne [IntPtr]::Zero) { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } if ($cryptoTransform -ne $null) { $cryptoTransform.Dispose(); } if ($aes -ne $null) { $aes.Dispose(); } } $body.Add("password", $passwordJson) return $Body } # create body for creating session credential $encryptedRemoteConsoleCredential = Create-CredentialRequest -FriendlyName "Encryped" -UserName $remoteConsoleUsername -Password $remoteConsolePassword try { # create session credential $sessionCredential = Invoke-RestMethod $Uris.SessionCredentials -body ($encryptedRemoteConsoleCredential.password | ConvertTo-Json -Depth 20) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # create windows patch group and add by multiple CVEs $windowsPatchGroup = Invoke-RestMethod -Uri $Uris.PatchGroups -Body( @{ Name = "Windows Patch Group" } | ConvertTo-Json ) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST $windowsPatchesResponse = Invoke-RestMethod ($windowsPatchGroup.links.self.href + "/patches/Cves") -body ( @{ Cves = "CVE-2019-0536","CVE-2017-11305"} |ConvertTo-Json -Depth 20) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # create linux patch group and add by multiple CVes $linuxPatchGroup = Invoke-RestMethod -uri $Uris.LinuxPatchGroups -Body ( @{ Name = "Linux Patch Group" } | ConvertTo-Json ) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST $linuxPatchesResponse = Invoke-RestMethod ($linuxPatchGroup.links.self.href + "/patches/Cves") -body ( @{ Cves ="CVE-2017-18267","CVE-2017-5461" } | ConvertTo-Json -Depth 20) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # create linux scan and deploy configurations $linuxScanConfiguration = Invoke-RestMethod -Uri $Uris.LinuxPatchScanConfigurations -Body ( @{ Name = "Linux Scan Configuration"; Filter = @{ PatchGroupIds = @( $($linuxPatchGroup.id) ) } } | ConvertTo-Json -Depth 20 ) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST $linuxDeployConfiguration = Invoke-RestMethod -Uri $Uris.LinuxPatchDeploymentConfigurations -Body ( @{ Name = "Linux Deployment Configuration"; }| ConvertTo-Json -Depth 20 ) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # create policy $policy = Invoke-RestMethod -Uri $Uris.Policies -Body ( @{ Name = "Policy"; }| ConvertTo-Json -Depth 20 ) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # create linux patch task $linuxPatchTaskName = "Demo Linux Patch Task"; $linuxPatchTask = Invoke-RestMethod -Uri ($policy.links.tasks.href + "/linuxpatch") -Body ( @{ Name = $linuxPatchTaskName; DeploymentEnabled = $false; ScanConfigurationId = $linuxScanConfiguration.Id }| ConvertTo-Json -Depth 20 ) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # create windows patch task $windowsPatchTaskName = "Demo Windows Patch Task"; $windowPatchTask = Invoke-RestMethod -Uri ($policy.links.tasks.href + "/windowspatch") -Body ( @{ Name = $windowsPatchTaskName ; DeploymentEnabled = $false; }| ConvertTo-Json -Depth 20 ) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # create credentials for accessing both linux and windows machines $rootBody = Create-CredentialRequest -FriendlyName "root" -UserName "root" -Password $remoteConsolePassword $rootCred = Invoke-RestMethod $Uris.Credentials -Method Post -body ( $rootBody | ConvertTo-Json -Depth 20) -Credential $script:remoteConsoleCredential -ContentType 'application/json' $protectRemoteConsoleCredential = Invoke-RestMethod $Uris.Credentials -Method Post -body ( $encryptedRemoteConsoleCredential | ConvertTo-Json -Depth 20) -Credential $script:remoteConsoleCredential -ContentType 'application/json' # share and unshare credential with service example Invoke-RestMethod $rootCred.links.sharewithservice.href -Credential $script:remoteConsoleCredential -Method POST Invoke-RestMethod $rootCred.links.sharewithservice.href -Credential $script:remoteConsoleCredential -Method DELETE # create machine group with both linux and windows machines $linuxIPAddress = "linuxIPAddress"; $windowsMachineName = "WindowsMachineName" $machineGroupBody = @{ name = "Machine group" CredentialId = $rootCred.id discoveryFilters = @( @{ category = "IPAddress"; name = $linuxIPAddress; }, @{ category = "MachineName"; name = $windowsMachineName; AdminCredentialId = $protectRemoteConsoleCredential.id; }) } $machineGroup = Invoke-RestMethod $Uris.MachineGroups -Body ( $machineGroupBody | ConvertTo-Json -Depth 20) -Credential $script:remoteConsoleCredential -ContentType 'application/json' -Method POST # push an agent to all systems in created machine group with the created policy $agentInput = @{ PolicyId = $policy.Id; MachineGroupIds = @($machineGroup.Id); } $startTime = [DateTime]::Now $TimeoutMinutes = 5 $agentDeployment = Invoke-WebRequest $Uris.AgentDeployment -Method Post -Body ( $agentInput | ConvertTo-Json -Depth 20 ) -UseBasicParsing -Credential $script:remoteConsoleCredential -ContentType 'application/json' Write-Verbose -Verbose ("Installing agent to machine group " + $machineGroup.name) #wait for agent deployment to complete $agentOperationLocation = $agentDeployment.Headers["Operation-Location"] $agentOperationResult = Invoke-RestMethod $agentOperationLocation -Method Get -Credential $script:remoteConsoleCredential while ($agentOperationResult.Status -eq 'Running' -or $agentOperationResult.Status -eq 'NotStarted') { if ([DateTime]::Now -gt $startTime.AddMinutes($TimeoutMinutes)) { throw "Timed out waiting for operation to complete" } Start-Sleep 5 $agentOperationResult = Invoke-RestMethod $agentOperationLocation -Method Get -Credential $script:remoteConsoleCredential } if( $agentOperationResult.Status -ne "Succeeded") { throw "Installing agent to machines in " + $machineGroup.name + " " + $agentOperationResult.status; } Write-Verbose -Verbose ("Finished installing to machines in " + $machineGroup.name) # get all agents $allAgents = Invoke-RestMethod $Uris.Agents -Credential $script:remoteConsoleCredential # filter to just windows agent $agent = $allAgents.value | Where-Object { $_.MachineName -eq $windowsMachineName } # make windows agent check in $result = Invoke-RestMethod $agent.links.checkin.href -Credential $script:remoteConsoleCredential -Method POST # get all tasks $allTasks = Invoke-RestMethod $agent.links.tasks.href -Credential $script:remoteConsoleCredential # fitler to windows patch task $invokablePatchTask = $allTasks.value | Where-Object { $_.taskName -eq $windowsPatchTaskName } # invoke patch task $result = Invoke-RestMethod $invokablePatchTask.links.self.href -Credential $script:remoteConsoleCredential -Method POST } finally { # remote session credential Invoke-RestMethod $Uris.SessionCredentials -Credential $script:remoteConsoleCredential -Method DELETE }