AnsweredAssumed Answered

Asset Purge API Call

Question asked by Kevin Trojak on May 17, 2019
Latest reply on Jul 24, 2019 by Keith Shaw

Hi everyone,

 

Over the past few weeks I've been working on automating our purge process and have been asking question on the Community and finally figured it out so I figured I would pay it forward and post my script below. I am using PowerShell to count the number of assets not scanned in 14 days, tracked by IP or DNS (using an asset report call) and then display a confirmation textbox to confirm the purge.

 

I am new to Powershell and this API so I'm sure this script isn't great but we now use it to automate this process. See below and comment for any questions/suggestions. (*this is for one of our many networks) Thanks! I am now going to work on automating our process of taking discovery scan results and adding them to an asset group to scan.

 

 

 

#Text box for UserID
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')

$title = 'Qualys Username'
$msg = 'Enter your username:'

[System.String]$userid = [Microsoft.VisualBasic.Interaction]::InputBox($msg, $title)


#Text Box for Password
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')

$title2 = 'Qualys Password'
$msg2 = 'Enter your password:'

[System.String]$password = [Microsoft.VisualBasic.Interaction]::InputBox($msg2, $title2)


#Encrypt userid and password header with TLS
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser
Clear-Host
Set-StrictMode -Version 1
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
[System.String]$SSLHandlerCode = @"
public class SSLHandler {
public static System.Net.Security.RemoteCertificateValidationCallback GetSSLHandler()
{
return new System.Net.Security.RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });
}
}
"@


Add-Type -TypeDefinition $SSLHandlerCode
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = [SSLHandler]::GetSSLHandler()
[System.string]$EncodedPassword = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("$($userid):$($password)"))
[System.String]$basicAuthValue = "Basic $EncodedPassword"
$headers = @{ Authorization = $basicAuthValue }
$headers.Add("Accept-Encoding", "gzip")
$headers.Add("X-Requested-With", "Powershell")

#Calculates date/time format of 14 days ago
[System.String]$no_vm_scan_since = (Get-Date).AddDays(-([Math]::Abs(14))).ToUniversalTime().ToString("u").Replace(' ', 'T')


###############
# 1st Network      #
###############

#URL with parameters
[System.String]$vQURL = "https://qualysapi.qualys.com/api/2.0/fo/report/asset/?action=search&output_format=xml&tracking_method=IP&asset_groups=All&assets_in_my_network_only=1&ips_network_id=0&last_vm_scan_days=14&last_vm_scan_modifier=not within"
Write-Verbose "Qualys URL: $($vQURL)"
$vresult = Invoke-WebRequest -Uri $vQURL -Headers $headers -Verbose -Method GET -OutFile .\XMLs\AssetCount.xml


#Change from XML to object.node format
[xml]$vsearch = (Select-Xml -Path .\XMLs\AssetCount.xml -XPath /).Node

$vItemObj = New-Object System.Collections.ArrayList

foreach ($vitem in $vsearch.SelectNodes("//HOST")) {

$vItemObj.Add(

[PSCustomObject]@{

'IP'=$vitem.IP.InnerText

}

)

}

 

#URL with parameters
[System.String]$vQURL3 = "https://qualysapi.qualys.com/api/2.0/fo/report/asset/?action=search&output_format=xml&tracking_method=DNS&asset_groups=All&assets_in_my_network_only=1&ips_network_id=0&last_vm_scan_days=14&last_vm_scan_modifier=not within"
Write-Verbose "Qualys URL: $($vQURL3)"
$vresult2 = Invoke-WebRequest -Uri $vQURL3 -Headers $headers -Verbose -Method GET -OutFile .\XMLs\AssetCount.xml


#Change from XML to object.node format
[xml]$vsearch2 = (Select-Xml -Path .\XMLs\AssetCount.xml -XPath /).Node


foreach ($vitem2 in $vsearch2.SelectNodes("//HOST")) {

$vItemObj.Add(

[PSCustomObject]@{

'IP'=$vitem2.IP.InnerText

}

)

}

 

#Declare variables and create list of IPs and count
$vIPcount=0
[array]$vIPlist= @()
foreach ($vitem in $vItemObj) {
$vIPlist += $vitem.IP
$vIPcount = $vIPcount + 1
}

$vNewList = $vIPlist -join ","


#Display confirmation text box
Add-Type -AssemblyName PresentationCore,PresentationFramework

$vButtonType = [System.Windows.MessageBoxButton]::YesNo
$vMessageboxtitle = "Confirm Purge"
$vMessageboxbody = "You are about to purge $vIPcount hosts from the network. Are you sure you want to proceed?"
$vMessageIcon = [System.Windows.MessageBoxImage]::Error
$vconfirm = [System.Windows.MessageBox]::Show($vMessageboxbody,$vMessageboxtitle,$vButtonType,$vMessageIcon)

 

if ($vconfirm -eq 'Yes'){
[System.String]$vQURL2 = "https://qualysapi.qualys.com/api/2.0/fo/asset/host/?action=purge&network_ids=0&ips=$($vNewList)&no_vm_scan_since=$($no_vm_scan_since)"
Write-Verbose "Qualys URL: $($vQURL2)"
$vsearchresult = Invoke-WebRequest -Uri $vQURL2 -Headers $headers -Verbose -Method POST
Write-Host "Purged the following IPs from the network: $vNewList"
}
else {
Write-Host "Network Purge cancelled"
}

Outcomes