Start-to-Finish Example Using PowerShell

Goal

Provide an example PowerShell script that invokes the REST API and that illustrates the patch process from start to finish. There are three ways to utilize the script:

  • Review it and learn how to create advanced PowerShell scripts that invoke the REST API
  • Execute it and watch it in action
  • Modify it to experiment with different scanning and deployment options or to execute it from a remote machine

Default Settings

This example is designed to run on the console machine. By default it will scan for a null patch (MSST-001/QSK2745) that will always be detected as missing. Other default settings include:

  • Will scan the local console machine
  • Will not perform a reboot
  • Will not deploy any missing patches
  • Will prompt you for the credentials that you want to use to execute the REST API calls
  • Will prompt you for the Administrator credentials to use when authenticating to the console machine during the scan and patch process
  • Will clean up after itself by deleting the sample data in the application
  • Does not specify CVEs

Try it yourself

Run the example script without any modifications

  1. Open PowerShell ISE or a similar tool with Run as Administrator privileges.
  2. Execute the following command and note the current ExecutionPolicy setting.
  3. Get-ExecutionPolicy

  4. If needed, set ExecutionPolicy to unrestricted by executing the following command:
  5. Set-ExecutionPolicy Unrestricted

  6. Copy the example script provided below and save it to a new PowerShell file.
  7. For example, you might name the file ExampleScript.ps1.

  8. Run the script.
  9. .\ExampleScript.ps1

  10. At the first prompt, provide the credentials you want to use to execute the REST API calls.
  11. At the second prompt, provide the credentials you want to use when performing the scan and deployment.

(Optional) Modify and rerun the example script

If desired, feel free to experiment with the example script by making small modifications. For example, you might choose to perform a reboot (by changing $reboot = $false) or deploy missing patches (by changing $deployPatches = $false).

Another option is to execute the script on a remote machine. In this case you must do the following:

  • Modify the $consoleAuthenticationAndSessionUserName setting to be a valid administrator account on the remote machine.
  • Modify the $apiServer setting to be the name of the remote console.
  • Import the console certificate to the remote 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.
#
#################################################################################
# Reboot the machine to scan immediately after deployment
$reboot = $false
 
# Deploy ALL missing patches
$deployPatches = $false

# Delete the sample data from the application
$deleteSampleData = $true

# IP Address, NETBios Name or FQDN
$machineToScan =  "127.0.0.1"
 
# What CVE do you want to add to the patch group and deploy to the machine to scan?
#
# The null patches is always scanned for. This will be in addition to
#
# Example @("CVE1", "CVE2")
$cveList = @()
 
# What is the credential user name that is an administrator of the console machine?  This script defaults it to the current user.
$consoleAuthenthicationAndSessionUserName = "$env:USERDOMAIN\$env:USERNAME"
#$consoleAuthenthicationAndSessionUserName = "SLO-2012R2\Administrator"
 
# What is the Console's IP Address, NETBios Name or FQDN?  The script defaults to the current computer name.
$apiServer = if ([String]::IsNullOrEmpty($env:userdnsdomain)) {"$env:computername"} else {"$env:computername.$env:userdnsdomain"}
#$apiServer = "SLO-2012R2"

$apiLocalPort = 3121
 
