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
}