PSSymantecSEPM.psm1
#Region '.\Classes\Exceptions-Policy.ps1' 0 <# Class to manage exceptions policy. Create a policy exceptions object and add exception types to it with custom methods Structure follows the API documentation : https://apidocs.securitycloud.symantec.com/#/doc?id=policies Section : Update Exceptions Policy #> class SEPMPolicyExceptionsStructure { <# Define the class. Try constructors, properties, or methods. #> [object] $configuration [object] $lockedoptions [Nullable[bool]] $enabled [string] $desc [string] $name SEPMPolicyExceptionsStructure() { $this.configuration = [object]@{ files = [System.Collections.Generic.List[object]]::new() non_pe_rules = [System.Collections.Generic.List[object]]::new() directories = [System.Collections.Generic.List[object]]::new() webdomains = [System.Collections.Generic.List[object]]::new() certificates = [System.Collections.Generic.List[object]]::new() applications = [System.Collections.Generic.List[object]]::new() denylistrules = [System.Collections.Generic.List[object]]::new() applications_to_monitor = [System.Collections.Generic.List[object]]::new() mac = [object]@{ files = [System.Collections.Generic.List[object]]::new() } linux = [object]@{ directories = [System.Collections.Generic.List[object]]::new() extension_list = [object]::new() } extension_list = [object]@{ deleted = $null rulestate = [object]@{ enabled = $null source = $null } scancategory = $null extensions = [System.Collections.Generic.List[object]]::new() } knownrisks = [System.Collections.Generic.List[object]]::new() tamper_files = [System.Collections.Generic.List[object]]::new() dns_and_host_applications = [System.Collections.Generic.List[object]]::new() dns_and_host_denylistrules = [System.Collections.Generic.List[object]]::new() } $this.lockedoptions = [object]@{} } # Method to Update lockedoptions object [void] UpdateLockedOptions( [Nullable[bool]] $knownrisks = $null, [Nullable[bool]] $extension = $null, [Nullable[bool]] $file = $null, [Nullable[bool]] $domain = $null, [Nullable[bool]] $securityrisk = $null, [Nullable[bool]] $sonar = $null, [Nullable[bool]] $application = $null, [Nullable[bool]] $dnshostfile = $null, [Nullable[bool]] $certificate = $null ) { # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $knownrisks) { $this.lockedoptions.knownrisks = $knownrisks } if ($null -ne $extension) { $this.lockedoptions.extension = $extension } if ($null -ne $file) { $this.lockedoptions.file = $file } if ($null -ne $domain) { $this.lockedoptions.domain = $domain } if ($null -ne $securityrisk) { $this.lockedoptions.securityrisk = $securityrisk } if ($null -ne $sonar) { $this.lockedoptions.sonar = $sonar } if ($null -ne $application) { $this.lockedoptions.application = $application } if ($null -ne $dnshostfile) { $this.lockedoptions.dnshostfile = $dnshostfile } if ($null -ne $certificate) { $this.lockedoptions.certificate = $certificate } } # Method to add description [void] AddDescription( [string] $description ) { $this.desc = $description } # Method to create a file hashtable [hashtable] CreateFilesHashTable( [Nullable[bool]] $sonar = $null, [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $scancategory = "", [string] $pathvariable = "", [string] $path = "", [Nullable[bool]] $applicationcontrol = $null, [Nullable[bool]] $securityrisk = $null, [Nullable[bool]] $recursive = $null ) { # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $sonar) { $HashTable['sonar'] = $sonar } if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($scancategory)) { $HashTable['scancategory'] = $scancategory } if (![string]::IsNullOrEmpty($pathvariable)) { $HashTable['pathvariable'] = $pathvariable } if (![string]::IsNullOrEmpty($path)) { $HashTable['path'] = $path } if ($null -ne $applicationcontrol) { $HashTable['applicationcontrol'] = $applicationcontrol } if ($null -ne $securityrisk) { $HashTable['securityrisk'] = $securityrisk } if ($null -ne $recursive) { $HashTable['recursive'] = $recursive } # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add files exceptions [void] AddConfigurationFilesExceptions( [hashtable] $file # Use CreateFilesHashTable method ) { $this.configuration.files.Add($file) } # Method to create a file hashtable [hashtable] CreateNonPEFilesHashTable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $file_sha2 = "", [string] $file_md5 = "", [string] $file_name = "", [string] $file_company = "", [Nullable[Int64]] $file_size = $null, [string] $file_description = "", [string] $file_directory = "", [string] $action = "", [string] $actor_sha2 = "", [string] $actor_md5 = "", [string] $actor_name = "", [string] $actor_company = "", [Nullable[Int64]] $actor_size = $null, [string] $actor_description = "", [string] $actor_directory = "" ) { return @{ deleted = $deleted rulestate = [PSCustomObject]@{ enabled = $rulestate_enabled source = $rulestate_source } file = [PSCustomObject]@{ sha2 = $file_sha2 md5 = $file_md5 name = $file_name company = $file_company size = $file_size description = $file_description directory = $file_directory } action = $action actor = [PSCustomObject]@{ sha2 = $actor_sha2 md5 = $actor_md5 name = $actor_name company = $actor_company size = $actor_size description = $actor_description directory = $actor_directory } } } # Method to add non PE files exceptions [void] AddConfigurationNonPEFilesExceptions( [hashtable] $non_pe_file # Use CreateNonPEFilesHashTable method ) { $this.configuration.non_pe_rules.Add($non_pe_file) } # Method to create a directory hashtable [hashtable] CreateDirectoryHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $scancategory = "", [string] $scantype = "", [string] $pathvariable = "", [string] $directory = "", [Nullable[bool]] $recursive = $null ) { # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($scancategory)) { $HashTable['scancategory'] = $scancategory } if (![string]::IsNullOrEmpty($scantype)) { $HashTable['scantype'] = $scantype } if ($null -ne $recursive) { $HashTable['recursive'] = $recursive } # Add key/value pairs to the hashtable only if the value is not $null or empty or throw an error if (![string]::IsNullOrEmpty($pathvariable)) { $HashTable['pathvariable'] = $pathvariable } else { throw "The 'pathvariable' parameter is mandatory and cannot be $null or empty." } if (![string]::IsNullOrEmpty($directory)) { $HashTable['directory'] = $directory } else { throw "The 'directory' parameter is mandatory and cannot be $null or empty." } # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add directories [void] AddConfigurationDirectoriesExceptions( [hashtable] $directory # Use CreateDirectoryHashtable method ) { $this.configuration.directories.Add($directory) } # Method to create a webdomains hashtable [hashtable] CreateWebdomainsHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $domain = "" ) { # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if ([string]::IsNullOrEmpty($domain)) { throw "The 'domain' parameter is mandatory and cannot be $null or empty." } else { $HashTable['domain'] = $domain } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add webdomains [void] AddWebdomains( [hashtable] $webdomains # Use CreateWebdomainsHashtable method ) { $this.configuration.webdomains.Add($webdomains) } # Method to create a certificate hashtable [hashtable] CreateCertificatesHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $signature_fingerprint_algorith = "", [string] $signature_fingerprint_value = "", [string] $signature_company_name = "", [string] $signature_issuer = "" ) { # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($signature_company_name)) { $HashTable['signature_company_name'] = $signature_company_name } if (![string]::IsNullOrEmpty($signature_issuer)) { $HashTable['signature_issuer'] = $signature_issuer } # SIGNATURE FINGERPRINT # Create an empty hashtable for 'signature_fingerprint' $signature_fingerprint = @{} # Add 'algorithm' to 'signature_fingerprint' or throw an error if it's $null or empty if ([string]::IsNullOrEmpty($signature_fingerprint_algorith)) { throw "The 'algorithm' parameter is mandatory and cannot be $null or empty." } else { $signature_fingerprint['algorithm'] = $signature_fingerprint_algorith } # Add 'value' to 'signature_fingerprint' or throw an error if it's $null or empty if ([string]::IsNullOrEmpty($signature_fingerprint_value)) { throw "The 'value' parameter is mandatory and cannot be $null or empty." } else { $signature_fingerprint['value'] = $signature_fingerprint_value } # Add 'signature_fingerprint' to the main hashtable $HashTable['signature_fingerprint'] = [PSCustomObject]$signature_fingerprint # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add certificates [void] AddCertificates( [hashtable] $certificates # Use CreateCertificatesHashtable method ) { $this.configuration.certificates.Add($certificates) } # Method to create a applications hashtable [hashtable] CreateApplicationsHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $processfile_sha2 = "", [string] $processfile_md5 = "", [string] $processfile_name = "", [string] $processfile_company = "", [Nullable[Int64]] $processfile_size = $null, [string] $processfile_description = "", [string] $processfile_directory = "", [string] $action = "" ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # processfile = [PSCustomObject]@{ # sha2 = $processfile_sha2 # md5 = $processfile_md5 # name = $processfile_name # company = $processfile_company # size = $processfile_size # description = $processfile_description # directory = $processfile_directory # } # action = $action # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($action)) { $HashTable['action'] = $action } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } # PROCESSFILE # Create an empty hashtable for 'processfile' $processfile = @{} # Add 'sha2' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_sha2)) { $processfile['sha2'] = $processfile_sha2 } # Add 'md5' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_md5)) { $processfile['md5'] = $processfile_md5 } # Add 'name' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_name)) { $processfile['name'] = $processfile_name } # Add 'company' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_company)) { $processfile['company'] = $processfile_company } # Add 'size' to 'processfile' only if it's not $null or empty if ($null -ne $processfile_size) { $processfile['size'] = $processfile_size } # Add 'description' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_description)) { $processfile['description'] = $processfile_description } # Add 'directory' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_directory)) { $processfile['directory'] = $processfile_directory } # Add 'processfile' to the main hashtable $HashTable['processfile'] = [PSCustomObject]$processfile return $HashTable } # Method to add applications [void] AddApplications( [hashtable] $applications # Use CreateApplicationsHashtable method ) { $this.configuration.applications.Add($applications) } # Method to create a denylistrules hashtable [hashtable] CreateDenylistrulesHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $processfile_sha2 = "", [string] $processfile_md5 = "", [string] $processfile_name = "", [string] $processfile_company = "", [Nullable[Int64]] $processfile_size = $null, [string] $processfile_description = "", [string] $processfile_directory = "", [string] $action = "" ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # processfile = [PSCustomObject]@{ # sha2 = $processfile_sha2 # md5 = $processfile_md5 # name = $processfile_name # company = $processfile_company # size = $processfile_size # description = $processfile_description # directory = $processfile_directory # } # action = $action # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($action)) { $HashTable['action'] = $action } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } # PROCESSFILE # Create an empty hashtable for 'processfile' $processfile = @{} # Add 'sha2' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_sha2)) { $processfile['sha2'] = $processfile_sha2 } # Add 'md5' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_md5)) { $processfile['md5'] = $processfile_md5 } # Add 'name' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_name)) { $processfile['name'] = $processfile_name } # Add 'company' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_company)) { $processfile['company'] = $processfile_company } # Add 'size' to 'processfile' only if it's not $null or empty if ($null -ne $processfile_size) { $processfile['size'] = $processfile_size } # Add 'description' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_description)) { $processfile['description'] = $processfile_description } # Add 'directory' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_directory)) { $processfile['directory'] = $processfile_directory } # Add 'processfile' to the main hashtable $HashTable['processfile'] = [PSCustomObject]$processfile return $HashTable } # Method to add denylistrules [void] AddDenylistrules( [hashtable] $denylistrules # Use CreateDenylistrulesHashtable method ) { $this.configuration.denylistrules.Add($denylistrules) } # Method to create a applications_to_monitor hashtable [hashtable] CreateApplicationsToMonitorHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $name = "" ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # name = $name # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($name)) { $HashTable['name'] = $name } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add applications_to_monitor [void] AddApplicationsToMonitor( [hashtable] $applications_to_monitor # Use CreateApplicationsToMonitorHashtable method ) { $this.configuration.applications_to_monitor.Add($applications_to_monitor) } # Method to create a mac_files hashtable [hashtable] CreateMacFilesHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $pathvariable = "", [string] $path = "" ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # pathvariable = $pathvariable # path = $path # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($pathvariable)) { $HashTable['pathvariable'] = $pathvariable } if (![string]::IsNullOrEmpty($path)) { $HashTable['path'] = $path } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add mac_files [void] AddMacFiles( [hashtable] $mac_files # Use CreateMacFilesHashtable method ) { $this.configuration.mac.files.Add($mac_files) } # Method to create a linux_directories hashtable [hashtable] CreateLinuxDirectoriesHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $scancategory = "", [string] $pathvariable = "", [string] $directory = "", [Nullable[bool]] $recursive = $null ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # scancategory = $scancategory # pathvariable = $pathvariable # directory = $directory # recursive = $recursive # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($scancategory)) { $HashTable['scancategory'] = $scancategory } if ($null -ne $recursive) { $HashTable['recursive'] = $recursive } # Add key/value pairs to the hashtable only if the value is not $null or empty or throw an error if (![string]::IsNullOrEmpty($pathvariable)) { $HashTable['pathvariable'] = $pathvariable } else { throw "The 'pathvariable' parameter is mandatory and cannot be $null or empty." } if (![string]::IsNullOrEmpty($directory)) { $HashTable['directory'] = $directory } else { throw "The 'directory' parameter is mandatory and cannot be $null or empty." } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add linux_directories [void] AddLinuxDirectories( [hashtable] $linux_directories # Use CreateLinuxDirectoriesHashtable method ) { $this.configuration.linux.directories.Add($linux_directories) } # Method to create a linux_extension_list hashtable [hashtable] CreateLinuxExtensionListHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", # $extensions is a PSOBject list [PSObject[]] $extensions = @() ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # extensions = $extensions # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($extensions)) { $HashTable['extensions'] = $extensions } # Verify if $Extensions is not an empty list if ($extensions.Count -eq 0) { throw "The 'extensions' parameter is mandatory and cannot be an empty list." } else { # Verify if $Extensions is not an empty list foreach ($extension in $extensions) { if ([string]::IsNullOrEmpty($extension)) { throw "The 'extensions' parameter is mandatory and cannot be an empty list." } } } # Add 'extensions' to the main hashtable $HashTable['extensions'] = $extensions # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add linux_extension_list [void] AddLinuxExtensionList( [hashtable] $linux_extension_list # Use CreateLinuxExtensionListHashtable method ) { $this.configuration.linux.extension_list.Add($linux_extension_list) } # Method to create a knownrisks hashtable [hashtable] CreateKnownrisksHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $threat_id = "", [string] $threat_name = "", [string] $action = "" ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # threat_id = $threat_id # threat_name = $threat_name # action = $action # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($action)) { $HashTable['action'] = $action } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } # THREAT # Create an empty hashtable for 'threat' $threat = @{} # Add 'id' to 'threat' only if it's not $null or empty or throw an error if (![string]::IsNullOrEmpty($threat_id)) { $threat['id'] = $threat_id } else { throw "The 'id' parameter is mandatory and cannot be $null or empty." } # Add 'name' to 'threat' only if it's not $null or empty or throw an error if (![string]::IsNullOrEmpty($threat_name)) { $threat['name'] = $threat_name } else { throw "The 'name' parameter is mandatory and cannot be $null or empty." } # Add 'threat' to the main hashtable $HashTable['threat'] = [PSCustomObject]$threat return $HashTable } # Method to add knownrisks [void] AddKnownrisks( [hashtable] $knownrisks # Use CreateKnownrisksHashtable method ) { $this.configuration.knownrisks.Add($knownrisks) } # Method to create a tamper_files hashtable [hashtable] CreateTamperFilesHashtable( [Nullable[bool]] $sonar = $null, [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $scancategory = "", [string] $pathvariable = "", [string] $path = "", [Nullable[bool]] $applicationcontrol = $null, [Nullable[bool]] $securityrisk = $null, [Nullable[bool]] $recursive = $null ) { # return @{ # sonar = $sonar # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # scancategory = $scancategory # pathvariable = $pathvariable # applicationcontrol = $applicationcontrol # securityrisk = $securityrisk # recursive = $recursive # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $sonar) { $HashTable['sonar'] = $sonar } if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($scancategory)) { $HashTable['scancategory'] = $scancategory } if (![string]::IsNullOrEmpty($pathvariable)) { $HashTable['pathvariable'] = $pathvariable } if (![string]::IsNullOrEmpty($path)) { $HashTable['path'] = $path } if ($null -ne $applicationcontrol) { $HashTable['applicationcontrol'] = $applicationcontrol } if ($null -ne $securityrisk) { $HashTable['securityrisk'] = $securityrisk } if ($null -ne $recursive) { $HashTable['recursive'] = $recursive } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } return $HashTable } # Method to add tamper_files [void] AddTamperFiles( [hashtable] $tamper_files # Use CreateTamperFilesHashtable method ) { $this.configuration.tamper_files.Add($tamper_files) } # Method to create a dns_and_host_applications hashtable [hashtable] CreateDnsAndHostApplicationsHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $processfile_sha2 = "", [string] $processfile_md5 = "", [string] $processfile_name = "", [string] $processfile_company = "", [Nullable[Int64]] $processfile_size = $null, [string] $processfile_description = "", [string] $processfile_directory = "", [string] $action = "" ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # processfile = [PSCustomObject]@{ # sha2 = $processfile_sha2 # md5 = $processfile_md5 # name = $processfile_name # company = $processfile_company # size = $processfile_size # description = $processfile_description # directory = $processfile_directory # } # action = $action # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($action)) { $HashTable['action'] = $action } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } # PROCESSFILE # Create an empty hashtable for 'processfile' $processfile = @{} # Add 'sha2' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_sha2)) { $processfile['sha2'] = $processfile_sha2 } # Add 'md5' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_md5)) { $processfile['md5'] = $processfile_md5 } # Add 'name' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_name)) { $processfile['name'] = $processfile_name } # Add 'company' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_company)) { $processfile['company'] = $processfile_company } # Add 'size' to 'processfile' only if it's not $null or empty if ($null -ne $processfile_size) { $processfile['size'] = $processfile_size } # Add 'description' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_description)) { $processfile['description'] = $processfile_description } # Add 'directory' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_directory)) { $processfile['directory'] = $processfile_directory } # Add 'processfile' to the main hashtable $HashTable['processfile'] = [PSCustomObject]$processfile return $HashTable } # Method to add dns_and_host_applications [void] AddDnsAndHostApplications( [hashtable] $dns_and_host_applications # Use CreateDnsAndHostApplicationsHashtable method ) { $this.configuration.dns_and_host_applications.Add($dns_and_host_applications) } # Method to create a dns_and_host_denyrules hashtable [hashtable] CreateDnsAndHostDenyrulesHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", [string] $processfile_sha2 = "", [string] $processfile_md5 = "", [string] $processfile_name = "", [string] $processfile_company = "", [Nullable[Int64]] $processfile_size = $null, [string] $processfile_description = "", [string] $processfile_directory = "", [string] $action = "" ) { # return @{ # deleted = $deleted # rulestate = [PSCustomObject]@{ # enabled = $rulestate_enabled # source = $rulestate_source # } # processfile = [PSCustomObject]@{ # sha2 = $processfile_sha2 # md5 = $processfile_md5 # name = $processfile_name # company = $processfile_company # size = $processfile_size # description = $processfile_description # directory = $processfile_directory # } # action = $action # } # Create an empty hashtable $HashTable = @{} # Add key/value pairs to the hashtable only if the value is not $null or empty if ($null -ne $deleted) { $HashTable['deleted'] = $deleted } if (![string]::IsNullOrEmpty($action)) { $HashTable['action'] = $action } # RULESTATE # Create an empty hashtable for 'rulestate' $rulestate = @{} # Add 'enabled' to 'rulestate' only if it's not $null if ($null -ne $rulestate_enabled) { $rulestate['enabled'] = $rulestate_enabled } # Add 'source' to 'rulestate' only if it's not $null or empty if (![string]::IsNullOrEmpty($rulestate_source)) { $rulestate['source'] = $rulestate_source } # Add 'rulestate' to the main hashtable only if it's not empty if ($rulestate.Count -gt 0) { $HashTable['rulestate'] = [PSCustomObject]$rulestate } # PROCESSFILE # Create an empty hashtable for 'processfile' $processfile = @{} # Add 'sha2' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_sha2)) { $processfile['sha2'] = $processfile_sha2 } # Add 'md5' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_md5)) { $processfile['md5'] = $processfile_md5 } # Add 'name' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_name)) { $processfile['name'] = $processfile_name } # Add 'company' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_company)) { $processfile['company'] = $processfile_company } # Add 'size' to 'processfile' only if it's not $null or empty if ($null -ne $processfile_size) { $processfile['size'] = $processfile_size } # Add 'description' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_description)) { $processfile['description'] = $processfile_description } # Add 'directory' to 'processfile' only if it's not $null or empty if (![string]::IsNullOrEmpty($processfile_directory)) { $processfile['directory'] = $processfile_directory } # Add 'processfile' to the main hashtable $HashTable['processfile'] = [PSCustomObject]$processfile return $HashTable } } #EndRegion '.\Classes\Exceptions-Policy.ps1' 1194 #Region '.\Private\Build-SEPMQueryURI.ps1' 0 function Build-SEPMQueryURI { <# .SYNOPSIS Constructs a URI from a base URI and query strings .DESCRIPTION Constructs a URI from a base URI and query strings .PARAMETER BaseURI The base URI to use .PARAMETER QueryStrings A hashtable of query strings to add to the URI .NOTES helper function .EXAMPLE $BaseURI = "https://gdc8ap0030:8446/sepm/api/v1/computers" $QueryStrings = @{ sort = "COMPUTER_NAME" pageIndex = 1 pageSize = 100 } $URI = Build-SEPMQueryURI -BaseURI $BaseURI -QueryStrings $QueryStrings #> param ( [string]$BaseURI, [hashtable]$QueryStrings ) # Construct the URI $builder = New-Object System.UriBuilder($BaseURI) $query = [System.Web.HttpUtility]::ParseQueryString($builder.Query) foreach ($param in $QueryStrings.GetEnumerator()) { $query[$param.Key] = $param.Value } $builder.Query = $query.ToString() $BaseURI = $builder.ToString() return $BaseURI } #EndRegion '.\Private\Build-SEPMQueryURI.ps1' 40 #Region '.\Private\Import-SepmConfiguration.ps1' 0 function Import-SepmConfiguration { <# .SYNOPSIS Loads in the default configuration values, and then updates the individual properties with values that may exist in a file. .DESCRIPTION Loads in the default configuration values, and then updates the individual properties with values that may exist in a file. .PARAMETER Path The file that may or may not exist with a serialized version of the configuration values for this module. .OUTPUTS PSCustomObject .NOTES Internal helper method. No side-effects. .EXAMPLE Import-SepmConfiguration -Path 'c:\foo\config.json' Creates a new default config object and updates its values with any that are found within a deserialized object from the content in $Path. The configuration object is then returned. #> [CmdletBinding()] param( [string] $Path ) # Create a configuration object with all the default values. We can then update the values # with any that we find on disk. $config = [PSCustomObject]@{ 'ServerAddress' = '' 'port' = '8446' 'domain' = '' } $jsonObject = Read-SepmConfiguration -Path $Path Get-Member -InputObject $config -MemberType NoteProperty | ForEach-Object { $name = $_.Name $type = $config.$name.GetType().Name $config.$name = Resolve-PropertyValue -InputObject $jsonObject -Name $name -Type $type -DefaultValue $config.$name } return $config } #EndRegion '.\Private\Import-SepmConfiguration.ps1' 53 #Region '.\Private\Invoke-ABRestMethod.ps1' 0 function Invoke-ABRestMethod { <# .SYNOPSIS Invokes a REST method with a PS version-appropriate method .DESCRIPTION Invokes a REST method with a PS version-appropriate method Handles the differences between PS versions 5 and 6 for certificate validation skipping Tests the certificate of the server if self signed .NOTES Helper function for Invoke-ABRestMethod .PARAMETER params A hashtable of parameters to pass to the Invoke-RestMethod cmdlet .EXAMPLE $params = @{ Method = 'POST' Uri = $URI headers = $headers } Invoke-ABRestMethod -params $params #> param ( # Hashtable of parameters [Parameter( Mandatory = $true )] [hashtable] $params ) # Test the certificate if self signed if (-not $script:SkipCert) { Test-SEPMCertificate -URI $params.Uri } switch ($PSVersionTable.PSVersion.Major) { { $_ -ge 6 } { try { if ($script:SkipCert -eq $true) { $resp = Invoke-RestMethod @params -SkipCertificateCheck } else { $resp = Invoke-RestMethod @params } } catch { Write-Warning -Message "Error: $_" return "Error: $_" } } default { try { if ($script:SkipCert -eq $true) { Skip-Cert $resp = Invoke-RestMethod @params } else { $resp = Invoke-RestMethod @params } } catch { Write-Warning -Message "Error: $_" return "Error: $_" } } } # return the response return $resp } #EndRegion '.\Private\Invoke-ABRestMethod.ps1' 68 #Region '.\Private\Optimize-ExceptionPolicyStructure.ps1' 0 function Optimize-ExceptionPolicyStructure { [CmdletBinding()] param ( [Parameter( Mandatory = $true, ValueFromPipeline = $true )] [object] $obj ) process { # convert the object to a PSCustomObject (trick to convert custom class to PSCustomObject) $obj = $obj | ConvertTo-Json -Depth 100 | ConvertFrom-Json -Depth 100 # Listing all properties of the object $AllProperties = $obj | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name foreach ($property in $AllProperties) { # Conditional nested objects lookup switch ($property) { "configuration" { # recursively call the function to dig deeper $obj.$property = Optimize-ExceptionPolicyStructure $obj.$property # If configuration object is empty, remove it if (($obj.$property | Get-Member -MemberType NoteProperty).count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "lockedoptions" { # TODO Change the lockedoptions cleanup way via a custom method in the class # # list all properties of the lockedoptions object # $lockedproperties = $obj.$property | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name # # Parse the lockedoptions properties and remove the ones with $null values # foreach ($lockedproperty in $lockedproperties) { # if ($null -eq $obj.$property.$lockedproperty) { # $obj.$property = $obj.$property | Select-Object -ExcludeProperty $lockedproperty # } # } # If lockedoptions object is empty, remove it if (($obj.$property | Get-Member -MemberType NoteProperty).count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "extension_list" { # If no extensions are defined, remove the extension_list property if ($obj.$property.extensions.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "mac" { # If no files are defined, remove the mac property if ($obj.$property.files.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } "linux" { # If no directories are defined, remove the directories list if ($obj.$property.directories.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "directories" } # If no extensions are defined, remove them from the linux object if ($obj.$property.extension_list.extensions.count -eq 0) { $obj.$property = $obj.$property | Select-Object -ExcludeProperty "extension_list" } # If linux object is empty, remove it if (($obj.$property | Get-Member -MemberType NoteProperty).count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } } } # If the property is empty, remove the property if ($obj.$property.count -eq 0) { $obj = $obj | Select-Object -ExcludeProperty $property } # If the property is null, remove the property if ($null -eq $obj.$property) { $obj = $obj | Select-Object -ExcludeProperty $property } } return $obj } } #EndRegion '.\Private\Optimize-ExceptionPolicyStructure.ps1' 92 #Region '.\Private\Read-SepmConfiguration.ps1' 0 function Read-SepmConfiguration { <# .SYNOPSIS Loads in the default configuration values and returns the deserialized object. .DESCRIPTION Loads in the default configuration values and returns the deserialized object. .PARAMETER Path The file that may or may not exist with a serialized version of the configuration values for this module. .OUTPUTS PSCustomObject .NOTES Internal helper method. No side-effects. .EXAMPLE Read-SepmConfiguration -Path 'c:\foo\config.json' Returns back an object with the deserialized object contained in the specified file, if it exists and is valid. #> [CmdletBinding()] param( [string] $Path ) $content = Get-Content -Path $Path -Encoding UTF8 -ErrorAction Ignore if (-not [String]::IsNullOrEmpty($content)) { try { return ($content | ConvertFrom-Json) } catch { $message = 'The configuration file for this module is in an invalid state. Use Reset-SEPMConfiguration to recover.' Write-Warning -Message $message } } return [PSCustomObject]@{} } #EndRegion '.\Private\Read-SepmConfiguration.ps1' 43 #Region '.\Private\Remove-NestedNullOrEmptyProperties.ps1' 0 function Remove-NestedNullOrEmptyProperties { <# .SYNOPSIS Remove nested properties with $null or empty values from a PSObject .DESCRIPTION This function will recursively iterate over all properties of a PSObject or list of PSObjects and remove the ones with $null or empty values. .EXAMPLE $obj = [PSCustomObject]@{ "property1" = "value1" "property2" = $null "property3" = "" "property4" = [PSCustomObject]@{ "property5" = "value5" "property6" = $null "property7" = "" "property8" = [PSCustomObject]@{ "property9" = "value9" "property10" = $null "property11" = "" } } } $obj = Remove-NestedNullOrEmptyProperties -InputObject $obj $obj | ConvertTo-Json { "property1": "value1", "property4": { "property5": "value5", "property8": { "property9": "value9" } } } .NOTES helper function #> param ( [Parameter(Mandatory = $true)] [PSObject] $InputObject ) # Get all properties of the input object $properties = $InputObject | Get-Member -MemberType NoteProperty -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name # Iterate over the properties and remove the ones with $null values foreach ($property in $properties) { # If the property value is $null, remove the property if ($null -eq $InputObject.$property) { $InputObject = $InputObject | Select-Object -ExcludeProperty $property } # If the property value is an empty string, remove the property elseif ($InputObject.$property -eq "") { $InputObject = $InputObject | Select-Object -ExcludeProperty $property } # If the property value is another PSObject, recursively call this function elseif ($InputObject.$property -is [PSObject]) { $InputObject.$property = Remove-NestedNullOrEmptyProperties -InputObject $InputObject.$property -ErrorAction SilentlyContinue } # If the property value is a list of PSObjects, iterate over the list and recursively call this function on each item elseif ($InputObject.$property -is [System.Collections.IEnumerable] -and $InputObject.$property -isnot [string]) { $InputObject.$property = $InputObject.$property | ForEach-Object { if ($_ -is [PSObject]) { Remove-NestedNullOrEmptyProperties -InputObject $_ } else { $_ } } } } # Return the modified object return $InputObject } #EndRegion '.\Private\Remove-NestedNullOrEmptyProperties.ps1' 77 #Region '.\Private\Resolve-PropertyValue.ps1' 0 function Resolve-PropertyValue { <# .SYNOPSIS Returns the requested property from the provided object, if it exists and is a valid value. Otherwise, returns the default value. .DESCRIPTION Returns the requested property from the provided object, if it exists and is a valid value. Otherwise, returns the default value. .PARAMETER InputObject The object to check the value of the requested property. .PARAMETER Name The name of the property on InputObject whose value is desired. .PARAMETER Type The type of the value stored in the Name property on InputObject. Used to validate that the property has a valid value. .PARAMETER DefaultValue The value to return if Name doesn't exist on InputObject or is of an invalid type. .EXAMPLE Resolve-PropertyValue -InputObject $config -Name defaultOwnerName -Type String -DefaultValue $null Checks $config to see if it has a property named "defaultOwnerName". If it does, and it's a string, returns that value, otherwise, returns $null (the DefaultValue). #> [CmdletBinding()] param( [PSCustomObject] $InputObject, [Parameter(Mandatory)] [string] $Name, [Parameter(Mandatory)] [ValidateSet('String', 'Boolean', 'Int32', 'Int64')] [String] $Type, $DefaultValue ) if ($null -eq $InputObject) { return $DefaultValue } $typeType = [String] if ($Type -eq 'Boolean') { $typeType = [Boolean] } if ($Type -eq 'Int32') { $typeType = [Int32] } if ($Type -eq 'Int64') { $typeType = [Int64] } $numberEquivalents = @('Int32', 'Int64', 'long', 'int') if (Test-PropertyExists -InputObject $InputObject -Name $Name) { if (($InputObject.$Name -is $typeType) -or (($Type -in $numberEquivalents) -and ($InputObject.$Name.GetType().Name -in $numberEquivalents))) { return $InputObject.$Name } else { return $DefaultValue } } else { return $DefaultValue } } #EndRegion '.\Private\Resolve-PropertyValue.ps1' 65 #Region '.\Private\Save-SepmConfiguration.ps1' 0 function Save-SepmConfiguration { <# .SYNOPSIS Serializes the provided settings object to disk as a JSON file. .DESCRIPTION Serializes the provided settings object to disk as a JSON file. .PARAMETER Configuration The configuration object to persist to disk. .PARAMETER Path The path to the file on disk that Configuration should be persisted to. .NOTES Internal helper method. .EXAMPLE Save-SepmConfiguration -Configuration $config -Path 'c:\foo\config.json' Serializes $config as a JSON object to 'c:\foo\config.json' #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [PSCustomObject] $Configuration, [Parameter(Mandatory)] [string] $Path ) if (-not $PSCmdlet.ShouldProcess('Sepm Configuration', 'Save')) { return } $null = New-Item -Path $Path -Force ConvertTo-Json -InputObject $Configuration | Set-Content -Path $Path -Force -ErrorAction SilentlyContinue -ErrorVariable ev if (($null -ne $ev) -and ($ev.Count -gt 0)) { $message = "Failed to persist these updated settings to disk. They will remain for this PowerShell session only." Write-Warning -Message $message } } #EndRegion '.\Private\Save-SepmConfiguration.ps1' 45 #Region '.\Private\Skip-Cert.ps1' 0 function Skip-Cert { <# .SYNOPSIS This function allows skipping the SSL/TLS Secure channel check in the event that there is not a valid certificate available .DESCRIPTION This function allows skipping the SSL/TLS Secure channel check in the event that there is not a valid certificate available .NOTES Required for self-signed certificates skipping with Windows Powershell 5.1 and below This function is used internally by the module and should not be called directly .PARAMETER None .EXAMPLE Skip-Cert .OUTPUTS None #> if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type) { $certCallback = @" using System; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; public class ServerCertificateValidationCallback { public static void Ignore() { if(ServicePointManager.ServerCertificateValidationCallback ==null) { ServicePointManager.ServerCertificateValidationCallback += delegate ( Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors ) { return true; }; } } } "@ Add-Type $certCallback } [ServerCertificateValidationCallback]::Ignore() } #EndRegion '.\Private\Skip-Cert.ps1' 48 #Region '.\Private\Test-PropertyExists.ps1' 0 function Test-PropertyExists { <# .SYNOPSIS Determines if an object contains a property with a specified name. .DESCRIPTION Determines if an object contains a property with a specified name. This is essentially using Get-Member to verify that a property exists, but additionally adds a check to ensure that InputObject isn't null. .PARAMETER InputObject The object to check to see if it has a property named Name. .PARAMETER Name The name of the property on InputObject that is being tested for. .EXAMPLE Test-PropertyExists -InputObject $listing -Name 'title' Returns $true if $listing is non-null and has a property named 'title'. Returns $false otherwise. .NOTES Internal-only helper method. #> [CmdletBinding()] [OutputType([bool])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Exists isn't a noun and isn't violating the intention of this rule.")] param( [Parameter(Mandatory)] [AllowNull()] $InputObject, [Parameter(Mandatory)] [String] $Name ) return (($null -ne $InputObject) -and ($null -ne (Get-Member -InputObject $InputObject -Name $Name -MemberType Properties))) } #EndRegion '.\Private\Test-PropertyExists.ps1' 42 #Region '.\Private\Test-SEPMAccessToken.ps1' 0 function Test-SEPMAccessToken { <# .SYNOPSIS Test if the access token is still valid .DESCRIPTION Test if the access token is still valid. If no token is passed, will test the cached token. Returns $true if the token is still valid, $false otherwise .PARAMETER TokenInfo The token to test .OUTPUTS System.Boolean .NOTE Internal helper method. This function is used internally by the module and should not be called directly. #> param ( [Alias('AccessToken', 'Token')] [PSCustomObject]$TokenInfo ) # If no paramater is passed, test the cached token if ($null -eq $TokenInfo) { # if token in memory if (-not [string]::IsNullOrEmpty($script:accessToken.token) ) { # if token still valid if ($script:accessToken.tokenExpiration -gt (Get-Date)) { return $true } } } # Check if the access token has expired if ($TokenInfo.tokenExpiration -gt (Get-Date)) { return $true } # If we get here, no valid token was found return $false } #EndRegion '.\Private\Test-SEPMAccessToken.ps1' 44 #Region '.\Private\Test-SEPMCertificate.ps1' 0 function Test-SEPMCertificate { <# .SYNOPSIS This function tests a webserver to see if it is using a self-signed certificate .DESCRIPTION This function tests a webserver to see if it is using a self-signed certificate If so, sets the $script:SkipCert variable to $true to continue with the connection .PARAMETER URI The URI of the webserver to test .INPUTS System.String .OUTPUTS None .EXAMPLE Test-SEPMCertificate -URI https://www.example.com Tests the webserver at https://www.example.com to see if it is using a self-signed certificate #> [CmdletBinding()] param ( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [string] $URI ) try { # Test the certificate Invoke-WebRequest $URI # If no error, then the certificate is valid $script:SkipCert = $false } catch { # Get SEPM server name from URI $uriObject = New-Object System.Uri($URI) $domain = $uriObject.Host # Get the error message $message = "SSL Certificate test failed. The certificate for $domain is self-signed." Write-Warning -Message $message # Prompt for user input to continue # TODO addd a remove option for user interaction with -skipcertificationcheck # $Response = Read-Host -Prompt 'Press enter to ignore this and continue without SSL/TLS secure channel for this session' # if ($Response -eq "") { if ($PSVersionTable.PSVersion.Major -lt 6) { Skip-Cert } $script:SkipCert = $true # } } } #EndRegion '.\Private\Test-SEPMCertificate.ps1' 57 #Region '.\Public\Add-SEPMFileFingerprintList.ps1' 0 function Add-SEPMFileFingerprintList { <# .SYNOPSIS Adds a blacklist as a file fingerprint list .DESCRIPTION Adds a blacklist as a file fingerprint list .PARAMETER name The name of the blacklist to be added .PARAMETER domainId The domain id of the domain to add the blacklist to Only takes the domain id. Can be found using Get-SEPMDomain .PARAMETER HashType The type of hash to use for the blacklist Valid values are SHA256 and MD5 .PARAMETER description The description of the blacklist .PARAMETER hashlist The hash list to add to the blacklist Can be generated using Get-FileHash or takes a string array of hashes .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE $DomainId = Get-SEPMDomain | Where-Object { $_.name -eq "Default" } $HashList = ls -file C:\Users\$env:USERNAME\Downloads\*.exe | Get-FileHash -algorithm SHA256 Add-SEPMFileFingerprintList -name "My Blacklist" -domainId $domainId -HashType "SHA256" -description "My Blacklist" -hashlist $hashlist.hash Gets the domain id for the default domain Create a hash list of all the files in the downloads folder of the currently logged in user Adds the hash list as a blacklist to the default domain #> [CmdletBinding()] param ( [Parameter()] [string]$name, [Parameter()] [string]$domainId, [Parameter()] [ValidateSet('SHA256', 'MD5')] [string]$HashType, [Parameter()] [string]$description, [Parameter()] $hashlist, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { $URI = $script:BaseURLv1 + "/policy-objects/fingerprints" # Construct the body & required fields $body = @{ name = $name domainId = $domainId hashType = $HashType description = $description data = $hashlist } $params = @{ Method = 'POST' Uri = $URI headers = $headers Body = $body | ConvertTo-Json ContentType = 'application/json' } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Add-SEPMFileFingerprintList.ps1' 95 #Region '.\Public\Backup-SEPMAuthentication.ps1' 0 function Backup-SEPMAuthentication { <# .SYNOPSIS Exports the user's current authentication file. .DESCRIPTION Exports the user's current authentication file. This is primarily used for unit testing scenarios. .PARAMETER Path The path to store the user's current authentication file. .PARAMETER Force If specified, will overwrite the contents of any file with the same name at the location specified by Path. .EXAMPLE Backup-SEPMAuthentication -Path 'c:\foo\credentials.xml' Writes the user's current authentication file to c:\foo\credentials.xml. #> [CmdletBinding()] param( [string] $Path, [switch] $Force, [switch] $Credentials, [switch] $AccessToken ) # Make sure that the path that we're going to be storing the file exists. $null = New-Item -Path (Split-Path -Path $Path -Parent) -ItemType Directory -Force if ($Credentials) { if (Test-Path -Path $script:credentialsFilePath -PathType Leaf) { $null = Copy-Item -Path $script:credentialsFilePath -Destination $Path -Force:$Force } } if ($AccessToken) { if (Test-Path -Path $script:accessTokenFilePath -PathType Leaf) { $null = Copy-Item -Path $script:accessTokenFilePath -Destination $Path -Force:$Force } } } #EndRegion '.\Public\Backup-SEPMAuthentication.ps1' 49 #Region '.\Public\Backup-SEPMConfiguration.ps1' 0 function Backup-SEPMConfiguration { <# .SYNOPSIS Exports the user's current configuration file. .DESCRIPTION Exports the user's current configuration file. This is primarily used for unit testing scenarios. .PARAMETER Path The path to store the user's current configuration file. .PARAMETER Force If specified, will overwrite the contents of any file with the same name at the location specified by Path. .EXAMPLE Backup-SEPMConfiguration -Path 'c:\foo\config.json' Writes the user's current configuration file to c:\foo\config.json. #> [CmdletBinding()] param( [string] $Path, [switch] $Force ) # Make sure that the path that we're going to be storing the file exists. $null = New-Item -Path (Split-Path -Path $Path -Parent) -ItemType Directory -Force if (Test-Path -Path $script:configurationFilePath -PathType Leaf) { $null = Copy-Item -Path $script:configurationFilePath -Destination $Path -Force:$Force } else { ConvertTo-Json -InputObject @{} | Set-Content -Path $Path -Force:$Force } } #EndRegion '.\Public\Backup-SEPMConfiguration.ps1' 39 #Region '.\Public\Clear-SepmAuthentication.ps1' 0 function Clear-SEPMAuthentication { <# .SYNOPSIS Clears out any API token from memory, as well as from local file storage. .DESCRIPTION Clears out any API token from memory, as well as from local file storage. .EXAMPLE Clear-SEPMAuthentication Clears out any API token from memory, as well as from local file storage. .NOTES This command will not clear your configuration settings. Please use Reset-SEPMConfiguration to accomplish that. #> [CmdletBinding()] param() # Clear out Credential and AccessToken variables from memory $script:Credential = $null $script:accessToken = $null # Remove file that stores the Access Token Remove-Item -Path $script:accessTokenFilePath -ErrorAction SilentlyContinue -Force -ErrorVariable ev Remove-Item -Path $script:credentialsFilePath -ErrorAction SilentlyContinue -Force -ErrorVariable ev if (($null -ne $ev) -and ($ev.Count -gt 0) -and ($ev[0].FullyQualifiedErrorId -notlike 'PathNotFound*')) { $message = "Experienced a problem trying to remove the file that persists the Access Token [$script:credentialsFilePath]." Write-Warning -Message $message } $message = "This has not cleared your configuration settings. Call Reset-SEPMConfiguration to accomplish that." Write-Verbose -Message $message } #EndRegion '.\Public\Clear-SepmAuthentication.ps1' 39 #Region '.\Public\Confirm-SEPMEventInfo.ps1' 0 function Confirm-SEPMEventInfo { <# # TODO add examples once finished .SYNOPSIS Post Acknowledgement For Notification .DESCRIPTION Acknowledges a specified event for a given event ID. A system administrator account is required for this REST API. .PARAMETER EventID The event ID to acknowledge. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> $SEPMEvents = Confirm-SEPMEventInfo -eventID 30D8A67F0A6606220DEB5989DC3FAC50 #> [CmdletBinding()] param ( [Parameter( Mandatory = $true )] [string] $EventID, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/events/acknowledge/$eventID" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # return the response return $resp } } #EndRegion '.\Public\Confirm-SEPMEventInfo.ps1' 59 #Region '.\Public\ConvertTo-FlatObject.ps1' 0 # From https://github.com/EvotecIT/PSSharedGoods/tree/master/Public/Converts Function ConvertTo-FlatObject { <# .SYNOPSIS Flattends a nested object into a single level object. .DESCRIPTION Flattends a nested object into a single level object. .PARAMETER Objects The object (or objects) to be flatten. .PARAMETER Separator The separator used between the recursive property names .PARAMETER Base The first index name of an embedded array: - 1, arrays will be 1 based: <Parent>.1, <Parent>.2, <Parent>.3, … - 0, arrays will be 0 based: <Parent>.0, <Parent>.1, <Parent>.2, … - "", the first item in an array will be unnamed and than followed with 1: <Parent>, <Parent>.1, <Parent>.2, … .PARAMETER Depth The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop. .PARAMETER Uncut The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop. .PARAMETER ExcludeProperty The propertys to be excluded from the output. .EXAMPLE $Object3 = [PSCustomObject] @{ "Name" = "Przemyslaw Klys" "Age" = "30" "Address" = @{ "Street" = "Kwiatowa" "City" = "Warszawa" "Country" = [ordered] @{ "Name" = "Poland" } List = @( [PSCustomObject] @{ "Name" = "Adam Klys" "Age" = "32" } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = "33" } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = 30 } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = $null } ) } ListTest = @( [PSCustomObject] @{ "Name" = "Sława Klys" "Age" = "33" } ) } $Object3 | ConvertTo-FlatObject .NOTES Based on https://powersnippets.com/convertto-flatobject/ #> [CmdletBinding()] Param ( [Parameter(ValueFromPipeLine)][Object[]]$Objects, [String]$Separator = ".", [ValidateSet("", 0, 1)]$Base = 1, [int]$Depth = 5, [string[]] $ExcludeProperty, [Parameter(DontShow)][String[]]$Path, [Parameter(DontShow)][System.Collections.IDictionary] $OutputObject ) Begin { $InputObjects = [System.Collections.Generic.List[Object]]::new() } Process { foreach ($O in $Objects) { if ($null -ne $O) { $InputObjects.Add($O) } } } End { If ($PSBoundParameters.ContainsKey("OutputObject")) { $Object = $InputObjects[0] $Iterate = [ordered] @{} if ($null -eq $Object) { #Write-Verbose -Message "ConvertTo-FlatObject - Object is null" } elseif ($Object.GetType().Name -in 'String', 'DateTime', 'TimeSpan', 'Version', 'Enum') { $Object = $Object.ToString() } elseif ($Depth) { $Depth-- If ($Object -is [System.Collections.IDictionary]) { $Iterate = $Object } elseif ($Object -is [Array] -or $Object -is [System.Collections.IEnumerable]) { $i = $Base foreach ($Item in $Object.GetEnumerator()) { $NewObject = [ordered] @{} If ($Item -is [System.Collections.IDictionary]) { foreach ($Key in $Item.Keys) { if ($Key -notin $ExcludeProperty) { $NewObject[$Key] = $Item[$Key] } } } elseif ($Item -isnot [Array] -and $Item -isnot [System.Collections.IEnumerable]) { foreach ($Prop in $Item.PSObject.Properties) { if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) { $NewObject["$($Prop.Name)"] = $Item.$($Prop.Name) } } } else { $NewObject = $Item } $Iterate["$i"] = $NewObject $i += 1 } } else { foreach ($Prop in $Object.PSObject.Properties) { if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) { $Iterate["$($Prop.Name)"] = $Object.$($Prop.Name) } } } } If ($Iterate.Keys.Count) { foreach ($Key in $Iterate.Keys) { if ($Key -notin $ExcludeProperty) { ConvertTo-FlatObject -Objects @(, $Iterate["$Key"]) -Separator $Separator -Base $Base -Depth $Depth -Path ($Path + $Key) -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty } } } else { $Property = $Path -Join $Separator if ($Property) { # We only care if property is not empty if ($Object -is [System.Collections.IDictionary] -and $Object.Keys.Count -eq 0) { $OutputObject[$Property] = $null } else { $OutputObject[$Property] = $Object } } } } elseif ($InputObjects.Count -gt 0) { foreach ($ItemObject in $InputObjects) { $OutputObject = [ordered]@{} ConvertTo-FlatObject -Objects @(, $ItemObject) -Separator $Separator -Base $Base -Depth $Depth -Path $Path -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty [PSCustomObject] $OutputObject } } } } #EndRegion '.\Public\ConvertTo-FlatObject.ps1' 162 #Region '.\Public\Get-SEPClientDefVersions.ps1' 0 function Get-SEPClientDefVersions { <# .SYNOPSIS Gets a list of clients for a group by content version. .DESCRIPTION Gets a list of clients for a group by content version. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPClientDefVersions version clientsCount ------- ------------ 2023-09-04 rev. 002 15 2023-09-03 rev. 002 4 2023-09-01 rev. 008 2 2023-08-31 rev. 021 2 2023-08-31 rev. 002 1 2023-08-29 rev. 003 1 Gets a list of clients grouped by content version. #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/stats/client/content" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPClientDefVersions.ps1' 60 #Region '.\Public\Get-SEPClientInfectedStatus.ps1' 0 function Get-SEPClientInfectedStatus { <# .SYNOPSIS Gets SEP Clients with Infected or Clean status .DESCRIPTION Gets SEP Clients with Infected or Clean status NOTES : Clean status is just Infected = 0 .INPUTS None .OUTPUTS List of SEP Clients with Infected status .PARAMETER Clean If specified, returns SEP Clients with Clean status .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE Get-SEPClientInfectedStatus Gets computer details for all computers in the domain .EXAMPLE Get-SEPClientInfectedStatus -Clean Gets computer details for all computers in the domain that are not infected #> [CmdletBinding()] param ( [Parameter()] [switch] $Clean, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { if ($SkipCertificateCheck) { $script:SkipCert = $true } } process { if ($clean) { $non_infected = Get-SEPComputers | Where-Object { $_.infected -ne 1 } return $non_infected } else { $infected = Get-SEPComputers | Where-Object { $_.infected -eq 1 } return $Infected } } } #EndRegion '.\Public\Get-SEPClientInfectedStatus.ps1' 55 #Region '.\Public\Get-SEPClientStatus.ps1' 0 function Get-SEPClientStatus { <# .SYNOPSIS Gets a list and count of the online and offline clients. .DESCRIPTION Gets a list and count of the online and offline clients. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE C:\PSSymantecSEPM> Get-SEPClientStatus lastUpdated clientCountStatsList ----------- -------------------- 1693910248728 {@{status=ONLINE; clientsCount=212}, @{status=OFFLINE; clientsCount=48}} Gets a list and count of the online and offline clients. #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/stats/client/onlinestatus" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPClientStatus.ps1' 54 #Region '.\Public\Get-SEPClientVersion.ps1' 0 function Get-SEPClientVersion { <# .SYNOPSIS Gets a list and count of clients by client product version. .DESCRIPTION Gets a list and count of clients by client product version. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> $SEPversions = Get-SEPClientVersion PS C:\PSSymantecSEPM> $SEPversions.clientVersionList version clientsCount formattedVersion ------- ------------ ---------------- 11.0.6000.550 1 11.0.6 (11.0 MR6) build 550 12.1.2015.2015 1 12.1.2 (12.1 RU2) build 2015 12.1.6867.6400 1 12.1.6 (12.1 RU6 MP4) build 6867 12.1.7004.6500 3 12.1.6 (12.1 RU6 MP5) build 7004 12.1.7454.7000 177 12.1.7 (12.1 RU7) build 7454 14.0.3752.1000 36 14.0.3 (14.0 RU3 MP7) build 1000 14.2.1031.0100 21 14.2.1 (14.2 RU1) build 0100 14.2.3335.1000 3 14.2.3 (14.2 RU3 MP3) build 1000 14.3.510.0000 12 14.3 (14.3) build 0000 14.3.558.0000 5 14.3 (14.3) build 0000 Gets a list and count of clients by client product version. #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/stats/client/version" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPClientVersion.ps1' 65 #Region '.\Public\Get-SEPComputers.ps1' 0 function Get-SEPComputers { <# .SYNOPSIS Gets the information about the computers in a specified domain .DESCRIPTION Gets the information about the computers in a specified domain. either from computer names or group names .PARAMETER ComputerName Specifies the name of the computer for which you want to get the information. Supports wildcards .PARAMETER GroupName Specifies the group full path name for which you want to get the information. Supports wildcards .PARAMETER IncludeSubGroups Specifies whether to include subgroups when querying by group name .EXAMPLE Get-SEPComputers Gets computer details for all computers in the domain .EXAMPLE "MyComputer1","MyComputer2" | Get-SEPComputers Gets computer details for the specified computer MyComputer via pipeline .EXAMPLE Get-SEPComputers -ComputerName "MyComputer*" Gets computer details for all computer names starting by MyComputer .EXAMPLE Get-SEPComputers -GroupName "My Company\EMEA\Workstations" Gets computer details for all computers in the specified group MyGroup .EXAMPLE Get-SEPComputers -GroupName "My Company\EMEA\Workstations" -IncludeSubGroups Gets computer details for all computers in the specified group MyGroup and its subgroups #> [CmdletBinding( DefaultParameterSetName = 'ComputerName' )] Param ( # ComputerName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ComputerName' )] [Alias("Hostname", "DeviceName", "Device", "Computer")] [String] $ComputerName, # group name [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GroupName' )] [Alias("Group")] [String] $GroupName, # switch parameter to include subgroups [Parameter( ParameterSetName = 'GroupName' )] [switch] $IncludeSubGroups, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # Using computer name API call if ($ComputerName) { $allResults = @() $URI = $script:BaseURLv1 + "/computers" # URI query strings $QueryStrings = @{ sort = "COMPUTER_NAME" pageIndex = 1 pageSize = 100 computerName = $ComputerName } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings do { # Invoke the request params $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Process the response $allResults += $resp.content # Increment the page index & update URI $QueryStrings.pageIndex++ $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings } until ($resp.lastPage -eq $true) # Add a PSTypeName to the object $allresults | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.Computer") } # return the response return $allResults } # Using computer name API call then filtering elseif ($GroupName) { $allResults = @() $URI = $script:BaseURLv1 + "/computers" # URI query strings $QueryStrings = @{ sort = "COMPUTER_NAME" pageIndex = 1 pageSize = 100 computerName = $ComputerName # empty string value to ensure the URI is constructed correctly & query all computers } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings do { # Invoke the request params $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Process the response $allResults += $resp.content # Increment the page index & update URI $QueryStrings.pageIndex++ $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings } until ($resp.lastPage -eq $true) # Filtering if ($IncludeSubGroups) { $allResults = $allResults | Where-Object { $_.group.name -like "$GroupName*" } } else { $allResults = $allResults | Where-Object { $_.group.name -eq $GroupName } } # Add a PSTypeName to the object $allresults | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.Computer") } # return the response return $allResults } # No parameters else { $allResults = @() $URI = $script:BaseURLv1 + "/computers" # URI query strings $QueryStrings = @{ sort = "COMPUTER_NAME" pageIndex = 1 pageSize = 100 } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings do { # Invoke the request params $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Process the response $allResults += $resp.content # Increment the page index & update URI $QueryStrings.pageIndex++ $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings } until ($resp.lastPage -eq $true) # Add a PSTypeName to the object $allresults | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.Computer") } # return the response return $allResults } } } #EndRegion '.\Public\Get-SEPComputers.ps1' 223 #Region '.\Public\Get-SEPFileDetails.ps1' 0 function Get-SEPFileDetails { <# .SYNOPSIS Gets the details of a binary file, such as the checksum and the file size .DESCRIPTION Gets the details of a binary file, such as the checksum and the file size .PARAMETER FileID The ID of the file to get the details of Is a required parameter Can be found in the command ID of the response from Send-SEPMCommandGetFile .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPFileDetails -FileID 12345678901234567890123456789 id fileSize checksum -- -------- -------- CD02BC8E0A6606D53533F2428BB86D4E 1071101 4BE0BB3B57044CAD186FB59C2B7A13BB #> [CmdletBinding()] param ( [Parameter()] [string] $FileID, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/command-queue/file/$FileID/details" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # URI query strings $QueryStrings = @{ file_id = $FileID } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPFileDetails.ps1' 68 #Region '.\Public\Get-SEPGUPList.ps1' 0 function Get-SEPGUPList { <# .SYNOPSIS Gets a list of group update providers .DESCRIPTION Gets a list of SEP clients acting as group update providers .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPGUPList Gets a list of GUPs clients .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPGUPList | Select-Object Computername, AgentVersion, IpAddress, port computerName agentVersion ipAddress port ------------ ------------ --------- ---- Server01 12.1.7454.7000 10.0.0.150 2967 Server02 14.3.558.0000 10.1.0.150 2967 Workstation01 12.1.7454.7000 192.168.0.1 2967 Workstation02 14.3.558.0000 192.168.1.1 2967 Gets a list of GUPs clients with specific properties #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/gup/status" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPGUPList.ps1' 62 #Region '.\Public\Get-SEPMAccessToken.ps1' 0 function Get-SEPMAccessToken { <# .SYNOPSIS Retrieves the API token for use in the rest of the module. .DESCRIPTION Retrieves the API token for use in the rest of the module. First will try to use the one that may have been provided as a parameter. If not provided, then will try to use the one already cached in memory. If still not found, will look to see if there is a file with the API token stored on disk Finally, if there is still no available token : - check if the SEPM server name is configured - check if the credentials are configured or stored on disk - query one from the SEPM server - store it in memory and on disk - return the token .PARAMETER AccessToken If provided, this will be returned instead of using the cached/configured value .OUTPUTS System.String #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [PSCustomObject] $AccessToken ) # First will try to use the one that may have been provided as a parameter. if (-not [String]::IsNullOrEmpty($AccessToken.token)) { if (Test-SEPMAccessToken -Token $AccessToken) { $script:accessToken = $AccessToken return $AccessToken } } # If not provided, then will try to use the one already cached in memory. if (-not [String]::IsNullOrEmpty($script:accessToken)) { if (Test-SEPMAccessToken -Token $script:accessToken) { return $script:accessToken } } # If still not found, will look to see if there is a file with the API token stored in the disk if (Test-Path $script:accessTokenFilePath) { $AccessToken = Import-Clixml -Path $script:accessTokenFilePath -ErrorAction Ignore if (Test-SEPMAccessToken -Token $AccessToken) { $script:accessToken = $AccessToken return $script:accessToken } } # Finally, if there is still no available token, query one from the SEPM server. # Then caches the token in memory and stores it in a file on disk as a SecureString # Test if the SEPM server name is configured if ($null -eq $script:configuration.ServerAddress) { $message = "SEPM Server name not found. Provide server name :" Write-Warning -Message $message $ServerAddress = Read-Host -Prompt $message Set-SepmConfiguration -ServerAddress $ServerAddress } # Look for credentials stored in the disk if (Test-Path $script:credentialsFilePath) { $script:Credential = Import-Clixml -Path $script:credentialsFilePath } if ($null -eq $script:Credential) { $message = "Credentials not found. Provide credentials :" Write-Warning -Message $message Set-SEPMAuthentication -credential (Get-Credential) } # Test the certificate of the SEPM server $URI_Authenticate = $script:BaseURLv1 + '/identity/authenticate' Test-SEPMCertificate -URI $URI_Authenticate # Construct the request $body = @{ "username" = $script:Credential.UserName "password" = ([System.Net.NetworkCredential]::new("", $script:Credential.Password).Password) "appName" = "PSSymantecSEPM PowerShell Module" "domain" = $script:configuration.domain } $Params = @{ Method = 'POST' Uri = $URI_Authenticate ContentType = "application/json" Body = ($body | ConvertTo-Json) } # Invoke the request and SkipCert if needed $Response = Invoke-ABRestMethod -params $Params # Sort the response $CachedToken = [PSCustomObject]@{ token = $response.token tokenExpiration = (Get-Date).AddSeconds($Response.tokenExpiration) SkipCert = $script:SkipCert } # Caches the token in memory $script:accessToken = $CachedToken # Stores it in a file on disk as a SecureString if (-not (Test-Path ($Script:accessTokenFilePath | Split-Path))) { New-Item -ItemType Directory -Path ($Script:accessTokenFilePath | Split-Path) -Force | Out-Null } $script:accessToken | Export-Clixml -Path $script:accessTokenFilePath -Force # return the token return $script:accessToken } #EndRegion '.\Public\Get-SEPMAccessToken.ps1' 117 #Region '.\Public\Get-SEPMAdmins.ps1' 0 Function Get-SEPMAdmins { <# .SYNOPSIS Displays a list of admins in the Symantec Database .DESCRIPTION Gets the list of administrators for a particular domain. The Git repo for this module can be found here: https://github.com/Douda/PSSymantecSEPM .PARAMETER AdminName Displays only a specific user from the Admin List .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE Get-SEPMAdmins .EXAMPLE Get-SEPMAdmins -AdminName admin #> [CmdletBinding()] Param ( # AdminName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [String] [Alias("Admin")] $AdminName, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/admin-users" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # URI query strings $QueryStrings = @{ domain = $script:configuration.domain } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Process the response if ([string]::IsNullOrEmpty($AdminName)) { return $resp } else { $resp = $resp | Where-Object { $_.loginName -eq $AdminName } return $resp } } } #EndRegion '.\Public\Get-SEPMAdmins.ps1' 83 #Region '.\Public\Get-SEPMCommandStatus.ps1' 0 function Get-SEPMCommandStatus { <# .SYNOPSIS Get Command Status Details .DESCRIPTION Gets the details of a command status .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> $status = Get-SEPMCommandStatus -Command_ID D17D6DF9877049559910DD7B0306711C content : {@{beginTime=; lastUpdateTime=; computerName=MyWorkstation01; computerIp=192.168.1.1; domainName=Default; currentLoginUserName=localadmin; stateId=0; subStateId=0; subStateDesc=; binaryFileId=; resultInXML=; computerId=ABCDEF2837CD5C4FD167AD5E2CB31C71; hardwareKey=ABCDEF2837CD5C4FD167AD5E2CB31C71}} number : 0 size : 20 sort : {@{direction=ASC; property=Begintime; ascending=True}} numberOfElements : 1 firstPage : True totalPages : 1 lastPage : True totalElements : 1 PS C:\PSSymantecSEPM> $status.content beginTime : lastUpdateTime : computerName : MyWorkstation01 computerIp : 192.168.1.1 domainName : Default currentLoginUserName : localadmin stateId : 0 subStateId : 0 subStateDesc : binaryFileId : resultInXML : computerId : ABCDEF2837CD5C4FD167AD5E2CB31C71 hardwareKey : ABCDEF2837CD5C4FD167AD5E2CB31C71 Gets the status of a command .PARAMETER Command_ID The ID of the command to get the status of #> [CmdletBinding()] param ( [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true )] [string] [Alias("ID", "CommandID")] $Command_ID, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/command-queue/$command_id" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMCommandStatus.ps1' 88 #Region '.\Public\Get-SEPMDatabaseInfo.ps1' 0 function Get-SEPMDatabaseInfo { <# .SYNOPSIS Gets the database infromation of local site. .DESCRIPTION Gets the database infromation of local site .PARAMETER SkipCertificateCheck Skip certificate check .INPUTS None .OUTPUTS System.Object .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMDatabaseInfo name : SQLSRV01 description : address : SQLSRV01 instanceName : port : 1433 type : Microsoft SQL Server version : 12.00.5000 installedBySepm : False database : sem5 dbUser : sem5 dbPasswords : dbTLSRootCertificate : Gets detailed information on the database of the local site #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/admin/database" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMDatabaseInfo.ps1' 67 #Region '.\Public\Get-SEPMDomain.ps1' 0 function Get-SEPMDomain { <# .SYNOPSIS Gets a list of all accessible domains .DESCRIPTION Gets a list of all accessible domains .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMDomain id : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX name : Default description : createdTime : 1360247301316 enable : True companyName : contactInfo : administratorCount : 15 Gets a list of all accessible domains #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/domains" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMDomain.ps1' 60 #Region '.\Public\Get-SEPMEventInfo.ps1' 0 function Get-SEPMEventInfo { <# .SYNOPSIS Gets the information about the computers in a specified domain .DESCRIPTION Gets the information about the computers in a specified domain. A system administrator account is required for this REST API. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> $SEPMEvents = Get-SEPMEventInfo lastUpdated totalUnacknowledgedMessages criticalEventsInfoList ----------- --------------------------- ---------------------- 1693911276712 4906 {@{eventId=XXXXXXXXXXXXXXXXXXXXXXXXX; eventDateTime=2023-08-12 19:22:21.0... PS C:\PSSymantecSEPM> $SEPMEvents.criticalEventsInfoList | Select-Object -First 1 eventId : XXXXXXXXXXXXXXXXXXXXXXXXX eventDateTime : 2023-08-12 19:22:21.0 subject : CRITICAL: OLD SONAR DEFINITIONS message : 306 computers found with SONAR definitions older than 7 days. acknowledged : 0 Example of an event gathered from the SEPM server. #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/events/critical" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMEventInfo.ps1' 64 #Region '.\Public\Get-SEPMExceptionPolicy.ps1' 0 function Get-SEPMExceptionPolicy { <# .SYNOPSIS Get Exception Policy .DESCRIPTION Get Exception Policy details Note this is a V2 API call, and replies are originally JSON based .PARAMETER PolicyName The name of the policy to get the details of Is a required parameter .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMExceptionPolicy -PolicyName "Standard Servers - Exception policy" Name Value ---- ----- sources {} configuration {[files, System.Object[]], [non_pe_rules, System.Object[]], [directories, System.Object[]], [webdomains, System.Object[]]…} lockedoptions {[knownrisk, True], [extension, True], [file, True], [domain, True]…} enabled True desc name Standard Servers - Exception policy lastmodifiedtime 1646398353107 Shows an example of getting the Exception policy details for the policy named "Standard Servers - Exception policy" #> [CmdletBinding()] Param ( # PolicyName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [Alias("Policy_Name")] [String] $PolicyName, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } # BaseURL V2 $URI = $script:BaseURLv2 + "/policies/exceptions" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } # Stores the policy summary for all policies only once $policies = Get-SEPMPoliciesSummary } process { # Get Policy ID from policy name $policyID = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty id $policy_type = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty policytype if ($policy_type -ne "exceptions") { $message = "policy type is not of type EXCEPTIONS or does not exist - Please verify the policy name" Write-Error -Message $message throw $message } # Updating URI with policy ID $URI = $URI + "/" + $policyID # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers UseBasicParsing = $true } $resp = Invoke-ABRestMethod -params $params # JSON response to convert to PSObject $resp = $resp | ConvertFrom-Json -AsHashtable -Depth 100 # return the response return $resp } } #EndRegion '.\Public\Get-SEPMExceptionPolicy.ps1' 97 #Region '.\Public\Get-SEPMFileFingerprintList.ps1' 0 function Get-SEPMFileFingerprintList { <# TODO update help .SYNOPSIS Get File Finger Print List By Name .DESCRIPTION Gets the file fingerprint list for a specified Name as a set of hash values .PARAMETER FingerprintListName The name of the file fingerprint list .PARAMETER FingerprintListID The ID of the file fingerprint list .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMFileFingerprintList -FingerprintListName "Fingerprint list for workstations" id : 2A331150CDB44B9A9F1332E27321A1EE name : Fingerprint list for workstations hashType : MD5 source : WEBSERVICE description : data : {01BCE403043C0695EBB04D89C2B3A027, 03F3C0A7A2DD4EE1E81FABDBC557E2E8, 043A1B77C731F053FCA5DCC4AA18838F, 07996DCEEA57D8615B91A48AA7B49EC3…} groupIds : {46B9A36B0A66062224C839F606E6B1CE, AD3CD4620A95B05502CBDB658A6F7BE3, 09CC40530A6606221853DEA0AC606451, 96017A1E0A6906231EFEACCBD915B592…} Gets the file fingerprint list for a specified Name as a set of hash values .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMFileFingerprintList -FingerprintListID 2A331150CDB44B9A9F1332E27321A1EE id : 2A331150CDB44B9A9F1332E27321A1EE name : ASD01P0215 hashType : MD5 source : WEBSERVICE description : data : {01BCE403043C0695EBB04D89C2B3A027, 03F3C0A7A2DD4EE1E81FABDBC557E2E8, 043A1B77C731F053FCA5DCC4AA18838F, 07996DCEEA57D8615B91A48AA7B49EC3…} groupIds : {46B9A36B0A66062224C839F606E6B1CE, AD3CD4620A95B05502CBDB658A6F7BE3, 09CC40530A6606221853DEA0AC606451, 96017A1E0A6906231EFEACCBD915B592…} Gets the file fingerprint list for a specified ID as a set of hash values #> [CmdletBinding()] param ( [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [string] $FingerprintListName, [Parameter( ValueFromPipelineByPropertyName = $true )] [string] $FingerprintListID, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { if ($FingerprintListName) { $URI = $script:BaseURLv1 + "/policy-objects/fingerprints" # URI query strings $QueryStrings = @{ name = $FingerprintListName } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params } if ($FingerprintListID) { $URI = $script:BaseURLv1 + "/policy-objects/fingerprints/$FingerprintListID" # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params } # return the response return $resp } } #EndRegion '.\Public\Get-SEPMFileFingerprintList.ps1' 114 #Region '.\Public\Get-SEPMFirewallPolicy.ps1' 0 function Get-SEPMFirewallPolicy { <# .SYNOPSIS Get Firewall Policy .DESCRIPTION Get Firewall Policy details .PARAMETER PolicyName The name of the policy to get the details of Is a required parameter .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMFirewallPolicy -PolicyName "Standard Servers - Firewall policy" sources : configuration : @{enforced_rules=System.Object[]; baseline_rules=System.Object[]; ignore_parent_rules=; smart_dhcp=False; smart_dns=False; smart_wins=False; token_ring_traffic=False; netbios_protection=False; reverse_dns=False; port_scan=False; dos=False; antimac_spoofing=False; autoblock=False; autoblock_duration=600; stealth_web=False; antiIP_spoofing=False; hide_os=False; windows_firewall=NO_ACTION; windows_firewall_notification=False; endpoint_notification=; p2p_auth=; mac=} enabled : True desc : Standard Server Firewall Policy - This policy is for standard servers. It is a strict policy that blocks all traffic except for the services that are explicitly allowed. name : Standard Servers - Firewall policy lastmodifiedtime : 1692253688318 Shows an example of getting the firewall policy details for the policy named "Standard Servers - Firewall policy" #> [CmdletBinding()] Param ( # PolicyName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [Alias("Policy_Name")] [String] $PolicyName, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/policies/firewall" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } # Stores the policy summary for all policies only once $policies = Get-SEPMPoliciesSummary } process { # Get Policy ID from policy name $policyID = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty id $policy_type = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty policytype if ($policy_type -ne "fw") { $message = "policy type is not of type FIREWALL or does not exist - Please verify the policy name" Write-Error -Message $message throw $message } # Updating URI with policy ID $URI = $URI + "/" + $policyID # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMFirewallPolicy.ps1' 88 #Region '.\Public\Get-SEPMGroups.ps1' 0 function Get-SEPMGroups { <# .SYNOPSIS Gets a group list .DESCRIPTION Gets a group list .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\GitHub_Projects\PSSymantecSEPM> Get-SEPMGroups | Select-Object -First 1 id : XXXXXXXXXXXXXXXXXXXXXXXXX name : My Company description : fullPathName : My Company numberOfPhysicalComputers : 0 numberOfRegisteredUsers : 0 createdBy : XXXXXXXXXXXXXXXXXXXXXXXXX created : 1360247401336 lastModified : 1639056401576 policySerialNumber : 718B-09/04/2023 12:56:58 775 policyDate : 1693832218775 customIpsNumber : domain : @{id=XXXXXXXXXXXXXXXXXXXXXXXXX; name=Default} policyInheritanceEnabled : False Gets the first group of the list of groups #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/groups" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } # QueryString parameters for pagination $QueryStrings = @{ pageSize = 25 pageIndex = 1 } # Invoke the request do { try { # Invoke the request params $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Process the response $allResults += $resp.content # Increment the page index & update URI $QueryStrings.pageIndex++ $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings } catch { Write-Warning -Message "Error: $_" } } until ($resp.lastPage -eq $true) # return the response return $allResults } } #EndRegion '.\Public\Get-SEPMGroups.ps1' 95 #Region '.\Public\Get-SEPMIpsPolicy.ps1' 0 function Get-SEPMIpsPolicy { # TODO : returned object has empty configuration fields. Could be a bug ? # Example # PS C:\PSSymantecSEPM> $IPS_example | ConvertTo-Json # { # "sources": null, # "configuration": {}, # "enabled": true, # "desc": "Summary : added IP as excluded host to avoid ServiceNow discovery service conflicts", # "name": "Intrusion Prevention policy PRODUCTION", # "lastmodifiedtime": 1693559858824 # } <# .SYNOPSIS Get IPS Policy .DESCRIPTION Get IPS Policy details .PARAMETER PolicyName The name of the policy to get the details of Is a required parameter .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMIpsPolicy -PolicyName "Intrusion Prevention policy PRODUCTION" sources : configuration : enabled : True desc : IPS description field name : Intrusion Prevention policy PRODUCTION lastmodifiedtime : 1693559858824 Shows an example of getting the IPS policy details for the policy named "Intrusion Prevention policy PRODUCTION" #> [CmdletBinding()] Param ( # PolicyName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [Alias("Policy_Name")] [String] $PolicyName, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/policies/ips" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } # Stores the policy summary for all policies only once $policies = Get-SEPMPoliciesSummary } process { # Get Policy ID from policy name $policyID = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty id $policy_type = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty policytype if ($policy_type -ne "ips") { $message = "policy type is not of type IPS or does not exist - Please verify the policy name" Write-Error -Message $message throw $message } # Updating URI with policy ID $URI = $URI + "/" + $policyID # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers UseBasicParsing = $true } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMIpsPolicy.ps1' 99 #Region '.\Public\Get-SEPMLatestDefinition.ps1' 0 function Get-SEPMLatestDefinition { <# .SYNOPSIS Get AV Def Latest Info .DESCRIPTION Gets the latest revision information for antivirus definitions from Symantec Security Response. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMLatestDefinition contentName publishedBySymantec publishedBySEPM ----------- ------------------- --------------- AV_DEFS 9/4/2023 rev. 2 9/4/2023 rev. 2 Gets the latest revision information for antivirus definitions from Symantec Security Response. #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/content/avdef/latest" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMLatestDefinition.ps1' 55 #Region '.\Public\Get-SEPMPoliciesSummary.ps1' 0 function Get-SEPMPoliciesSummary { <# .SYNOPSIS Get summary of all or feature specific policies .DESCRIPTION Get the policy summary for specified policy type. Also gets the list of groups to which the policies are assigned. .PARAMETER PolicyType The policy type for which the summary is to be retrieved. The valid values are hid, exceptions, mem, ntr, av, fw, ips, lu, hi, adc, msl, upgrade. If not specified, the summary for all policies is retrieved. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMPoliciesSummary content : {@{sources=System.Object[]; enabled=True; desc=Created automatically during product ...}} size : 136 number : 0 sort : numberOfElements : 136 totalElements : 136 totalPages : 1 lastPage : True firstPage : True Get policy statistics for all policies and its assigned groups .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMPoliciesSummary -PolicyType fw Get policy statistics for firewall policies and its assigned groups #> [CmdletBinding()] param ( [Parameter()] [ValidateSet( 'hid', 'exceptions', 'mem', 'ntr', 'av', 'fw', 'ips', 'lucontent', 'lu', 'hi', 'adc', 'msl', # Currently getting an error when trying to get this policy type # {"errorCode":"400","appErrorCode":"","errorMessage":"The policy type argument is invalid."} # TODO: Investigate this error 'upgrade' )] [string] $PolicyType, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/policies/summary" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } # Get the list of groups and IDs to inject into the response $groups = Get-SEPMGroups } process { if (-not $PolicyType) { # Invoke the request try { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Add group FullPath to the response from their Group ID for ease of use # Parsing every response object foreach ($policy in $resp.content) { # Parsing every location this policy is applied to foreach ($location in $policy.assignedtolocations) { # Getting the group name from the group ID, and adding it to the response object $group = $groups | Where-Object { $_.id -match $location.groupid } | Get-Unique $location | Add-Member -NotePropertyName "groupNameFullPath" -NotePropertyValue $group.fullPathName } } } catch { Write-Warning -Message "Error: $_" } # return the response return $resp.content } if ($PolicyType) { $URI = $script:BaseURLv1 + "/policies/summary" + "/" + $PolicyType # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } # Invoke the request try { $resp = Invoke-ABRestMethod -params $params # Add group FullPath to the response from their Group ID for ease of use # Parsing every response object foreach ($policy in $resp.content) { # Parsing every location this policy is applied to foreach ($location in $policy.assignedtolocations) { # Getting the group name from the group ID, and adding it to the response object $group = $groups | Where-Object { $_.id -match $location.groupid } | Get-Unique $location | Add-Member -NotePropertyName "groupNameFullPath" -NotePropertyValue $group.fullPathName } } } catch { Write-Warning -Message "Error: $_" } # return the response return $resp.content } } } #EndRegion '.\Public\Get-SEPMPoliciesSummary.ps1' 143 #Region '.\Public\Get-SEPMReplicationStatus.ps1' 0 function Get-SEPMReplicationStatus { <# .SYNOPSIS Get Replication Status .DESCRIPTION Get Replication Status .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMReplicationStatus replicationStatus ----------------- @{siteName=Site Europe; siteLocation=Paris; replicationPartnerStatusList=System.Object[]; id=XXXXXXXXXXXXXXXXXXXXXXXX} Get a list of replication status with every remote site #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/replication/status" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMReplicationStatus.ps1' 55 #Region '.\Public\Get-SEPMThreatStats.ps1' 0 function Get-SEPMThreatStats { <# .SYNOPSIS Gets threat statistics .DESCRIPTION Gets threat statistics .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMThreatStats Stats ----- @{lastUpdated=1693912098821; infectedClients=1} Gets threat statistics #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/stats/threat" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMThreatStats.ps1' 55 #Region '.\Public\Get-SEPMVersion.ps1' 0 Function Get-SEPMVersion { <# .SYNOPSIS Gets the current version of Symantec Endpoint Protection Manager. .DESCRIPTION Gets the current version of Symantec Endpoint Protection Manager. This function dot not require authentication. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMVersion API_SEQUENCE API_VERSION version ------------ ----------- ------- 230504014 14.3.7000 14.3.9816.7000 Gets the current version of Symantec Endpoint Protection Manager. #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/version" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Get-SEPMVersion.ps1' 55 #Region '.\Public\Remove-SEPMFileFingerprintList.ps1' 0 function Remove-SEPMFileFingerprintList { <# .SYNOPSIS Deletes a file fingerprint list .DESCRIPTION Deletes a file fingerprint list, and removes it from a group to which it applies .PARAMETER FingerprintListName The name of the file fingerprint list .PARAMETER FingerprintListID The ID of the file fingerprint list .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Remove-SEPMFileFingerprintList -FingerprintListName "Fingerprint list for workstations" Removes the file fingerprint list with the name "Fingerprint list for workstations" .EXAMPLE PS C:\PSSymantecSEPM> "Fingerprint list for workstations" | Remove-SEPMFileFingerprintList Removes the file fingerprint list with the name "Fingerprint list for workstations" via the pipeline .EXAMPLE PS C:\PSSymantecSEPM> Remove-SEPMFileFingerprintList -FingerprintListID 2A331150CDB44B9A9F1332E27321A1EE Removes the file fingerprint list with the ID "2A331150CDB44B9A9F1332E27321A1EE" #> [CmdletBinding( DefaultParameterSetName = 'Name' )] param ( [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Name' )] [string] $FingerprintListName, [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ID' )] [string] $FingerprintListID, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # Get the FingerprintListID if the FingerprintListName is provided if ($FingerprintListName) { $URI = $script:BaseURLv1 + "/policy-objects/fingerprints" $FingerprintListID = Get-SEPMFileFingerprintList -FingerprintListName $FingerprintListName | Select-Object -ExpandProperty id } $URI = $script:BaseURLv1 + "/policy-objects/fingerprints/$FingerprintListID" $params = @{ Method = 'DELETE' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Remove-SEPMFileFingerprintList.ps1' 86 #Region '.\Public\Reset-SepmConfiguration.ps1' 0 function Reset-SEPMConfiguration { <# .SYNOPSIS Clears out the user's configuration file and configures this session with all default configuration values. .DESCRIPTION Clears out the user's configuration file and configures this session with all default configuration values. .EXAMPLE Reset-SEPMConfiguration Deletes the local configuration file and loads in all default configuration values. .NOTES This command will not clear your authentication token. Please use Clear-SEPMAuthentication to accomplish that. #> $null = Remove-Item -Path $script:configurationFilePath -Force -ErrorAction SilentlyContinue -ErrorVariable ev if (($null -ne $ev) -and ($ev.Count -gt 0) -and ($ev[0].FullyQualifiedErrorId -notlike 'PathNotFound*')) { $message = "Reset was unsuccessful. Experienced a problem trying to remove the file [$script:configurationFilePath]." Write-Warning -Message $message } $message = "This has not cleared your authentication token. Call Clear-SEPMAuthentication to accomplish that." Write-Verbose -Message $message } #EndRegion '.\Public\Reset-SepmConfiguration.ps1' 31 #Region '.\Public\Restore-SEPMAuthentication.ps1' 0 function Restore-SEPMAuthentication { <# .SYNOPSIS Sets the specified file to be the user's authentication file. .DESCRIPTION Sets the specified file to be the user's authentication file. This is primarily used for unit testing scenarios. .PARAMETER Path The path to store the user's current authentication file. .EXAMPLE Restore-SEPMAuthentication -Path 'c:\foo\config.xml' Makes the contents of c:\foo\config.xml be the user's authentication for the module. #> [CmdletBinding()] param( [ValidateScript({ if (Test-Path -Path $_ -PathType Leaf) { $true } else { throw "$_ does not exist." } })] [string] $Path, [Parameter(ParameterSetName = 'AccessToken')] [switch] $AccessToken, [Parameter(ParameterSetName = 'Credential')] [switch] $Credential ) if ($AccessToken) { # Make sure that the path that we're going to be storing the file exists. $null = New-Item -Path (Split-Path -Path $script:accessTokenFilePath -Parent) -ItemType Directory -Force $null = Copy-Item -Path $Path -Destination $script:accessTokenFilePath -Force } if ($Credential) { # Make sure that the path that we're going to be storing the file exists. $null = New-Item -Path (Split-Path -Path $script:credentialsFilePath -Parent) -ItemType Directory -Force $null = Copy-Item -Path $Path -Destination $script:credentialsFilePath -Force } Initialize-SepmConfiguration } #EndRegion '.\Public\Restore-SEPMAuthentication.ps1' 48 #Region '.\Public\Restore-SEPMConfiguration.ps1' 0 function Restore-SEPMConfiguration { <# .SYNOPSIS Sets the specified file to be the user's configuration file. .DESCRIPTION Sets the specified file to be the user's configuration file. This is primarily used for unit testing scenarios. .PARAMETER Path The path to store the user's current configuration file. .EXAMPLE Restore-SEPMConfiguration -Path 'c:\foo\config.json' Makes the contents of c:\foo\config.json be the user's configuration for the module. #> [CmdletBinding()] param( [ValidateScript({ if (Test-Path -Path $_ -PathType Leaf) { $true } else { throw "$_ does not exist." } })] [string] $Path ) # Make sure that the path that we're going to be storing the file exists. $null = New-Item -Path (Split-Path -Path $script:configurationFilePath -Parent) -ItemType Directory -Force $null = Copy-Item -Path $Path -Destination $script:configurationFilePath -Force Initialize-SepmConfiguration } #EndRegion '.\Public\Restore-SEPMConfiguration.ps1' 33 #Region '.\Public\Send-SEPMCommandClearIronCache.ps1' 0 function Send-SEPMCommandClearIronCache { <# # TODO update help .SYNOPSIS Send a quarantine/unquarantine command to SEP endpoints .DESCRIPTION Send a quarantine/unquarantine command to SEP endpoints .PARAMETER ComputerName The name of the computer to send the command to Cannot be used with GroupName .PARAMETER GroupName The name of the group to send the command to Cannot be used with ComputerName Does not include subgroups .PARAMETER Unquarantine Switch parameter to unquarantine the SEP client .PARAMETER SHA256 SHA256 hash of the suspicious file. Cannot be used with MD5 or SHA1 .PARAMETER MD5 MD5 hash of the suspicious file. Cannot be used with SHA256 or SHA1 .PARAMETER SHA1 SHA1 hash of the suspicious file. Cannot be used with SHA256 or MD5 .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE Send-SEPMCommandClearIronCache -ComputerName "Computer1" Sends a command to quarantine Computer1 .EXAMPLE "Computer1", "Computer2" | Send-SEPMCommandClearIronCache Sends a command to quarantine Computer1 and Computer2 .EXAMPLE Send-SEPMCommandClearIronCache -GroupName "My Company\EMEA\Workstations\Site1" Sends a command to quarantine all computers in "My Company\EMEA\Workstations\Site1" Does not include subgroups #> #TODO finish function [CmdletBinding()] param ( # ComputerName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ComputerName' )] [Alias("Hostname", "DeviceName", "Device", "Computer")] [String] $ComputerName, # group name [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GroupName' )] [Alias("Group")] [String] $GroupName, # SHA256 hash of the suspicious file. [Parameter(ParameterSetName = 'SHA256Hash')] [Parameter(ParameterSetName = 'ComputerName')] [ValidateScript({ if ($_.Length -ne 64) { throw "SHA256 hash must be 64 characters long" } return $true })] [string] $SHA256, # MD5 hash of the suspicious file. [Parameter(ParameterSetName = 'MD5Hash')] [Parameter(ParameterSetName = 'ComputerName')] [ValidateScript({ if ($_.Length -ne 32) { throw "MD5 hash must be 32 characters long" } return $true })] [string] $MD5, # SHA1 hash of the suspicious file. [Parameter(ParameterSetName = 'SHA1Hash')] [Parameter(ParameterSetName = 'ComputerName')] [ValidateScript({ if ($_.Length -ne 40) { throw "SHA1 hash must be 40 characters long" } return $true })] [string] $SHA1, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { if ($ComputerName) { # Get computer ID(s) from computer name(s) $ComputerIDList = @() foreach ($C in $ComputerName) { $ComputerID = Get-SEPComputers -ComputerName $C | Select-Object -ExpandProperty uniqueId $ComputerIDList += $ComputerID } $URI = $script:BaseURLv1 + "/command-queue/ironcache" # URI query strings $QueryStrings = @{ computer_ids = $ComputerIDList } # Construct the URI $builder = New-Object System.UriBuilder($URI) $query = [System.Web.HttpUtility]::ParseQueryString($builder.Query) foreach ($param in $QueryStrings.GetEnumerator()) { $query[$param.Key] = $param.Value } $builder.Query = $query.ToString() $URI = $builder.ToString() # Building body and add correct hash to body # TODO verify this works $body = @{ FingerPrintListPayload = @{ data = @() } } $hashParameter = $PSCmdlet.MyInvocation.BoundParameters.Keys | Where-Object { $_ -in @('SHA256', 'MD5', 'SHA1') } switch ($hashParameter) { 'SHA256' { $body.FingerPrintListPayload.Add("hashType", "sha256") $body.FingerPrintListPayload.data += $SHA256 } 'MD5' { $body.FingerPrintListPayload.Add("hashType", "md5") $body.FingerPrintListPayload.data += $MD5 } 'SHA1' { $body.FingerPrintListPayload.Add("hashType", "sha1") $body.FingerPrintListPayload.data += $SHA1 } } # Invoke the request params $params = @{ Method = 'POST' Uri = $URI headers = $headers Body = $body | ConvertTo-Json ContentType = 'application/json' } $resp = Invoke-ABRestMethod -params $params # return the response return $resp } # If group name is specified elseif ($GroupName) { # Get group ID from group name $GroupID = Get-SEPMGroups | Where-Object { $_.fullPathName -eq $GroupName } | Select-Object -ExpandProperty id -First 1 $URI = $script:BaseURLv1 + "/command-queue/ironcache" # URI query strings $QueryStrings = @{ group_ids = $GroupID } # Construct the URI $builder = New-Object System.UriBuilder($URI) $query = [System.Web.HttpUtility]::ParseQueryString($builder.Query) foreach ($param in $QueryStrings.GetEnumerator()) { $query[$param.Key] = $param.Value } $builder.Query = $query.ToString() $URI = $builder.ToString() # Invoke the request params $params = @{ Method = 'POST' Uri = $URI headers = $headers ContentType = 'application/json' } $resp = Invoke-ABRestMethod -params $params # return the response return $resp } } } #EndRegion '.\Public\Send-SEPMCommandClearIronCache.ps1' 212 #Region '.\Public\Send-SEPMCommandGetFile.ps1' 0 function Send-SEPMCommandGetFile { <# .SYNOPSIS Sends a commands to request a suspicious file be uploaded back to Symantec Endpoint Protection Manager .DESCRIPTION Sends a commands to request a suspicious file be uploaded back to Symantec Endpoint Protection Manager .PARAMETER ComputerName The list of computers on which to search for the suspicious file. .PARAMETER SHA256 SHA256 hash of the suspicious file. .PARAMETER MD5 MD5 hash of the suspicious file. .PARAMETER SHA1 SHA1 hash of the suspicious file. .PARAMETER Source The source to search for the suspicious file Possible values are: FILESYSTEM (default), QUARANTINE, or BOTH. 12.1.x clients only use FILESYSTEM. .PARAMETER FilePath The file path of the suspicious file. .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Send-SEPMCommandGetFile -ComputerName MyWorkstation01 -SHA256 1234567890123456789012345678901234567890123456789012345678901234 -FilePath C:\Temp\malware.exe -Source BOTH Sends a command to request the following file C:\Temp\malware.exe be uploaded from MyWorkstation01 to Symantec Endpoint Protection Manager. Requests includes both the file system and quarantine locations to be looked at. #> [CmdletBinding()] param ( # The list of computers on which to search for the suspicious file. [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("Hostname", "DeviceName", "Device", "Computer")] [String] $ComputerName, # SHA256 hash of the suspicious file. [Parameter(ParameterSetName = 'SHA256Hash')] [ValidateScript({ if ($_.Length -ne 64) { throw "SHA256 hash must be 64 characters long" } return $true })] [string] $SHA256, # MD5 hash of the suspicious file. [Parameter(ParameterSetName = 'MD5Hash')] [ValidateScript({ if ($_.Length -ne 32) { throw "MD5 hash must be 32 characters long" } return $true })] [string] $MD5, # SHA1 hash of the suspicious file. [Parameter(ParameterSetName = 'SHA1Hash')] [ValidateScript({ if ($_.Length -ne 40) { throw "SHA1 hash must be 40 characters long" } return $true })] [string] $SHA1, # The source to search for the suspicious file [Parameter()] [ValidateSet('FILESYSTEM ', 'QUARANTINE', 'BOTH')] [string] $Source, # The file path of the suspicious file. [Parameter()] [ValidateScript({ if ($_ -notmatch '^.+\\[^\\]+\.[^\\]+$') { throw "The string must be a file path ending with a file name and an extension" } return $true })] [Alias("Path")] [string] $FilePath, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # Get computer ID(s) from computer name(s) $ComputerIDList = @() foreach ($C in $ComputerName) { $ComputerID = Get-SEPComputers -ComputerName $C | Select-Object -ExpandProperty uniqueId $ComputerIDList += $ComputerID } $URI = $script:BaseURLv1 + "/command-queue/files" # URI query strings $QueryStrings = @{ computer_ids = $ComputerIDList file_path = $FilePath source = $Source } # Add correct hash to query strings if ($SHA256) { $QueryStrings.Add("sha256", $SHA256) } elseif ($MD5) { $QueryStrings.Add("md5", $MD5) } elseif ($SHA1) { $QueryStrings.Add("sha1", $SHA1) } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Send-SEPMCommandGetFile.ps1' 147 #Region '.\Public\Send-SEPMCommandQuarantine.ps1' 0 function Send-SEPMCommandQuarantine { <# .SYNOPSIS Send a quarantine/unquarantine command to SEP endpoints .DESCRIPTION Send a quarantine/unquarantine command to SEP endpoints .PARAMETER ComputerName The name of the computer to send the command to Cannot be used with GroupName .PARAMETER GroupName The name of the group to send the command to Cannot be used with ComputerName Does not include subgroups .PARAMETER Unquarantine Switch parameter to unquarantine the SEP client .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE Send-SEPMCommandQuarantine -ComputerName "Computer1" Sends a command to quarantine Computer1 .EXAMPLE "Computer1", "Computer2" | Send-SEPMCommandQuarantine Sends a command to quarantine Computer1 and Computer2 .EXAMPLE Send-SEPMCommandQuarantine -GroupName "My Company\EMEA\Workstations\Site1" Sends a command to quarantine all computers in "My Company\EMEA\Workstations\Site1" Does not include subgroups #> [CmdletBinding()] param ( # ComputerName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ComputerName' )] [Alias("Hostname", "DeviceName", "Device", "Computer")] [String] $ComputerName, # group name [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GroupName' )] [Alias("Group")] [String] $GroupName, # Unquarantine [Parameter()] [switch] $Unquarantine, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { if ($ComputerName) { # Get computer ID(s) from computer name(s) $ComputerIDList = @() foreach ($C in $ComputerName) { $ComputerID = Get-SEPComputers -ComputerName $C | Select-Object -ExpandProperty uniqueId $ComputerIDList += $ComputerID } $URI = $script:BaseURLv1 + "/command-queue/quarantine" # URI query strings $QueryStrings = @{ computer_ids = $ComputerIDList } # Add unquarantine if specified if ($Unquarantine) { $QueryStrings['undo'] = $true } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } # If group name is specified elseif ($GroupName) { # Get group ID from group name $GroupID = Get-SEPMGroups | Where-Object { $_.fullPathName -eq $GroupName } | Select-Object -ExpandProperty id -First 1 $URI = $script:BaseURLv1 + "/command-queue/quarantine" # URI query strings $QueryStrings = @{ group_ids = $GroupID } # Add unquarantine if specified if ($Unquarantine) { $QueryStrings['undo'] = $true } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } } #EndRegion '.\Public\Send-SEPMCommandQuarantine.ps1' 144 #Region '.\Public\Set-SepmAuthentication.ps1' 0 function Set-SEPMAuthentication { <# .SYNOPSIS Allows the user to configure the SEPM Authentication .DESCRIPTION Allows the user to configure the SEPM Authentication Username and password will be securely stored on the machine for use in all future PowerShell sessions. .PARAMETER Credentials A PSCredential object containing the username and password to use for authentication .EXAMPLE Set-SEPMAuthentication Credentials (Get-Credential) Prompts the user for username and password, saves them to disk and in the PS Session .EXAMPLE $Credentials = Get-Credential Set-SEPMAuthentication -Credential $cred #> [CmdletBinding()] param( [PSCredential] $Credentials ) # If no credentials are provided, prompt the user for them if ($null -eq $Credentials) { $Credentials = Get-Credential } # If the user provides a username and password, verify if password is not null or empty if ([String]::IsNullOrWhiteSpace($Credentials.GetNetworkCredential().Password)) { $message = "Password not provided. Provide correct credentials and try again." Write-Error -Message $message throw $message } # Setting script scope variable so that credentials can be used in other functions $script:Credential = $Credentials # Saving credentials to disk $Credentials | Export-Clixml -Path $script:credentialsFilePath -Force } #EndRegion '.\Public\Set-SepmAuthentication.ps1' 47 #Region '.\Public\Set-SepmConfiguration.ps1' 0 function Set-SepmConfiguration { <# .SYNOPSIS Change the value of a configuration property for the PSSymantecSEPM module .DESCRIPTION Change the value of a configuration property for the PSSymantecSEPM module A single call to this method can set any number or combination of properties. .PARAMETER ServerAddress The hostname of the SEPM instance to communicate with. .PARAMETER Port The port number of the SEPM instance to communicate with. .EXAMPLE Set-SepmConfiguration -ServerAddress "MySEPMServer" Set the SEPM server address to "MySEPMServer" .EXAMPLE Set-SepmConfiguration -ServerAddress "MySEPMServer" -Port 8446 Set the SEPM server address to "MySEPMServer" and the port to 8446 #> [CmdletBinding( PositionalBinding = $false )] param( [string] $ServerAddress, [int] $Port ) # Load in the persisted configuration object $persistedConfig = Read-SepmConfiguration -Path $script:configurationFilePath # Update the configuration object with any values that were provided as parameters $properties = Get-Member -InputObject $script:configuration -MemberType NoteProperty | Select-Object -ExpandProperty Name # $PSBoundParameters is a hashtable of all the parameters that were passed to this function # We can use this to determine which properties were passed in and update the configuration object # Allows to easily add new properties by adding new parameters without having to update the below loop foreach ($name in $properties) { if ($PSBoundParameters.ContainsKey($name)) { $value = $PSBoundParameters.$name if ($value -is [switch]) { $value = $value.ToBool() } $script:configuration.$name = $value Add-Member -InputObject $persistedConfig -Name $name -Value $value -MemberType NoteProperty -Force } } # Persist the configuration object to disk Save-SepmConfiguration -Configuration $persistedConfig -Path $script:configurationFilePath # Re-initialize the configuration object Initialize-SepmConfiguration } #EndRegion '.\Public\Set-SepmConfiguration.ps1' 58 #Region '.\Public\Start-SEPMReplication.ps1' 0 function Start-SEPMReplication { <# TODO update help .SYNOPSIS Initiates replication with a remote site .DESCRIPTION Initiates replication with a remote site .PARAMETER partnerSiteName The name of the remote site to replicate with .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Start-SEPMReplication -partnerSiteName "Remote site Americas" code ---- 0 Initiates replication with the remote site Americas. Response code 0 indicates success. #> [CmdletBinding()] param ( [Parameter()] [string] $partnerSiteName, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck # TODO known bug with SEPM API, these parameters are returning invalid option if not set to false # [switch] # $logs, # [switch] # $ContentAndPackages ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/replication/replicatenow" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # URI query strings $QueryStrings = @{ partnerSiteName = $partnerSiteName logs = $logs content = $ContentAndPackages } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Start-SEPMReplication.ps1' 78 #Region '.\Public\Start-SEPScan.ps1' 0 function Start-SEPScan { <# .SYNOPSIS Sends Active Scan command to the specified computer(s) or group(s) .DESCRIPTION Sends a command from SEPM to SEP endpoints to request an active scan on the endpoint(s) .PARAMETER ComputerName Specifies the name of the computer for which you want to send the command. Accepts pipeline input by Value and ByPropertyName .PARAMETER GroupName Specifies the group full path name for which you want to send the command .PARAMETER ActiveScan Specifies the type of scan to send to the endpoint(s) Valid values are ActiveScan and FullScan By default, the ActiveScan switch is used .PARAMETER FullScan Specifies the type of scan to send to the endpoint(s) Valid values are ActiveScan and FullScan By default, the ActiveScan switch is used .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE PS C:\PSSymantecSEPM> Start-SEPScan -ComputerName MyComputer01 -ActiveScan Sends an active scan command to the specified computer MyComputer01 .EXAMPLE "MyComputer1","MyComputer2" | Start-SEPScan Sends an active scan command to the specified computers MyComputer1 & MyComputer2 via pipeline By default, the ActiveScan switch is used .EXAMPLE Start-SEPScan -GroupName "My Company\EMEA\Workstations" -fullscan Sends a fullscan command to all endpoints part of the group "My Company\EMEA\Workstations" #> [CmdletBinding( DefaultParameterSetName = 'ComputerNameActiveScan' )] Param ( # ComputerName [Parameter( ParameterSetName = 'ComputerNameActiveScan', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Parameter( ParameterSetName = 'ComputerNameFullScan', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("Hostname", "DeviceName", "Device", "Computer")] [String] $ComputerName, # group name [Parameter( ParameterSetName = 'GroupNameActiveScan', ValueFromPipelineByPropertyName = $true )] [Parameter( ParameterSetName = 'GroupNameFullScan', ValueFromPipelineByPropertyName = $true )] [Alias("Group")] [String] $GroupName, # ActiveScan [Parameter(ParameterSetName = 'ComputerNameActiveScan')] [Parameter(ParameterSetName = 'GroupNameActiveScan')] [switch] $ActiveScan, # FullScan [Parameter(ParameterSetName = 'ComputerNameFullScan')] [Parameter(ParameterSetName = 'GroupNameFullScan')] [switch] $FullScan, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # If specific computer name(s) are specified if ($ComputerName) { # Get computer ID(s) from computer name(s) $ComputerIDList = @() foreach ($C in $ComputerName) { $ComputerID = Get-SEPComputers -ComputerName $C | Select-Object -ExpandProperty uniqueId $ComputerIDList += $ComputerID } if ($ActiveScan) { $URI = $script:BaseURLv1 + "/command-queue/activescan" } if ($FullScan) { $URI = $script:BaseURLv1 + "/command-queue/fullscan" } # URI query strings $QueryStrings = @{ computer_ids = $ComputerIDList } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } # If by groupname elseif ($GroupName) { ####################################### # 1. finds all computers in the group # ####################################### $allComputers = @() $URI = $script:BaseURLv1 + "/computers" # URI query strings $QueryStrings = @{ sort = "COMPUTER_NAME" pageIndex = 1 pageSize = 100 computerName = $ComputerName # empty string value to ensure the URI is constructed correctly & query all computers } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # Get computer list do { try { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Process the response $allComputers += $resp.content # Increment the page index & update URI $QueryStrings.pageIndex++ $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings } catch { Write-Warning -Message "Error: $_" } } until ($resp.lastPage -eq $true) # filter list by group name $allComputers = $allComputers | Where-Object { $_.group.name -eq $GroupName } ################################################# # 2. send command to all computers in the group # ################################################# if ($ActiveScan) { $URI = $script:BaseURLv1 + "/command-queue/activescan" } if ($FullScan) { $URI = $script:BaseURLv1 + "/command-queue/fullscan" } $AllResp = @() foreach ($id in $allComputers.uniqueId) { # URI query strings $QueryStrings = @{ computer_ids = $id } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } # Send command to each computers in the group $resp = Invoke-ABRestMethod -params $params $AllResp += $resp } # return the response return $AllResp } } } #EndRegion '.\Public\Start-SEPScan.ps1' 221 #Region '.\Public\Update-SEPClientDefinitions.ps1' 0 function Update-SEPClientDefinitions { <# .SYNOPSIS Sends a command from SEPM to SEP endpoints to update content .DESCRIPTION Sends a command from SEPM to SEP endpoints to update content .PARAMETER ComputerName The name of the computer to send the command to cannot be used with GroupName .PARAMETER GroupName The name of the group to send the command to cannot be used with ComputerName .PARAMETER IncludeSubGroups Specifies whether to include subgroups when querying by group name .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE Update-SEPClientDefinitions -ComputerName "Computer1" Sends a command to update content to Computer1 .EXAMPLE "Computer1", "Computer2" | Update-SEPClientDefinitions Sends a command to update content to Computer1 and Computer2 .EXAMPLE Update-SEPClientDefinitions -GroupName "My Company\EMEA\Workstations" Sends a command to update content to all computers in "My Company\EMEA\Workstations" .EXAMPLE Update-SEPClientDefinitions -GroupName "My Company\EMEA\Workstations" -IncludeSubGroups Sends a command to update content to all computers in "My Company\EMEA\Workstations" and all subgroups #> [CmdletBinding()] param ( # ComputerName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ComputerName' )] [Alias("Hostname", "DeviceName", "Device", "Computer")] [String] $ComputerName, # group name [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GroupName' )] [Alias("Group")] [String] $GroupName, # switch parameter to include subgroups [Parameter( ParameterSetName = 'GroupName' )] [switch] $IncludeSubGroups, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { if ($ComputerName) { # Get computer ID(s) from computer name(s) $ComputerIDList = @() foreach ($C in $ComputerName) { $ComputerID = Get-SEPComputers -ComputerName $C | Select-Object -ExpandProperty uniqueId $ComputerIDList += $ComputerID } $URI = $script:BaseURLv1 + "/command-queue/updatecontent" # URI query strings $QueryStrings = @{ computer_ids = $ComputerIDList } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params return $resp } # If by groupname elseif ($GroupName) { ####################################### # 1. finds all computers in the group # ####################################### $allComputers = @() $URI = $script:BaseURLv1 + "/computers" # URI query strings $QueryStrings = @{ sort = "COMPUTER_NAME" pageIndex = 1 pageSize = 100 computerName = $ComputerName # empty string value to ensure the URI is constructed correctly & query all computers } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # Get computer list do { try { # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Process the response $allComputers += $resp.content # Increment the page index & update URI $QueryStrings.pageIndex++ $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings } catch { Write-Warning -Message "Error: $_" } } until ($resp.lastPage -eq $true) # filter list by group name # if IncludeSubGroups is specified, then get all computers from subgroups if ($IncludeSubGroups) { # get all subgroups $allComputers = $allComputers | Where-Object { $_.group.name -like "$GroupName*" } } else { $allComputers = $allComputers | Where-Object { $_.group.name -eq $GroupName } } ################################################# # 2. send command to all computers in the group # ################################################# $URI = $script:BaseURLv1 + "/command-queue/updatecontent" $AllResp = @() # Send command to each computers in the group individually foreach ($id in $allComputers.uniqueId) { # URI query strings $QueryStrings = @{ computer_ids = $id } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # prepare the parameters $params = @{ Method = 'POST' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params $AllResp += $resp } # return the response return $AllResp } } } #EndRegion '.\Public\Update-SEPClientDefinitions.ps1' 194 #Region '.\Public\Update-SEPMExceptionPolicy.ps1' 0 function Update-SEPMExceptionPolicy { <# .SYNOPSIS Update a Symantec Endpoint Protection Manager Exception Policy .DESCRIPTION Update a Symantec Endpoint Protection Manager Exception Policy .PARAMETER PolicyName The name of the policy to update .PARAMETER Description The description of the policy .PARAMETER EnablePolicy Enable the policy .PARAMETER DisablePolicy Disable the policy .PARAMETER WindowsFileException Add a Windows File Exception to the policy .PARAMETER WindowsFolderException Add a Windows Folder Exception to the policy .PARAMETER Sonar Add the SONAR type of scan to the exception .PARAMETER DeleteException Delete the exception .PARAMETER RulestateEnabled Enable the rule .PARAMETER RulestateDisabled Disable the rule .PARAMETER RulestateSource Source of the rule Default is the module name : PSSymantecSEPM .PARAMETER SecurityRiskCategory The type of security risk scan to add to the exception Valid values are : AllScans Auto-Protect ScheduledAndOndemand .PARAMETER PathVariable The path variable to use for the exception Valid values are : [NONE] [COMMON_APPDATA] [COMMON_DESKTOPDIRECTORY] [COMMON_DOCUMENTS] [COMMON_PROGRAMS] [COMMON_STARTUP] [PROGRAM_FILES] [PROGRAM_FILES_COMMON] [SYSTEM] [SYSTEM_DRIVE] [USER_PROFILE] [WINDOWS] .PARAMETER Path The path to add to the exception .PARAMETER ApplicationControl Add the Application Control type of scan to the exception .PARAMETER AllScans Add all types of scans to the exception Based on the exception type .PARAMETER ExcludeChildProcesses Exclude child processes from the exception Specific to Application Control type of scan Requires ApplicationControl to be explicitly set to true .PARAMETER Recursive Add the recursive option to the exception .EXAMPLE $params = @{ PolicyName = "Workstations Exception Policy" WindowsFileException = $true AllScans = $true PathVariable = "[COMMON_DESKTOPDIRECTORY]" Path = "InternalApplication.exe" } Update-SEPMExceptionPolicy @params (Get-SEPMExceptionPolicy -PolicyName "Workstations Exception Policy").configuration.directories | Where-Object { $_.directory -match "InternalApplication.exe" } Using splatting, excludes the InternalApplication.exe file located in the Desktop directory from all types of scans Get-SEPMExceptionPolicy command verifies that the exception has been added to the policy .EXAMPLE Update-SEPMExceptionPolicy -PolicyName "Workstations Exception Policy" -Description "Default Workstations policy" -WindowsFileException -AllScans -PathVariable "[COMMON_DESKTOPDIRECTORY]" -Path "InternalApplication.exe" Same example without splatting, excludes the InternalApplication.exe file located in the Desktop directory from all types of scans .EXAMPLE $params = @{ PolicyName = "Workstations Exception Policy" WindowsFileException = $true Sonar = $true Path = "C:\MyCorp\InternalApplication.exe" } Update-SEPMExceptionPolicy @params Using splatting, excludes the InternalApplication.exe file located in the C:\MyCorp directory from the SONAR type of scan .EXAMPLE $params = @{ PolicyName = "Workstations Exception Policy" Path = "C:\MyCorp\InternalApplication.exe" DeleteException = $true } Update-SEPMExceptionPolicy @params Using splatting, deletes the exception for the InternalApplication.exe file located in the C:\MyCorp directory #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Description [Parameter()] # [Alias('PolicyDescription')] [String] $PolicyDescription, # Enabled Policy [Parameter()] [switch] $EnablePolicy, # Disable Policy [Parameter()] [switch] $DisablePolicy, # WindowsFileException [Parameter(ParameterSetName = 'WindowsFileException')] [switch] $WindowsFileException, # WindowsFolderException [Parameter(ParameterSetName = 'WindowsFolderException')] [switch] $WindowsFolderException, # Sonar [Parameter(ParameterSetName = 'WindowsFileException')] [switch] $Sonar, # deleted [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [switch] $DeleteException, # Looks like this is not used in SEPM # RulestateEnabled [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [Alias('EnableRule')] [switch]$RulestateEnabled, # RulestateDisabled [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [Alias('DisableRuleState')] [switch]$RulestateDisabled, [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [string] $RulestateSource = "PSSymantecSEPM", # Scancategory - requires securityrisk to be set to true [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [ValidateSet( 'AllScans', 'Auto-Protect', 'ScheduledAndOndemand' )] [string] $SecurityRiskCategory, # Pathvariable [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [ValidateSet( '[NONE]', '[COMMON_APPDATA]', '[COMMON_DESKTOPDIRECTORY]', '[COMMON_DOCUMENTS]', '[COMMON_PROGRAMS]', '[COMMON_STARTUP]', '[PROGRAM_FILES]', '[PROGRAM_FILES_COMMON]', '[SYSTEM]', '[SYSTEM_DRIVE]', '[USER_PROFILE]', '[WINDOWS]' )] [string] $PathVariable = "[NONE]", # Path [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [string] $Path, # Applicationcontrol [Parameter(ParameterSetName = 'WindowsFileException')] [switch] $ApplicationControl, # AllScans [Parameter(ParameterSetName = 'WindowsFileException')] [Parameter(ParameterSetName = 'WindowsFolderException')] [switch] $AllScans, # Recursive - requires applicationcontrol to be set to true [Parameter(ParameterSetName = 'WindowsFileException')] [switch] $ExcludeChildProcesses, # Recursive [Parameter(ParameterSetName = 'WindowsFolderException')] [switch] $Recursive, # ScanType [Parameter(ParameterSetName = 'WindowsFolderException')] [ValidateSet( 'SecurityRisk', 'SONAR', 'ApplicationControl', 'All' )] [string] $ScanType ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv2 + "/policies/exceptions" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } $policies = Get-SEPMPoliciesSummary } process { # Get Policy ID from policy name $PolicyID = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty id # Update URI with Policy ID $URI = $URI + "/" + $PolicyID # Get the skeleton of the body structure to update the exception policy $ObjBody = [SEPMPolicyExceptionsStructure]::new() # Update the body structure with the mandatory parameters $ObjBody.name = $PolicyName # Init $ExceptionParams = @{} # Common parameters # Adding default values if not explicitely provided provided # As $PSBoundParameters.Keys doesn't contain default parameters values if ($pathvariable -eq "[NONE]") { $ExceptionParams.pathvariable = "[NONE]" } if ($RulestateSource -eq "PSSymantecSEPM") { $ExceptionParams.RulestateSource = "PSSymantecSEPM" } # Exception types are split in groups via switches # -WindowsFileException / -WindowsFolderException / etc... # Each switch contains the parameters specific to the exception type # The parameters are parsed and added to the $ExceptionParams hashtable # WindowsFileException if ($WindowsFileException) { # Parse the parameters provided switch ($PSBoundParameters.Keys) { "Sonar" { $ExceptionParams.sonar = $true } "DeleteException" { $ExceptionParams.deleted = $true } # Looks like this is not used in SEPM # TODO verify this RulestateEnabled / RulestateDisabled "RulestateEnabled" { $ExceptionParams.RulestateEnabled = $true } "RulestateDisabled" { $ExceptionParams.RulestateEnabled = $false } "RulestateSource" { $ExceptionParams.RulestateSource = $RulestateSource } "SecurityRiskCategory" { $ExceptionParams.securityrisk = $true $ExceptionParams.scancategory = $SecurityRiskCategory } "PathVariable" { $ExceptionParams.pathvariable = $pathvariable } "Path" { $ExceptionParams.path = $path } "ApplicationControl" { $ExceptionParams.applicationcontrol = $true } "ExcludeChildProcesses" { $ExceptionParams.applicationcontrol = $true $ExceptionParams.recursive = $true } "AllScans" { $ExceptionParams.securityrisk = $true $ExceptionParams.sonar = $true $ExceptionParams.applicationcontrol = $true } } # Create the file exception object with CreateFilesHashTable # Method parameters have to be in the same order as in the method definition $FilesHashTable = $ObjBody.CreateFilesHashTable( $ExceptionParams.sonar, $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.scancategory, $ExceptionParams.pathvariable, $ExceptionParams.path, $ExceptionParams.applicationcontrol, $ExceptionParams.securityrisk, $ExceptionParams.recursive ) # Add the file exception parameters to the body structure $ObjBody.AddConfigurationFilesExceptions($FilesHashTable) } # WindowsFolderException if ($WindowsFolderException) { switch ($PSBoundParameters.Keys) { "DeleteException" { $ExceptionParams.deleted = $true } "RulestateEnabled" { $ExceptionParams.RulestateEnabled = $true } "RulestateSource" { $ExceptionParams.RulestateSource = $RulestateSource } "SecurityRiskCategory" { # SecurityRiskCategory can only be used if the ScanType parameter is 'SecurityRisk' or 'All' if ($ScanType -eq 'SecurityRisk' -or $ScanType -eq 'All') { $ExceptionParams.scancategory = $SecurityRiskCategory } else { throw "The SecurityRiskCategory parameter can only be used if the ScanType parameter is 'SecurityRisk' or 'All'" } } "ScanType" { $ExceptionParams.scantype = $ScanType } "PathVariable" { $ExceptionParams.pathvariable = $pathvariable } "Path" { $ExceptionParams.directory = $path } "Recursive" { $ExceptionParams.recursive = $true } "AllScans" { $ExceptionParams.scancategory = "AllScans" $ExceptionParams.scantype = "All" } } # Create folder the exception object with CreateDirectoryHashtable # Method parameters have to be in the same order as in the method definition $DirectoryHashTable = $ObjBody.CreateDirectoryHashtable( $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.scancategory, $ExceptionParams.scantype, $ExceptionParams.pathvariable, $ExceptionParams.directory, $ExceptionParams.recursive ) # Add the folder exception parameters to the body structure $ObjBody.AddConfigurationDirectoriesExceptions($DirectoryHashTable) } # Verify if updates to the policy are needed switch ($psboundparameters.Keys) { "EnablePolicy" { $ObjBody.enabled = $true } "DisablePolicy" { $ObjBody.enabled = $false } "Description" { $ObjBody.desc = $PolicyDescription } } # Optimize the body structure (remove empty properties) $ObjBody = Optimize-ExceptionPolicyStructure -obj $ObjBody # TODO For testing only - remove this # $ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI headers = $headers contenttype = 'application/json' Body = $ObjBody | ConvertTo-Json -Depth 100 } try { $resp = Invoke-ABRestMethod -params $params } catch { Write-Warning -Message "Error: $_" } return $resp } } #EndRegion '.\Public\Update-SEPMExceptionPolicy.ps1' 447 #Region '.\Public\Update-SEPMFileFingerprintList.ps1' 0 function Update-SEPMFileFingerprintList { <# .SYNOPSIS Updates an existing fingerprint list .DESCRIPTION Updates an existing fingerprint list When updating the list, overwrite the entire list with the new list .PARAMETER FingerprintListName The name of the file fingerprint list Cannot be used with FingerprintListID parameter .PARAMETER FingerprintListID The ID of the file fingerprint list Cannot be used with FingerprintListName parameter .PARAMETER name The name of the fingerprint list that will appear on SEPM .PARAMETER domainId The domain id of the domain to add the fingerprint list to Only takes the domain id. Can be found using Get-SEPMDomain .PARAMETER HashType The type of hash to use for the fingerprint list Valid values are SHA256 and MD5 .PARAMETER description The description of the fingerprint list .PARAMETER hashlist The hash list to add to the fingerprint list Can be generated using Get-FileHash or takes a string array of hashes .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE $domainId = Get-SEPMDomain | Where-Object { $_.name -eq "Default" } | Select-Object -ExpandProperty id $hashlist = ls -file C:\Users\$env:USERNAME\Downloads\*.exe | Get-FileHash -algorithm SHA256 Update-SEPMFileFingerprintList -FingerprintListName "Downloaded .exe files" -name "Workstations downloaded files" -domainId $DomainId -HashType "SHA256" -description "Contains the list of .exe files downloaded with a specific workstations" -hashlist $hashlist.hash Gets the domain id for the default domain Create a hash list of all the files in the downloads folder of the currently logged in user Updates the fingerprint list "Downloaded .exe files" with the new hash list The fingerprint list needs to be existing before it can be updated #> [CmdletBinding( DefaultParameterSetName = 'Name' )] param ( [Parameter()] [string]$name, [Parameter()] [string]$domainId, [Parameter()] [ValidateSet('SHA256', 'MD5')] [string]$HashType, [Parameter()] [string]$description, [Parameter( ValueFromPipeline = $true )] $hashlist, [Parameter( ParameterSetName = 'Name' )] [string] $FingerprintListName, [Parameter( ParameterSetName = 'ID' )] [string] $FingerprintListID, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { # Get the FingerprintListID if the FingerprintListName is provided if ($FingerprintListName) { $URI = $script:BaseURLv1 + "/policy-objects/fingerprints" $FingerprintListID = Get-SEPMFileFingerprintList -FingerprintListName $FingerprintListName | Select-Object -ExpandProperty id } $URI = $script:BaseURLv1 + "/policy-objects/fingerprints/$FingerprintListID" # Construct the body & required fields $body = @{ name = $name domainId = $domainId hashType = $HashType description = $description data = $hashlist } $params = @{ Method = 'POST' Uri = $URI headers = $headers Body = $body | ConvertTo-Json ContentType = 'application/json' } $resp = Invoke-ABRestMethod -params $params # return the response return $resp } } #EndRegion '.\Public\Update-SEPMFileFingerprintList.ps1' 126 #Region '.\Public\zz_Initialize-SepmConfiguration.ps1' 0 #################################### # Init script for the whole module # #################################### ## This is the initialization script for the module. It is invoked at the end of the module's ## prefix file as "zz_" to load this module at last. This is done to ensure that all other functions are first loaded ## This function should be private but will stay Public for the moment as it needs to be the last function to be loaded in the module ## TODO make this function private # The credentials used to authenticate to the SEPM server. [PSCredential] $script:Credential = $null [PSCustomObject] $script:accessToken = $null # SEPM Server configuration [string] $script:ServerAddress = $null [string] $script:BaseURLv1 = $null [string] $script:BaseURLv2 = $null [bool] $script:SkipCert = $false # Needed for self-signed certificates # The location of the file that we'll store any settings that can/should roam with the user. [string] $script:configurationFilePath = [System.IO.Path]::Combine( [System.Environment]::GetFolderPath('ApplicationData'), 'PSSymantecSEPM', 'config.json') # The location of the file that we'll store credentials [string] $script:credentialsFilePath = [System.IO.Path]::Combine( [System.Environment]::GetFolderPath('ApplicationData'), 'PSSymantecSEPM', 'creds.xml') # The location of the file that we'll store the Access Token SecureString # which cannot/should not roam with the user. [string] $script:accessTokenFilePath = [System.IO.Path]::Combine( [System.Environment]::GetFolderPath('LocalApplicationData'), 'PSSymantecSEPM', 'accessToken.xml') # The session-cached copy of the module's configuration properties [PSCustomObject] $script:configuration = $null function Initialize-SepmConfiguration { <# .SYNOPSIS Populates the configuration of the module for this session, loading in any values that may have been saved to disk. .DESCRIPTION Populates the configuration of the module for this session, loading in any values that may have been saved to disk. .NOTES Internal helper method. This is actually invoked at the END of this file. #> [CmdletBinding()] param() # Load in the configuration from disk $script:configuration = Import-SepmConfiguration -Path $script:configurationFilePath if ($script:configuration.ServerAddress -and $script:configuration.port) { $script:BaseURLv1 = "https://" + $script:configuration.ServerAddress + ":" + $script:configuration.port + "/sepm/api/v1" $script:BaseURLv2 = "https://" + $script:configuration.ServerAddress + ":" + $script:configuration.port + "/sepm/api/v2" } else { # If no configuration was loaded from disk, or no server address was specified, reset the configuration Reset-SEPMConfiguration } # Load in the credentials from disk if (Test-Path $script:credentialsFilePath) { try { $script:Credential = Import-Clixml -Path $script:credentialsFilePath } catch { Write-Verbose "No credentials found from '$script:credentialsFilePath': $_" } } # Load in the access token from disk if (Test-Path $script:accessTokenFilePath) { try { $script:accessToken = Import-Clixml -Path $script:accessTokenFilePath } catch { Write-Verbose "Failed to import access token from '$script:accessTokenFilePath': $_" } } } # Invoke the initialization method to populate the configuration Initialize-SepmConfiguration #EndRegion '.\Public\zz_Initialize-SepmConfiguration.ps1' 90 |