$Uris =
@{
	AssetScanTemplates = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/asset/scantemplates"
	Credentials = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/credentials"
	CertificateConsole = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/configuration/certificate"	
	DistributionServers = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/distributionservers"
	Hypervisors = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/virtual/hypervisors"
	IPRanges = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/ipranges"
	MachineGroups = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/machinegroups"
	MetadataVendors = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/metadata/vendors"
	NullPatch = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patches?bulletinIds=MSST-001"
	Operations = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/operations"
	Patches = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patches"
	PatchDeployments = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/deployments"
	PatchDeployTemplates = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/deploytemplates"
	PatchDownloads = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/downloads"
	PatchDownloadsScansPatch = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/downloads/scans"
	PatchGroups = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/groups"
	PatchMetaData = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/patchmetadata"
	PatchScans = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/scans"
	PatchScanMachines = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/scans/{0}/machines"
	PatchScanMachinesPatches = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/scans/{0}/machines/{1}/patches"
	PatchScanTemplates = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/patch/scanTemplates"
	SessionCredentials = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/sessioncredentials"
	VCenters = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/virtual/vcenters"
	VirtualInfrastructure = "https://$apiServer`:$apiLocalPort/st/console/api/v1.0/virtual"
}
Add-Type -AssemblyName System.Security
#Encrypt using RSA
function Encrypt-RSAConsoleCert
{
	param
	(
		[Parameter(Mandatory=$True, Position = 0)]
		[Byte[]]$ToEncrypt,
		[Parameter(Mandatory=$True, Position = 1)]
		[PSCredential]$authenticationAndSessionCredential
	)
	try
	{
		$certResponse = Invoke-RestMethod $Uris.CertificateConsole -Method Get -Credential $authenticationAndSessionCredential -Verbose
		[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();
	}
}
 
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,

		[Parameter(Mandatory=$True, Position = 3)]
		[PSCredential]$AuthenticationAndSessionCredential
	)
 
	$body = @{ "userName" = $UserName; "name" = $FriendlyName; }
	$bstr = [IntPtr]::Zero;
	try
	{
		# Create an AES Session key.
		$algorithm = 'Aes'
		$aes = [System.Security.Cryptography.SymmetricAlgorithm]::Create($algorithm);
		$keyBytes = $aes.Key;
 
		# Encrypt the session key with the console cert
		$encryptedKey = Encrypt-RSAConsoleCert -ToEncrypt $keyBytes -authenticationAndSessionCredential $AuthenticationAndSessionCredential
		$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
}
function Get-PaginatedResults
{
	param
	(
		[String]$uri,
		[PSCredential]$authenticationAndSessionCredential
	)
 
	$entireList = [System.Collections.ArrayList]@()
	$nextUri = $uri
	do
	{
		$result = Invoke-RestMethod $nextUri -Method Get -ErrorAction Stop -Credential $authenticationAndSessionCredential -Verbose
		$result.value | Foreach-Object { $entireList.Add($_) }
 
		$nextUri = $result.links.next.href
	} until ($nextUri -eq $null)
 
	return $entireList
}

function Remove-RestResourceSafe
{
	param
	(
		[String]$Uri,
		[PSCredential] $authenticationAndSessionCredential
	)
	try
	{
		Invoke-RestMethod $uri -Method Delete -Credential $authenticationAndSessionCredential -Verbose > $null
	}
	catch
	{
	}
}

function Wait-Operation {
	param(
		[String] $OperationLocation,
		[Int32] $TimeoutMinutes,
		[PSCredential]$authenticationAndSessionCredential
	)
 
	$startTime = [DateTime]::Now
	$operationResult = Invoke-RestMethod -Uri $OperationLocation -Method Get -Credential $authenticationAndSessionCredential -Verbose
	while ($operationResult.Status -eq 'Running')
	{
		if ([DateTime]::Now -gt $startTime.AddMinutes($TimeoutMinutes))
		{
			throw "Timed out waiting for operation to complete"
		}
 
		Start-Sleep 5
		$operationResult = Invoke-RestMethod -Uri $OperationLocation -Method Get -Credential $authenticationAndSessionCredential -Verbose
	}
 
	return $operationResult
}

function Add-Credential
{
	Param
	(
		[String]$credentialName,
		[PSCredential]$credential,
		[PSCredential]$authenticationAndSessionCredential
	)
#	$body = @{ name = $credentialName; password = @{cipherText = $cipherText; protectionMode = "SessionKey"; sessionKey = "AES" }; username = $credential.UserName } | ConvertTo-Json -Depth 99
#	$response = Invoke-RestMethod -Uri $Uris.Credentials -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential -Verbose
 
	$body = Create-CredentialRequest -FriendlyName $credentialName -UserName $credential.UserName -Password $credential.Password -AuthenticationAndSessionCredential $authenticationAndSessionCredential | ConvertTo-Json -Depth 99
	$response = Invoke-RestMethod -Uri $Uris.Credentials -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential -Verbose
	return $response
}

