PowerShell Queries (Windows only)
In Cloudhouse Guardian (Guardian), you can use PowerShell queries on Windows nodes to perform custom data collection tasks that are not provided on a default node scan in Guardian. For example, you can add Active Directory queries to interact with and retrieve data from a directory service or you can add queries for files and folders to manipulate content in your PowerShell instance. The following topic describes how you can add PowerShell queries in the Scan Options page for Windows nodes.
Note: For comparable Linux functionality, see Custom Scripts (Linux only).
Add PowerShell Queries
On the Scan Options page in the Monitored tab (
Note: Alternatively, you can add PowerShell queries and additional scan options directly within the node group settings page. For more information, see Edit Node Group.
Tip: For more information on the other scan options available to configure, see Scan Options.
To add a PowerShell query to a node group's scan options, complete the following steps:
-
In the Guardian web application, navigate to the Monitored tab. By default, all currently monitored nodes are displayed within the 'All Nodes' node group.
Note: If you want to display the list of monitored nodes contained within a different node group, select a node group from the Node Groups drop-down menu.
-
Select the node you want to customize from the list of monitored nodes. The node scan results page is displayed.
-
Click the Edit drop-down and select Scan Options. The Scan Options page is displayed.
-
Select the node group you want to add a PowerShell query to from the list of Node Groups.
Warning: By default, the 'All Nodes' node group is displayed. However, scan options on the 'All Nodes' node group are not available to edit. Select a different node group to edit the corresponding scan options.
-
Select PowerShell from the list of Categories. The following options are displayed:
Field Description Description The display name for the scan results. Key Name The key name specifies which field uniquely identifies the row. This is required in scenarios where multiple results are returned by the query. Query The PowerShell query to be run by the Windows Connection Manager. For more information on the different queries you can use, see below. -
Once complete, click the Check button (
) to apply the PowerShell query to your node group's scan options. Repeat as required.
Once you add or edit an item from the Categories section, the scan options are saved and applied to the selected node group the next time the nodes are scanned. Results are then displayed alongside the default node scan data as flat files under the PowerShell query section of the node scan results page. For more information, see Node Scan Results.
The following sections describe several queries you can use in the PowerShell category to achieve different outputs that include automating valuable tasks, streamlining workflows, and more.
Active Directory
Adding Active Directory queries to the PowerShell scan option in Guardian allows you to incorporate commands that interact with and retrieve data from a directory service. This allows you to automate and streamline the management of Active Directory objects, such as users, groups, computers, and organizational units. The next sections describe some of the computers, schema, and users queries you can use.
Computers
Adding computer queries allows you to interact with computer objects within Active Directory and perform tasks, such as querying computer information or managing computer objects that are registered in the Active Directory domain. The following query demonstrates how you can perform an LDAP query to retrieve information about computer objects in an Active Directory domain:
$strFilter = '(objectClass=User)'
$objDomain = New-Object System.DirectoryServices.DirectoryEntry 'LDAP://DC=your,DC=ou,DC=here'
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = 'Subtree'
$colProplist = 'dnshostname','name','operatingsystem'
foreach ($i in $colPropList) {
$objSearcher.PropertiesToLoad.Add($i)
}
$colResults = $objSearcher.FindAll()
$finalResults = @()
foreach ($objResult in $colResults) {
foreach($prop in $objResult.Properties) {
$obj = New-Object -TypeName PSObject
foreach($col in $colProplist) {
$obj | Add-Member -MemberType NoteProperty -Name $col -Value $prop[$col]
}
}
}
$finalResults
Schema
The Active Directory schema defines the structure and attributes of objects stored in the Active Directory database, such as users, groups, and organizational units. The following query can be used to retrieve information that interacts with the Active Directory schema:
Import-Module ActiveDirectory
$schema = Get-ADObject -SearchBase ((Get-ADRootDSE -Server (hostname)).schemaNamingContext) -SearchScope OneLevel -Filter * -Property objectClass, name, whenChanged,`
whenCreated | Select-Object objectClass, name, whenCreated, whenChanged, `
@{name="event";expression={($_.whenCreated).Date.ToShortDateString()}} | `
Sort-Object whenCreated
$output = '' | Select name, raw
$output.name = 'AD Schema'
$output.raw = ''
foreach($result in $schema) {
$output.raw += $result | Select objectClass, name, whenCreated, whenChanged
$output.raw += "`r`n"
}
$output
Note: The query above has been set as a scan option on the domain controller itself. To set the scan option on another node, you need to either specify the name of the 'DC' against the 'Server' parameter, or simply run the Get-ADRootDSE
command without specifying a server, depending on your configuration.
Users
Adding Active Directory user queries allows you to include Active Directory user accounts as part of the data that PowerShell interacts with. The following query demonstrates how you can perform an LDAP query to retrieve information about user objects in an Active Directory domain:
$strFilter = '(objectClass=User)'
$objDomain = New-Object System.DirectoryServices.DirectoryEntry 'LDAP://DC=your,DC=ou,DC=here'
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = 'Subtree'
$colProplist = 'sAMAccountName','mail','name'
foreach ($i in $colPropList) {
$objSearcher.PropertiesToLoad.Add($i)
}
$colResults = $objSearcher.FindAll()
$finalResults = @()
foreach ($objResult in $colResults) {
foreach($prop in $objResult.Properties) {
$obj = New-Object -TypeName PSObject
foreach($col in $colProplist) {
$obj | Add-Member -MemberType NoteProperty -Name $col -Value $prop[$col]
}
}
}
$finalResults
Component Object Model Plus (COM+)
COM+ is a Microsoft technology that builds on COMs to provide a runtime environment to deploy and manage distributed applications. Adding COM+ queries to the PowerShell scan option in Guardian allows you to interact with and manage COM+ applications and services. The following query demonstrates how you can retrieve information about COM+ applications to take advantage of benefits, such as a deployment scope for components and dynamic loading of component DLLs:
Warning: To use this query, the Key Name field should be left blank.
$ComAdmin = New-Object -com COMAdmin.COMAdminCatalog
$oapplications = $ComAdmin.getcollection('Applications')
[HashTable[]]$collected = @()
$baseDetails = @('ID', 'Name', 'AppPartitionID', 'State')
$additionalDetails = @('RecycleMemoryLimit', 'AuthenticationCapability')
$allDetails = $baseDetails + $additionalDetails
$longestFieldLength = 0
if ($oapplications) {
$oapplications.populate()
foreach ($oapplication in $oapplications){
$obj = @{}
$skeyappli = $oapplication.key
$oappliInstances = $oapplications.getcollection('ApplicationInstances',$skeyappli)
$oappliInstances.populate()
If ($oappliInstances.count -eq 0) {
$obj['State'] = 'Stopped'
} Else{
$obj['State'] = 'Running'
}
foreach($prop in $allDetails) {
#"Prop: " + $prop
try {
# state must be included in details for output, but it is not a property per se
if ($prop -ne 'Password' -and $prop -ne 'State') {
# if there are multiple things with the same name, the last value will be reported
$obj[$prop] = $oapplication.Value($prop)
if($obj[$prop].ToString().Length -gt $longestFieldLength) {
$longestFieldLength = $obj[$prop].ToString().Length
}
}
} catch {
"Exception encountered: " + $_.Exception.Message
}
}
$collected += $obj
}
}
$collected = $collected | Sort-Object { $_['Name'] }
$com_plus = ''
# need the extra space for padding between columns
foreach($deet in $allDetails) {
$com_plus += $deet + (' ' * ($longestFieldLength - $deet.Length)) + ' '
}
$com_plus += "`r`n"
$com_plus += ('-' * ($com_plus.Length - 2)) + "`r`n"
foreach($o in $collected) {
foreach($key in $allDetails) {
if($key -ne "Password") {
$com_plus += $o[$key].ToString() + (' ' * ($longestFieldLength - $o[$key].ToString().Length)) + ' '
}
}
$com_plus += "`r`n"
}
$output = "" | Select folder, raw
$output.folder = $folder
$output.raw = $com_plus
$output
Files and Folders
Adding queries for files and folders to the PowerShell scan option in Guardian allows you to manipulate content in your PowerShell instance. For example, you could create, copy, move, delete files or directories, modify file attributes, search for files based on specific criteria, and so on. The following query demonstrates how you can ensure a folder is completely empty of files:
Get-ChildItem -File -Path "C:\Folder\To\Check" | Measure-Object).Count
This query reports the number of files that exist in the C:\Folder\To\Check
directory. You can then create a policy check on this scanned value and confirm the value is always '0'.
Note: The query above only checks for files in that folder rather than files in its nested directories.
The following query demonstrates how you can ensure all files in a directory are not older than 2 days:
(Get-ChildItem -File -Path "C:\Folder\To\Check" | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-2) } ).Count
This query counts the number of files in the C:\Folder\To\Check
directory, filters for files that are older than 2 days, and returns the number found. You can then create a policy check on this scanned value and confirm the value is always '0'.
Note: The query above only considers the number of files in that folder rather than files in its nested directories.
Global Assembly Cache (GAC)
The GAC is a machine-wide code cache that stores assemblies that were specifically created to be shared by several applications. Adding GAC queries to the PowerShell scan option in Guardian allows you to interact with and retrieve information from the GAC. The following query demonstrates how you can automate the management and inspection of assemblies in the GAC:
Warning: To use this query, the Key Name field should be left blank.
[regex]$regex = '(?<Name>\S+), Version=(?<Version>\S+), Culture=(?<Culture>\S+), PublicKeyToken=(?<PublicKeyToken>\S+)'
$folder = "C:\Windows\Assembly"
$gac = @()
foreach($f in Get-ChildItem -Path $folder -Recurse -Force -ErrorAction SilentlyContinue | where { ! $_.PSIsContainer })
{
try {
$obj = '' | Select Name, Culture, Version, PublicKeyToken
$a = [Reflection.Assembly]::LoadFile($f.FullName).FullName
$fv = [Diagnostics.FileVersionInfo]::GetVersionInfo($f.FullName).FileVersion
$result = $a -match $regex
$subfolder = $f.FullName.Split('\')[3]
$name = $subfolder + '-' + $matches.Name
$tempObj = New-Object –TypeName PSObject
$tempObj | Add-Member -MemberType NoteProperty –Name Name –Value $name
$tempObj | Add-Member -MemberType NoteProperty –Name Path –Value $f.FullName
$tempObj | Add-Member -MemberType NoteProperty –Name Culture –Value $matches.Culture
$tempObj | Add-Member -MemberType NoteProperty –Name Version –Value $matches.Version
$tempObj | Add-Member -MemberType NoteProperty -Name PublicKeyToken -Value $matches.PublicKeyToken
$tempObj | Add-Member -MemberType NoteProperty -Name FileVersion -Value $fv
$tempObj | Add-Member -MemberType NoteProperty -Name SubFolder -Value $subfolder
$gac += $tempObj
} catch [System.Exception] {
$err = "Could not read file " + $f + ": " + $_.Exception.Message
}
}
$gac
Internet Explorer (IE)
Adding IE queries to the PowerShell scan option in Guardian refers to interacting PowerShell queries to IE browser instances to enable automation of tasks. The following query demonstrates how you can automate web browsing tasks, such as web scraping, form filling, or UI testing:
Warning: To use this query, the Description field could be 'Internet Explorer X version' and the Key Name field should be left blank.
Get-ItemProperty 'hklm:\SOFTWARE\Microsoft\Internet Explorer'|Select SvcVersion
Internet Information Services (IIS)
Adding IIS queries to the PowerShell scan option in Guardian allows you to incorporate queries or cmdlets to interact with IIS within a PowerShell environment. That means enabling you to manage IIS servers and web applications in order to streamline repetitive or complex tasks with single queries, or ensure tasks are performed consistently across different environments. The next sections describe some of the IIS queries you can use to achieve different outputs.
Application Pools Info
Application pools in IIS are used to separate different web applications for better security, reliability, and availability. Adding application pool queries to PowerShell means retrieving, managing, and manipulating pools. The following query demonstrates how you can query and analyze the configuration of application pools in IIS:
$webConfig = 'C:\Windows\System32\inetsrv\config\applicationHost.config'
$doc = (Get-Content $webConfig) -as [Xml]
$output = @()
foreach($ap in $doc.configuration."system.applicationHost".applicationPools.add) {
$tempObj = New-Object -TypeName PSObject
$tempObj | Add-Member -MemberType NoteProperty -Name Name -Value $ap.name
$tempObj | Add-Member -MemberType NoteProperty -Name ManagedRuntimeVersion -Value $ap.managedRuntimeVersion
$tempObj | Add-Member -MemberType NoteProperty -Name ManagedPipelineMode -Value $ap.managedPipelineMode
$tempObj | Add-Member -MemberType NoteProperty -Name AutoStart -Value $ap.autoStart
$output += $tempObj
}
$output
Sites Logging Enabled
Enable logging lets you allow or manage logging for IIS sites, ensuring that activity on the web server is properly recorded and can be monitored. Adding sites logging enabled queries to PowerShell means configuring IIS logging settings for websites through PowerShell queries or cmdlets. The following query is useful for auditing IIS sites and checking their logging settings:
Import-Module WebAdministration
$output = @()
foreach ($site in Get-ChildItem IIS:\Sites) {
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty -Name Name -Value $site.name
$sitePath = "IIS:\Sites\" + $site.name
$logsEnabled = (GI $sitePath).logfile.enabled
$obj | Add-Member -MemberType NoteProperty -Name LogsEnabled -Value $logsEnabled
$output += $obj
}
$output
Virtual Directories
Virtual directories in IIS allow you to map a directory to a URL path within a website, enabling better organization and management of web content. Adding virtual directories to PowerShell means creating and managing virtual directories. The following query demonstrates how you can query the configuration of IIS sites and understand the structure of virtual directories within those sites:
$webConfig = 'C:\Windows\System32\inetsrv\config\applicationHost.config'
$doc = (Get-Content $webConfig) -as [Xml]
$output = @()
foreach($site in $doc.configuration."system.applicationHost".sites.site) {
foreach($vdir in $site.application.virtualDirectory) {
$tempObj = New-Object -TypeName PSObject
$tempObj | Add-Member -MemberType NoteProperty -Name Name -Value "$($site.name): $($vdir.path)"
$tempObj | Add-Member -MemberType NoteProperty -Name PhysicalPath -Value $vdir.physicalPath
$output += $tempObj
}
}
$output
Website Information
Adding website information to PowerShell allows you to access and manage the configuration and status details of IIS websites. The following query demonstrates how you can retrieve, update, and manage various properties and settings of IIS websites:
$webConfig = 'C:\Windows\System32\inetsrv\config\applicationHost.config'
$doc = (Get-Content $webConfig) -as [Xml]
$output = @()
foreach($site in $doc.configuration."system.applicationHost".sites.site) {
$tempObj = New-Object -TypeName PSObject
$tempObj | Add-Member -MemberType NoteProperty -Name Name -Value $site.name
$tempObj | Add-Member -MemberType NoteProperty -Name Id -Value $site.id
$tempObj | Add-Member -MemberType NoteProperty -Name ServerAutoStart -Value $site.serverAutoStart
$output += $tempObj
}
$output
Local Group Policy (LGP)
The LGP is a set of configurations that dictates how an operating system, software, or user setting behaves. Adding LGP queries to the PowerShell scan option in Guardian involves writing or importing PowerShell queries that modify or interact with the LGP settings on a Windows system. The next sections describe some of the LGP queries you can use to achieve different outputs.
Local Security Policies
Adding local security policies allows you to manage and configure Windows security settings using PowerShell queries. The following query demonstrates how you can export a local security policy configuration file, extract its settings into PowerShell objects, and return them as an array:
Function Parse-SecPol($CfgFile){
secedit /export /cfg "$CfgFile" | out-null
$Result = @()
$index = 0
$contents = Get-Content $CfgFile -raw
[regex]::Matches($contents,"(?<=\[)(.*)(?=\])") | %{
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_
[regex]::Matches($contents,"(?<=\]).*?((?=\[)|(\Z))", [System.Text.RegularExpressions.RegexOptions]::Singleline)[$index] | %{
$_.value -split "\r\n" | ?{$_.length -gt 0} | %{
$value = [regex]::Match($_,"(?<=\=).*").value
$name = [regex]::Match($_,".*(?=\=)").value
$obj | Add-Member -MemberType NoteProperty -Name $name.tostring().trim() -Value $value.tostring().trim() -ErrorAction SilentlyContinue | out-null
}
}
$Result += $obj
$index += 1
}
return $Result
}
$SecPol = Parse-SecPol -CfgFile C:\LocalSecurityPolicy.cfg
$SecPol
System Audit Policies
Adding system audit policies allows you to define what types of events are recorded in the security event log. These events can include things like successful or failed logon attempts, changes to user or group permissions, and more. The following query demonstrates how you can monitor and track various activities happening on the system:
Note: To use this query, the Description field could be 'Local Audit'.
$Result = @()
$output = auditpol /list /category
$output | ForEach-Object -Process {
$cat = $_.Trim()
if ($cat -eq "Category/Subcategory") {
return
}
$obj = New-Object psobject
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $cat
auditpol /get /category:"$cat" | Foreach-Object -Process {
$line = $_
if (-Not $line.StartsWith(" ")) {
return
}
$tokens = $line -split " "
$key = ""
$val = ""
foreach ($token in $tokens) {
if ($token.trim() -eq "") {
continue
}
if ($key -eq "") {
$key = $token
} else {
$val = $token
}
}
$obj | Add-Member -MemberType NoteProperty -Name $key.ToString().Trim() -Value $val.ToString().Trim() -ErrorAction SilentlyContinue
}
$Result += $obj
}
$Result
Once the query is saved and applied to the selected node group, the next time the nodes are scanned, the properties of this section should look like this:
Recursive File Integrity Check
Adding recursive file integrity check queries to the PowerShell scan option in Guardian allows you to verify the integrity of files by examining their content and comparing it against a baseline or checksum. The following query demonstrates how you can scan files recursively, that means, creating queries that can look into directories and their nested directories:
Warning: To use this query, the Key Name field should be left blank.
# 1. Set the folder path
$folder = "C:\Windows\System32\drivers"
# 2. Specify file extentions
$include_extentions = "all";
#$include_extentions = ".dll", ".config"
$crypto = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
foreach($f in Get-ChildItem -Path $folder -Recurse -Force -ErrorAction SilentlyContinue) {
if ($f.PSIsContainer) { continue; }
try {
$cast = [System.IO.FileInfo]$f
$extension = $f.Extension
if ($include_extentions -contains $extension -or $include_extentions -contains "all" ) {
$bytes = [System.IO.File]::ReadAllBytes($f.FullName)
$hash = [System.BitConverter]::ToString($crypto.ComputeHash($bytes))
if ($f.DirectoryName -ne $last_subfolder) {
$hashes += "# "
$hashes += $f.DirectoryName
$hashes += "`r`n"
$last_subfolder = $f.DirectoryName
}
$hashes += $hash.Replace('-','').ToLower()
$hashes += ": "
$version = $f.VersionInfo.FileVersion
if ($version.length -gt 0) {
$hashes += $version
$hashes += " - "
}
$hashes += $f.Name
$hashes += "`r`n"
}
} catch [System.InvalidCastException] {
#Continue
} catch [System.Exception] {
"Error: " + $f.FullName + ": " + $_.Exception.Message
}
}
$output = "" | Select folder, raw
$output.folder = $folder
$output.raw = $hashes
$output