function Add-MachineGroup
{
	Param
	(
		[String]$groupName,
		[String]$machineName,
		[String]$machineAdminCredentialId,
		[PSCredential]$authenticationAndSessionCredential
	)
		$body =
			@{
				name = $groupName;
				discoveryFilters =  @(
				@{
					AdminCredentialId = $machineAdminCredentialId;
					category = "MachineName";
					name = $machineName
				})
			} |  ConvertTo-Json -Depth 99
	$response = Invoke-RestMethod -Uri $Uris.MachineGroups -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential

	return $response
}

function Add-CveToPatchGroup
{
	Param
	(
		[String]$id,
		[String]$cve,
		[PSCredential]$authenticationAndSessionCredential
	)
 
	$body = @{ Cve = $cve; } | ConvertTo-Json -Depth 99
	Invoke-RestMethod -Uri "$($Uris.PatchGroups)/$($id)/patches/cve" -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential > $null
}

function Add-NullPatchToPatchGroup
{
	Param
	(
		[String]$id,
		[PSCredential]$authenticationAndSessionCredential
	)
 
	$nullPatchResult = Invoke-RestMethod -Uri $Uris.NullPatch -Method Get -Credential $authenticationAndSessionCredential -Verbose
	foreach($value in $nullPatchResult.value)
	{
		foreach ($vulnerability in $value.vulnerabilities)
		{
			$body = ConvertTo-Json -Depth 99 -InputObject  @(, $vulnerability.id)
			Invoke-RestMethod -Uri "$($Uris.PatchGroups)/$($id)/patches" -Method POST -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential > $null
		}
	}
}

function Add-PatchGroup
{
	Param
	(
		[String]$groupName,
		[PSCredential]$authenticationAndSessionCredential
	)
	$body = @{ name = $groupName; } | ConvertTo-Json -Depth 99
	$response = Invoke-RestMethod -Uri $Uris.PatchGroups -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential
	return $response
}

function Add-PatchScanTemplate
{
	Param
	(
		[String]$templateName,
		[String]$patchGroupId,
		[PSCredential]$authenticationAndSessionCredential
	)
	$body = @{ name = $templateName; PatchFilter = @{ patchGroupFilterType = 'Scan'; patchGroupIds = @($patchGroupId) }} | ConvertTo-Json -Depth 99
	$response = Invoke-RestMethod -Uri $Uris.PatchScanTemplates -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential -Verbose
	return $response
}

function Add-PatchDeployTemplate
{
	Param
	(
		[String]$templateName,
		[PSCredential]$authenticationAndSessionCredential
	)
 
	#never reboot
	if ($reboot)
	{
		# You want to reboot the machine immediately
		$body =@{
			name = $templateName;
			PostDeploymentReboot = @{
				options = @{
					powerState = 'Restart';
					countdownMinutes = 2;
					extendMinutes = 1;
					forceActionAfterMinutes = 1;
					loggedOnUserAction = 'ForceActionAfterMinutes';
					systemDialogSeconds = 10;
					userOptions = 'AllowExtension';
				}
			when = 'ImmediateIfRequired'
			}
		} | ConvertTo-Json -Depth 99;
	}
	else
	{
		$body =@{
			name = $templateName;
			PostDeploymentReboot = @{
			when = 'NoReboot'
			}
		} | ConvertTo-Json -Depth 99;
	}
 
	$response = Invoke-RestMethod -Uri $Uris.PatchDeployTemplates -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential -Verbose
	return $response
}

# Adds a session credential
function Add-SessionCredential
{
	Param
	(
		[PSCredential]$authenticationAndSessionCredential
	)

	# Using common code to create a secure credential request object.
	# For session credentials, only need to use the password property is used.
	$credentialRequest = Create-CredentialRequest -FriendlyName "unused" -UserName "unused" -Password $authenticationAndSessionCredential.Password -AuthenticationAndSessionCredential $authenticationAndSessionCredential
	$body = $credentialRequest.Password | ConvertTo-Json -Depth 99
	$response = Invoke-RestMethod -Uri $Uris.SessionCredentials -Method Post -Body $body -ContentType "application/json" -Credential $authenticationAndSessionCredential -Verbose
	return $response
}

# Removes a session credential
function Remove-SessionCredential
{
	Param
	(
		[PSCredential]$authenticationAndSessionCredential
	)

	try
	{
		Invoke-RestMethod -Uri $Uris.SessionCredentials -Method Delete -ContentType "application/json" -Credential $authenticationAndSessionCredential -Verbose
	}
	catch
	{
		# status code 404 indicates that the session cred did not exist.  This is common when running the script for the first time.
		if ($_.Exception.Response.StatusCode.value__ -ne 404)
		{
			throw
		}
	}
}

function Invoke-PatchAndDeploy
{
	Param
	(
		[String]$ScanTemplateName,
		[String]$MachineGroupName,
		[String]$DeployTemplateName,
		[String]$ScanName,
		[PSCredential]$authenticationAndSessionCredential
	)
 
	# Find scan template
	$allScanTemplates = Get-PaginatedResults $Uris.PatchScanTemplates $authenticationAndSessionCredential
	$foundScanTemplate = $allScanTemplates | Where-Object { $_.Name -eq $ScanTemplateName }
	if ($null -eq $foundScanTemplate)
	{
		Write-Error ("could not find patch scan template with name " + $ScanTemplateName)
	}
 
	# find machine group
	$allMachineGroups = Get-PaginatedResults $Uris.MachineGroups $authenticationAndSessionCredential
	$foundMachineGroup = $allMachineGroups | Where-Object { $_.Name -eq $MachineGroupName }
	if ($null -eq $foundMachineGroup)
	{
		Write-Error ("could not find machine group with name " + $MachineGroupName)
	}
 
	# Find deploy template
	$allDeployTemplates = Get-PaginatedResults $Uris.PatchDeployTemplates $authenticationAndSessionCredential
	$foundDeployTemplate = $allDeployTemplates | Where-Object { $_.Name -eq $DeployTemplateName }
	if ($null -eq $foundDeployTemplate)
	{
		Write-Error ("could not find patch deploy template with name " + $DeployTemplateName)
	}
 
	# perform the scan
	$body = @{ MachineGroupIds = @( $foundMachineGroup.id ); Name = $ScanName; TemplateId = $foundScanTemplate.id } | ConvertTo-Json -Depth 99
	Write-Host "Starting scan"
	$scanOperation = Invoke-WebRequest -Uri $Uris.PatchScans -Method Post -Body $body -Credential $authenticationAndSessionCredential -Verbose -ContentType 'application/json' -UseBasicParsing
 
	# wait for scan to complete
	$completedScan = Wait-Operation $scanOperation.headers['Operation-Location'] 5 $authenticationAndSessionCredential
 
	# get the scan id for future use
	$scan = Invoke-RestMethod -Uri $completedScan.resourceLocation -Credential $authenticationAndSessionCredential -Verbose -Method GET
	Write-Host ( "Scan complete " + $scan.id)
 
	# get the scan id for future use
	$machines = Invoke-RestMethod -Uri $scan.links.machines.href -Credential $authenticationAndSessionCredential -Verbose -Method GET
 
	foreach ($machineScanned in $machines)
	{
		foreach ($value in $machineScanned.value)
		{
			if (($value.installedPatchCount -gt 0) -or ($value.missingPatchCount -gt 0))
			{
				$patches = Invoke-RestMethod -Uri $value.links.patches.href -Credential $authenticationAndSessionCredential -Verbose -Method GET
				foreach ($patch in $patches.value)
				{
					if ($deployPatches -eq $false -or $patch.scanState -ne "MissingPatch")
					{
						Write-Host ( $patch.bulletinId + " / " + $patch.kb + " (" + $patch.scanState + ") - NOT being deployed." )
					}
					else
					{
						Write-Host ( $patch.bulletinId + " / " + $patch.kb + " (" + $patch.scanState + ") - DEPLOYING." )
					}
				}
			}
			else
			{
				Write-Host ( "No patches were found")
			}
		}
	}
	# perform the deployment
	if ($deployPatches)
	{
		Write-Host "Starting deployment"
		$body = @{ ScanId=$scan.id; TemplateId = $foundDeployTemplate.id } | ConvertTo-Json -Depth 99
		$deploy = Invoke-WebRequest -Uri $Uris.PatchDeployments -Method Post -Body $body -Credential $authenticationAndSessionCredential -Verbose -ContentType 'application/json' -UseBasicParsing
 
		# wait until deployment has a deployment resource location
		$operationUri = $deploy.Headers['Operation-Location']
		$operation = Invoke-RestMethod -Uri $operationUri -Credential $authenticationAndSessionCredential -Verbose -Method GET
 
		while((($null -eq $operation.resourceLocation) -or ($operation.operation -eq "PatchDownload")) -and -not ($operation.status -eq "Succeeded"))
		{
			if (($operation.operation -eq "PatchDownload") -and ($null -ne $operation.percentComplete))
			{
				Write-Host ("Downloading patches..." + $operation.percentComplete + "%")
			}
			Start-Sleep -Seconds 1
			$operation = Invoke-RestMethod -Uri $operationUri -Credential $authenticationAndSessionCredential -Verbose -Method GET
		}
 
		# It's possible we didn't have anything to patch in which case we're already succeeded.
		# If so, don't bother getting machine statuses as it will never return anything good.
		if ($operation.status -ne "Succeeded")
		{
			Write-Host "Deployment scheduled"
			# start getting deployment detailed status updates
			$statusUri = $deploy.Headers['Location'] + '/machines'
			$machineStatuses = Invoke-RestMethod $statusUri -Credential $authenticationAndSessionCredential -Verbose -Method GET
 
			# now start getting and displaying the statuses
			while(($machineStatuses.value[0].overallState -ne "Complete") -and ($machineStatuses.value[0].overallState -ne "Failed"))
			{
				Write-Host ("Overall Status = " + $machineStatuses.value[0].overallState)
				Write-Host ("Status Description = " + $machineStatuses.value[0].statusDescription)
 
				$updateDelaySeconds = 30
 
				# only check for new updates every $updateDelaySeconds
				Start-Sleep  -Seconds $updateDelaySeconds
				$machineStatuses = Invoke-RestMethod $statusUri -Credential $authenticationAndSessionCredential -Verbose -Method GET
			}
		}
	}
	else
	{
		Write-Host "You specified NOT to Deploy the patches."
	}
}
 
function Invoke-ScanAndDeploy
{
	Param
	(
		[parameter(Mandatory = $true)]
		[String]$machineToScan = $(throw "Must supply a machine to scan."),
		[parameter(Mandatory = $false)]
		[String[]]$cveToScanFor,		
		[parameter(Mandatory = $true)]
		[PSCredential]$machineAdminCredential = $(throw "Must supply machine administrator credentials."),
		[parameter(Mandatory = $true)]
		[PSCredential]$authenticationAndSessionCredential = $(throw "Must supply authentication and session credentials.")
	)
 
	$toDelete = [System.Collections.ArrayList]@()
	try
	{
		# For testing purposes, remove the session credentials before re-creating.
		Remove-SessionCredential $authenticationAndSessionCredential
		Add-SessionCredential $authenticationAndSessionCredential

		$uid = [Guid]::NewGuid()
		$machineAdminCredentialName = "Sample Machine Admin Credential -" + $uid
		$machineAdminCredentiaRef = Add-Credential $machineAdminCredentialName $machineAdminCredential $authenticationAndSessionCredential
		$toDelete.Add($machineAdminCredentiaRef.links.self.href) > $null

		$machineGrouplName = "Sample Machine Group -" + $uid
		$response = Add-MachineGroup $machineGrouplName $machineToScan $machineAdminCredentiaRef.id $authenticationAndSessionCredential
		$toDelete.Add($response.links.self.href) > $null

		$patchGroupName = "Sample Patch Group -" + $uid
		$patchGroupRef = Add-PatchGroup $patchGroupName $authenticationAndSessionCredential
		Add-NullPatchToPatchGroup $patchGroupRef.id $authenticationAndSessionCredential

		$cveToScanFor | ForEach-Object { Add-CveToPatchGroup $patchGroupRef.id $_ $authenticationAndSessionCredential }
		$toDelete.Add($patchGroupRef.links.self.href) > $null
		$scanTemplateName = "Sample Scan Template-" + $uid
		$response = Add-PatchScanTemplate $scanTemplateName $patchGroupRef.id $authenticationAndSessionCredential
		$toDelete.Add($response.links.self.href) > $null

		$deployTemplateName = "Sample Deploy Template -" + $uid
		$response = Add-PatchDeployTemplate $deployTemplateName $authenticationAndSessionCredential
		$toDelete.Add($response.links.self.href) > $null
		Invoke-PatchAndDeploy -ScanTemplateName $scanTemplateName -MachineGroupName $machineGrouplName -DeployTemplateName $deployTemplateName -ScanName $uid  -AuthenticationAndSessionCredential $authenticationAndSessionCredential
	}
	finally
	{
		if ($deleteSampleData)
		{
			# cleanup collateral
			$toDelete.Reverse();
			$toDelete | ForEach-Object { Remove-RestResourceSafe $_ $authenticationAndSessionCredential }
		}
		else
		{
			Write-Host "You did NOT want to delete the sample data."
		}
	}
}
#####################################
#   Start Script
#####################################
try
{
	# Who do you want to authenticate and run the REST API invoke calls 
	# This credential is an administrator on the console machine.  This credential is used to:
	# 1) authenticate to the console machine REST api
	# 2) used as the session credential on the console
	# 3) used as the credential that starts the scan and deployments.

	Write-Verbose -Verbose "Please enter the credential that will be used to authenticate to the console Rest API"

	$AuthenticationAndSessionCredential = Get-Credential $consoleAuthenthicationAndSessionUserName
	# The following 2 lines are a much less secure equivalent to using Get-Credential
	#$secpasswd = ConvertTo-SecureString "securePassword" -AsPlainText -Force
	#$AuthenticationAndSessionCredential = New-Object System.Management.Automation.PSCredential ($consoleAuthenthicationAndSessionUserName, $secpasswd)

	# What credential do you want to use when scanning the target machine?
	# This credential will be applied to the machine in the machine group being created.
	# Since this example scans the local machine, it most likely will be the same as $AuthenticationAndSessionCredential.  This credential:
	# 1) must be an administrator for the target machine being scanned.

	Write-Verbose -Verbose "Please enter the credential that is an administrator of the machine being scanned"

	$MachineAdministratorCredential = Get-Credential $consoleAuthenthicationAndSessionUserName
	# $MachineAdministratorCredential = $AuthenticationAndSessionCredential

	Invoke-ScanAndDeploy $machineToScan $cveList $MachineAdministratorCredential $AuthenticationAndSessionCredential
}
catch [Exception]
{
	$private:e = $_.Exception
	do
	{
		Write-Host "Error: " $private:e
		$private:e = $private:e.InnerException
	}
	while ($private:e -ne $null)
}