PSSymantecSEPM.psm1
#Region '.\Classes\Exceptions-Policy.ps1' -1 <# 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]::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 extensions hashtable [hashtable] CreateExtensionListHashtable( [Nullable[bool]] $deleted = $null, [Nullable[bool]] $rulestate_enabled = $null, [string] $rulestate_source = "PSSymantecSEPM", # $extensions is a PSOBject list [string] $scancategory = "", [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 } if (![string]::IsNullOrEmpty($scancategory)) { $HashTable['scancategory'] = $scancategory } # 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 extensions [void] AddExtensionsList( [hashtable] $extensions # Use CreateExtensionListHashtable method ) { $this.configuration.extension_list = $extensions } # 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] CreateLinuxDirectoryHashtable( [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] AddLinuxDirectory( [hashtable] $linux_directories # Use CreateLinuxDirectoryHashtable 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 [string] $scancategory = "", [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 } if (![string]::IsNullOrEmpty($scancategory)) { $HashTable['scancategory'] = $scancategory } # 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) $this.configuration.linux.extension_list = $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' 1259 #Region '.\Private\Build-SEPMQueryURI.ps1' -1 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' -1 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\Initialize-PolicyExceptionStructure.ps1' -1 function Initialize-PolicyExceptionStructure { <# .SYNOPSIS Initializes the skeleton of the body structure to update the exception policy. .DESCRIPTION Initializes the skeleton of the body structure to update the exception policy. Returns the body structure and its associated policy ID. .PARAMETER PolicyName The name of the policy to update. .NOTES This is an internal helper function. .EXAMPLE PS C:\> Initialize-PolicyExceptionStructure -PolicyName "Default" #> [CmdletBinding()] param ( # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName ) process { # Get all policies $policies = Get-SEPMPoliciesSummary # Get Policy ID from policy name $PolicyID = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty id # Instantiates 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 $return = [PSCustomObject]@{ ObjBody = $ObjBody PolicyID = $PolicyID } # Return ObjBody & policy ID return $return } } #EndRegion '.\Private\Initialize-PolicyExceptionStructure.ps1' 52 #Region '.\Private\Invoke-ABRestMethod.ps1' -1 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' -1 function Optimize-ExceptionPolicyStructure { <# .SYNOPSIS This function is used to optimize the structure of the exception policy object. .DESCRIPTION This function is used to optimize the structure of the exception policy object. It will remove empty properties and nested objects that are empty. .PARAMETER obj The object to optimize .EXAMPLE Optimize-ExceptionPolicyStructure -obj $exceptionPolicy .OUTPUTS System.Management.Automation.PSCustomObject #> [CmdletBinding()] param ( [Parameter( Mandatory = $true, ValueFromPipeline = $true )] [object] $obj ) process { # convert the object to a PSCustomObject (trick to convert custom class to PSCustomObject) # There might be cleaner ways to do this $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' 110 #Region '.\Private\Read-SepmConfiguration.ps1' -1 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' -1 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' -1 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' -1 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' -1 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' -1 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' -1 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' -1 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 #-AllowUnencryptedAuthentication # If no error, then the certificate is valid $script:SkipCert = $false } catch { if ($_.Exception.HttpRequestError -eq "SecureConnectionError") { # Get SEPM server name from URI $ServerName = (New-Object System.Uri($URI)).Host # Get the error message $message = "SSL Certificate test failed. The certificate for $ServerName is likely self-signed." Write-Warning -Message $message # Prompt for user input to continue # TODO add a remove option for user interaction with -skipcertificationcheck if ($PSVersionTable.PSVersion.Major -lt 6) { Skip-Cert } $script:SkipCert = $true } else { throw $_ } } } #EndRegion '.\Private\Test-SEPMCertificate.ps1' 57 #Region '.\Public\Add-SEPMFileFingerprintList.ps1' -1 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 .exe 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\Add-SEPMMacFileException.ps1' -1 function Add-SEPMMacFileException { <# TODO update help .SYNOPSIS Add a Mac File Exception to a Symantec Endpoint Protection Manager Policy .DESCRIPTION Add a Windows File Exception to a Symantec Endpoint Protection Manager Policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Path Path to add to the exception list .PARAMETER PathVariable Path variable to use for the path .PARAMETER Sonar Add the exception to the SONAR exclusions .PARAMETER SecurityRiskCategory Add the exception to the Security Risk exclusions Takes the following values: AllScans AutoProtect ScheduledAndOndemand .PARAMETER SkipCertificateCheck Skip the certificate check when connecting to the SEPM .EXAMPLE Add-SEPMMacFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -Sonar Exclude the file C:\Temp\file1.exe from SONAR scans in the policy Default .EXAMPLE Add-SEPMMacFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -SecurityRiskCategory "AllScans" Exclude the file C:\Temp\file1.exe from all Security Risk scans in the policy Default .EXAMPLE Add-SEPMMacFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -ApplicationControl Exclude the file C:\Temp\file1.exe from Application Control scans in the policy Default .EXAMPLE Add-SEPMMacFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -ApplicationControl -ExcludeChildProcesses Exclude the file C:\Temp\file1.exe from Application Control scans in the policy Default and exclude child processes .EXAMPLE Add-SEPMMacFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -AllScans Exclude the file C:\Temp\file1.exe from all scan types in the policy Default (SONAR / AutoProtect / Scheduled scans / Application Control) #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Scancategory - requires securityrisk to be set to true [ValidateSet( 'AllScans', 'AutoProtect', 'ScheduledAndOndemand' )] [ValidateScript({ if ($ScanType -ne "SecurityRisk") { throw "The -SecurityRiskCategory parameter requires the -ScanType parameter to be set to 'SecurityRisk'." } return $true })] [string] $SecurityRiskCategory, # Pathvariable [ValidateSet( '[NONE]', '[HOME]', '[APPLICATION]', '[LIBRARY ]' )] [string] $PathVariable = "[NONE]", # Path [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern("^/([^/ ]+(/|$))+[^/ ]+\.[^/ ]+$")] [string] $Path, # ScanType [ValidateSet( 'SecurityRisk', 'SONAR', 'All' )] [string] $ScanType = 'All' ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Init & populate the mandatory parameters $ExceptionParams = @{} $ExceptionParams.path = $path $ExceptionParams.pathvariable = $PathVariable $ExceptionParams.RulestateSource = $script:ModuleName # API does not support seem to support scan type for Mac exceptions # TODO: Test for scan types and report potential issue to Symantec # # Parse the optional parameters # switch ($PSBoundParameters.Keys) { # "SecurityRiskCategory" { # $ExceptionParams.securityrisk = $true # $ExceptionParams.scancategory = $SecurityRiskCategory # } # } # Create the file exception object with CreateFilesHashTable # Method parameters have to be in the same order as in the method definition $DirectoryHashTable = $Policy.ObjBody.CreateMacFilesHashtable( $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.pathvariable, $ExceptionParams.path ) # Add the file exception parameters to the body structure $Policy.ObjBody.AddMacFiles($DirectoryHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Add-SEPMMacFileException.ps1' 174 #Region '.\Public\Add-SEPMWindowsExtensionException.ps1' -1 function Add-SEPMWindowsExtensionException { <# .SYNOPSIS Add a Windows extension exception to a SEPM policy .DESCRIPTION Add a Windows extension exception to a SEPM policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Extension Extension to add to the exception list Accepts multiple values .PARAMETER ScanType Type of scan to apply the exception to Valid values are: AllScans AutoProtect ScheduledAndOndemand .EXAMPLE Add-SEPMWindowsExtensionException -PolicyName "Workstations Default Exception Policy" -Extension tmp,tmp2 Add 2 Windows extensions exception, tmp and tmp2 to the "Workstations Default Exception Policy" policy .EXAMPLE Add-SEPMWindowsExtensionException -PolicyName "Workstations Default Exception Policy" -Extension tmp,tmp2 -ScanType AutoProtect Add 2 Windows extensions exception, tmp and tmp2 to the "Workstations Default Exception Policy" policy, for AutoProtect scans #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Extension [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [string[]] $Extension, # Security Risk type [ValidateSet( 'AllScans', 'AutoProtect', 'ScheduledAndOndemand' )] [string] $ScanType = 'AllScans' ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Gather current extension exception list $PolicyExtList = (Get-SEPMExceptionPolicy -PolicyName $PolicyName).configuration.extension_list.extensions # Init & populate the mandatory parameters $ExceptionParams = @{} $ExtensionList = @() $ExceptionParams.RulestateSource = $script:ModuleName $ExceptionParams.scancategory = $ScanType # Add extension to the current list (as the API update call overwrites the list) $ExtensionList += $Extension $ExtensionList += $PolicyExtList $ExtensionList = $ExtensionList | Sort-Object | Get-Unique # Avoid duplicates $ExceptionParams.extensions += $ExtensionList # Create the file exception object with CreateExtensionListHashtable # Method parameters have to be in the same order as in the method definition $ExtensionHashTable = $Policy.ObjBody.CreateExtensionListHashtable( $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.scancategory, $ExceptionParams.extensions ) # Add the file exception parameters to the body structure $Policy.ObjBody.AddExtensionsList($ExtensionHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this # $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Add-SEPMWindowsExtensionException.ps1' 132 #Region '.\Public\Add-SEPMWindowsFileException.ps1' -1 function Add-SEPMWindowsFileException { <# .SYNOPSIS Add a Windows File Exception to a Symantec Endpoint Protection Manager Policy .DESCRIPTION Add a Windows File Exception to a Symantec Endpoint Protection Manager Policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Path Path to add to the exception list .PARAMETER PathVariable Path variable to use for the path .PARAMETER Sonar Add the exception to the SONAR exclusions .PARAMETER SecurityRiskCategory Add the exception to the Security Risk exclusions Takes the following values: AllScans AutoProtect ScheduledAndOndemand .PARAMETER ApplicationControl Add the exception to the Application Control exclusions .PARAMETER ExcludeChildProcesses Exclude child processes from the Application Control exclusions Requires ApplicationControl to be set to true .PARAMETER SkipCertificateCheck Skip the certificate check when connecting to the SEPM .PARAMETER AllScans Add the exception to all scan types Equivalent to setting Sonar, SecurityRiskCategory and ApplicationControl to true If no scan type is provided, default to AllScans .EXAMPLE Add-SEPMWindowsFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -Sonar Exclude the file C:\Temp\file1.exe from SONAR scans in the policy Default .EXAMPLE Add-SEPMWindowsFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -SecurityRiskCategory "AllScans" Exclude the file C:\Temp\file1.exe from all Security Risk scans in the policy Default .EXAMPLE Add-SEPMWindowsFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -ApplicationControl Exclude the file C:\Temp\file1.exe from Application Control scans in the policy Default .EXAMPLE Add-SEPMWindowsFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -ApplicationControl -ExcludeChildProcesses Exclude the file C:\Temp\file1.exe from Application Control scans in the policy Default and exclude child processes .EXAMPLE Add-SEPMWindowsFileException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -AllScans Exclude the file C:\Temp\file1.exe from all scan types in the policy Default (SONAR / AutoProtect / Scheduled scans / Application Control) #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Sonar [Parameter(ParameterSetName = 'WindowsFileException')] [switch] $Sonar, # Scancategory - requires securityrisk to be set to true [Parameter(ParameterSetName = 'WindowsFileException')] [ValidateSet( 'AllScans', 'AutoProtect', 'ScheduledAndOndemand' )] [string] $SecurityRiskCategory, # Pathvariable [Parameter(ParameterSetName = 'WindowsFileException')] [ValidateSet( '[NONE]', '[COMMON_APPDATA]', '[COMMON_DESKTOPDIRECTORY]', '[COMMON_DOCUMENTS]', '[COMMON_PROGRAMS]', '[COMMON_STARTUP]', '[PROGRAM_FILES]', '[PROGRAM_FILES_COMMON]', '[SYSTEM]', '[SYSTEM_DRIVE]', '[USER_PROFILE]', '[WINDOWS]' )] [Alias('WindowsPathVariable')] [string] $PathVariable = "[NONE]", # Path [Parameter(ParameterSetName = 'WindowsFileException', Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern("^[A-Za-z]:\\(?:[^\\/:*?""<>|\r\n]+\\)*[^\\/:*?""<>|\r\n]+\.[^\\/:*?""<>|\r\n]+$")] [Alias('WindowsPath')] [string] $Path, # Applicationcontrol [Parameter(ParameterSetName = 'WindowsFileException')] [switch] $ApplicationControl, # AllScans [Parameter(ParameterSetName = 'WindowsFileException')] [switch] $AllScans, # Recursive - requires applicationcontrol to be set to true [Parameter(ParameterSetName = 'WindowsFileException')] [ValidateScript({ if (-not $ApplicationControl) { throw "-ExcludeChildProcesses exception requires the -ApplicationControl switch to be set." } return $true })] [switch] $ExcludeChildProcesses ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Init & populate the mandatory parameters $ExceptionParams = @{} $ExceptionParams.path = $path $ExceptionParams.pathvariable = $PathVariable $ExceptionParams.RulestateSource = $script:ModuleName # Parse the optional parameters switch ($PSBoundParameters.Keys) { "Sonar" { $ExceptionParams.sonar = $true } "DeleteException" { $ExceptionParams.deleted = $true } "SecurityRiskCategory" { $ExceptionParams.securityrisk = $true $ExceptionParams.scancategory = $SecurityRiskCategory } "ApplicationControl" { $ExceptionParams.applicationcontrol = $true } "ExcludeChildProcesses" { $ExceptionParams.applicationcontrol = $true $ExceptionParams.recursive = $true } "AllScans" { $ExceptionParams.securityrisk = $true $ExceptionParams.sonar = $true $ExceptionParams.applicationcontrol = $true $ExceptionParams.scancategory = "AllScans" } } # If no scan type is provided, default to AllScans if (-not $ExceptionParams.securityrisk -and -not $ExceptionParams.sonar -and -not $ExceptionParams.applicationcontrol) { $ExceptionParams.securityrisk = $true $ExceptionParams.sonar = $true $ExceptionParams.applicationcontrol = $true $ExceptionParams.scancategory = "AllScans" } # Create the file exception object with CreateFilesHashTable # Method parameters have to be in the same order as in the method definition $FilesHashTable = $Policy.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 $Policy.ObjBody.AddConfigurationFilesExceptions($FilesHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this # $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Add-SEPMWindowsFileException.ps1' 235 #Region '.\Public\Add-SEPMWindowsFolderException.ps1' -1 function Add-SEPMWindowsFolderException { <# .SYNOPSIS Add a Windows Folder Exception to a Symantec Endpoint Protection Manager Policy .DESCRIPTION Add a Windows Folder Exception to a Symantec Endpoint Protection Manager Policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Path Path to add to the exception list Verify that the path is valid before adding it to the exception list .PARAMETER PathVariable Path variable to use for the path .PARAMETER Sonar Add the exception to the SONAR exclusions .PARAMETER SecurityRiskCategory Add the exception to the Security Risk exclusions Takes the following values: AllScans AutoProtect ScheduledAndOndemand .PARAMETER ApplicationControl Add the exception to the Application Control exclusions .PARAMETER ExcludeChildProcesses Exclude child processes from the Application Control exclusions Requires ApplicationControl to be set to true .PARAMETER SkipCertificateCheck Skip the certificate check when connecting to the SEPM .PARAMETER AllScans Add the exception to all scan types Equivalent to setting Sonar, SecurityRiskCategory and ApplicationControl to true If no scan type is provided, default to AllScans .EXAMPLE Add-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp" -Sonar Exclude the folder C:\Temp from SONAR scans in the policy Default .EXAMPLE Add-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp" -SecurityRiskCategory "AllScans" Exclude the folder C:\Temp from all Security Risk scans in the policy Default .EXAMPLE Add-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp" -ApplicationControl Exclude the folder C:\Temp from Application Control scans in the policy Default .EXAMPLE Add-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp" -ApplicationControl -ExcludeChildProcesses Exclude the folder C:\Temp from Application Control scans in the policy Default and exclude child processes .EXAMPLE Add-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp" -AllScans Exclude the folder C:\Temp from all scan types in the policy Default (SONAR / AutoProtect / Scheduled scans / Application Control) #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Scancategory - requires securityrisk to be set to true [Parameter(ParameterSetName = 'WindowsFolderException')] [ValidateSet( 'AllScans', 'AutoProtect', 'ScheduledAndOndemand' )] [ValidateScript({ if ($ScanType -ne "SecurityRisk") { throw "The -SecurityRiskCategory parameter requires the -ScanType parameter to be set to 'SecurityRisk'." } return $true })] [string] $SecurityRiskCategory, # Pathvariable [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]' )] [Alias('WindowsPathVariable')] [string] $PathVariable = "[NONE]", # Path [Parameter(ParameterSetName = 'WindowsFolderException', Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern("^[A-Za-z]:\\(?:[^\\/:*?""<>|\r\n]+\\)*")] [Alias('WindowsPath')] [string] $Path, # Recursive - requires applicationcontrol to be set to true [Parameter(ParameterSetName = 'WindowsFolderException')] [switch] $IncludeSubFolders, # ScanType [Parameter(ParameterSetName = 'WindowsFolderException')] [ValidateSet( 'SecurityRisk', 'SONAR', 'ApplicationControl', 'All' )] [string] $ScanType = 'All' ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Init & populate the mandatory parameters $ExceptionParams = @{} $ExceptionParams.directory = $path $ExceptionParams.pathvariable = $PathVariable $ExceptionParams.RulestateSource = $script:ModuleName # Parse the optional parameters switch ($PSBoundParameters.Keys) { "SecurityRiskCategory" { $ExceptionParams.securityrisk = $true $ExceptionParams.scancategory = $SecurityRiskCategory } "IncludeSubFolders" { $ExceptionParams.recursive = $true } } # If no scan type is provided, default to AllScans if (-not $ExceptionParams.scantype) { $ExceptionParams.scantype = "All" } # Create the file exception object with CreateFilesHashTable # Method parameters have to be in the same order as in the method definition $DirectoryHashTable = $Policy.ObjBody.CreateDirectoryHashtable( $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.scancategory, $ExceptionParams.scantype, $ExceptionParams.pathvariable, $ExceptionParams.directory, $ExceptionParams.recursive ) # Add the file exception parameters to the body structure $Policy.ObjBody.AddConfigurationDirectoriesExceptions($DirectoryHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this # $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Add-SEPMWindowsFolderException.ps1' 211 #Region '.\Public\Add-SEPMWindowsTamperProtectionException.ps1' -1 function Add-SEPMWindowsTamperProtectionException { <# .SYNOPSIS Add aTamper Protection Exception to a Symantec Endpoint Protection Manager Policy .DESCRIPTION Add aTamper Protection Exception to a Symantec Endpoint Protection Manager Policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Path Path to add to the exception list .PARAMETER PathVariable Path variable to use for the path Following values are allowed: [NONE] [COMMON_APPDATA] [COMMON_DESKTOPDIRECTORY] [COMMON_DOCUMENTS] [COMMON_PROGRAMS] [COMMON_STARTUP] [PROGRAM_FILES] [PROGRAM_FILES_COMMON] [SYSTEM] [SYSTEM_DRIVE] [USER_PROFILE] [WINDOWS] .EXAMPLE Add-SEPMWindowsTamperProtectionException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" Exclude the file C:\Temp\file1.exe from Tamper Protection scans in the policy Default .EXAMPLE Add-SEPMWindowsTamperProtectionException -PolicyName "Workstations Default Exception Policy" -Path ".gitconfig -PathVariable [USER_PROFILE] Exclude the file .gitconfig located in the %USERPROFILE% from Tamper Protection scans in the policy Default #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Pathvariable [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(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern("^[A-Za-z]:\\(?:[^\\/:*?""<>|\r\n]+\\)*[^\\/:*?""<>|\r\n]+\.[^\\/:*?""<>|\r\n]+$")] [Alias('WindowsPath')] [string] $Path ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Init & populate the mandatory parameters $ExceptionParams = @{} $ExceptionParams.path = $path $ExceptionParams.pathvariable = $PathVariable $ExceptionParams.RulestateSource = $script:ModuleName # Create the Tamper Protection exception object with CreateTamperFilesHashtable # Method parameters have to be in the same order as in the method definition $TamperHashTable = $Policy.ObjBody.CreateTamperFilesHashtable( $ExceptionParams.sonar, $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.scancategory, $ExceptionParams.pathvariable, $ExceptionParams.path, $ExceptionParams.applicationcontrol, $ExceptionParams.securityrisk, $ExceptionParams.recursive ) # Add the tamper exception parameters to the body structure $Policy.ObjBody.AddTamperFiles($TamperHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this # $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Add-SEPMWindowsTamperProtectionException.ps1' 144 #Region '.\Public\Backup-SEPMAuthentication.ps1' -1 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' -1 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' -1 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' -1 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' -1 # 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\Export-SEPMExceptionPolicyToExcel.ps1' -1 function Export-SEPMExceptionPolicyToExcel { <# TODO update help .SYNOPSIS A short one-line action-based description, e.g. 'Tests if a function is valid' .DESCRIPTION A longer description of the function, its purpose, common use cases, etc. .NOTES Information or caveats about the function e.g. 'This function is not supported in Linux' .LINK Specify a URI to a help page, this will show when Get-Help -Online is used. .EXAMPLE Test-MyTestFunction -Verbose Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines #> param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Path [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateScript({ # Validate the path if (-Not (Split-Path $_ -Parent | Test-Path)) { throw "Directory of `$_` does not exist" } # Validate the file extension if (-Not ($_ -match '\.xlsx$')) { throw "File `$_` does not have the .xlsx extension" } return $true })] [string] $Path ) begin { } process { # Get Exception policy object $ExceptionPolicy = Get-SEPMExceptionPolicy -PolicyName $PolicyName -SkipCertificateCheck:$SkipCertificateCheck # Verify the PSObject typename is "SEPM.ExceptionPolicy" if ($ExceptionPolicy.PSObject.TypeNames[0] -ne "SEPM.ExceptionPolicy") { $message = "The policy name provided is not of type Exception Policy or does not exist - Please verify the policy name" Write-Error -Message $message throw $message } # Add the categories to the PSObject $ExceptionCategory = [PSCustomObject]@{ Files = $ExceptionPolicy.configuration.files Folders = $ExceptionPolicy.configuration.directories Certificates = $ExceptionPolicy.configuration.certificates Tamper_files = $ExceptionPolicy.configuration.tamper_files Webdomain = $ExceptionPolicy.configuration.webdomains Mac = $ExceptionPolicy.configuration.mac.files Linux = $ExceptionPolicy.configuration.linux.directories Linux_Extension = $ExceptionPolicy.configuration.linux.extension_list KnownRisks = $ExceptionPolicy.configuration.knownrisks } # Define the properties to export $Props = [PSCustomObject]@{ Files = @("scancategory", "pathvariable", "path", "SONAR", "applicationcontrol", "securityrisk", "recursive") Folders = @("scancategory", "scantype", "pathvariable", "directory", "recursive") Certificates = @("*") Tamper_files = @("pathvariable", "path") Webdomain = @("domain") Mac = @("pathvariable", "path") Linux = @("scancategory", "pathvariable", "directory", "recursive") Linux_Extension = @("*") KnownRisks = @("threat.id", "threat.name", "action") } # Define Excel export parameters $excel_params = @{ ClearSheet = $true BoldTopRow = $true AutoSize = $true FreezeTopRow = $true AutoFilter = $true } # Export the data to Excel foreach ($category in $ExceptionCategory.PSObject.Properties.Name) { # Only export the category if it has data if ($ExceptionCategory.$category) { # Special case for Extensions. Split in an array of objects for correct formating if ($category -eq "Linux_Extension") { $Extensions = @() foreach ($line in $ExceptionCategory.Linux_Extension.extensions) { $obj = New-Object -TypeName PSObject $obj | Add-Member -MemberType NoteProperty -Name Extensions -Value $line $Extensions += $obj } $Extensions | Select-Object -Property $Props.$category | Export-Excel -Path $Path -WorksheetName $category @excel_params continue } $ExceptionCategory.$category | ConvertTo-FlatObject | Select-Object -Property $Props.$category | Export-Excel -Path $Path -WorksheetName $category @excel_params } } } } #EndRegion '.\Public\Export-SEPMExceptionPolicyToExcel.ps1' 119 #Region '.\Public\Get-SEPClientDefVersions.ps1' -1 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. .EXAMPLE PS C:\PSSymantecSEPM> $definitionVersions = Get-SEPClientDefVersions PS C:\PSSymantecSEPM> $definitionVersions 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 PS C:\PSSymantecSEPM> ($definitionVersions | Where-Object version -eq "2023-09-03 rev. 002").GetComputerWithThisDefinition() computerName ipAddresses GroupName ------------ ----------- --------- Computer123 {10.0.70.126} My Company\_Americas\Workstations Computer124 {10.1.19.127} My Company\_EMEA\Workstations Computer125 {10.5.125.128} My Company\_APAC\Workstations Computer126 {10.9.38.110} My Company\_LATAM\Workstations Gets a list of clients grouped by content version Then gets the list of computers with a specified content version (2023-09-03 rev. 002), using the GetComputerWithThisDefinition() method. #> [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 # Add a PSTypeName to the object $resp.clientDefStatusList | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.clientDefStatusList") } return $resp.clientDefStatusList } } #EndRegion '.\Public\Get-SEPClientDefVersions.ps1' 90 #Region '.\Public\Get-SEPClientInfectedStatus.ps1' -1 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' -1 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 # Add a PSTypeName to the object $resp.clientCountStatsList | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.clientStatusList") } return $resp.clientCountStatsList } } #EndRegion '.\Public\Get-SEPClientStatus.ps1' 60 #Region '.\Public\Get-SEPClientVersion.ps1' -1 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 # Add a PSTypeName to the object $resp.clientVersionList | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.clientVersionList") } return $resp.clientVersionList } } #EndRegion '.\Public\Get-SEPClientVersion.ps1' 71 #Region '.\Public\Get-SEPComputers.ps1' -1 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 = @{ computerName = $ComputerName } # Construct the URI $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings # Invoke the request params $params = @{ Method = 'GET' Uri = $URI headers = $headers } $allResults = (Invoke-ABRestMethod -params $params).content # Filtering $allResults = $allResults | Where-Object { $_.computerName -like $ComputerName } } # 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 } } } # 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' 198 #Region '.\Public\Get-SEPFileDetails.ps1' -1 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' -1 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 # Add a PSTypeName to the object $resp | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.GUPList") } return $resp } } #EndRegion '.\Public\Get-SEPGUPList.ps1' 68 #Region '.\Public\Get-SEPMAccessToken.ps1' -1 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' -1 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 # Add a PSTypeName to the object $resp | ForEach-Object { $_.PSTypeNames.Insert(0, "SEP.adminList") } # Process the response if ([string]::IsNullOrEmpty($AdminName)) { return $resp } else { $resp = $resp | Where-Object { $_.loginName -eq $AdminName } return $resp } } } #EndRegion '.\Public\Get-SEPMAdmins.ps1' 89 #Region '.\Public\Get-SEPMCommandStatus.ps1' -1 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 { $allResults = @() $QueryStrings = @{} do { # prepare the parameters $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, "SEPM.CommandStatus") } return $allResults } } #EndRegion '.\Public\Get-SEPMCommandStatus.ps1' 107 #Region '.\Public\Get-SEPMDatabaseInfo.ps1' -1 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 # Add a PSTypeName to the object $resp.PSObject.TypeNames.Insert(0, 'SEPM.DatabaseInfo') return $resp } } #EndRegion '.\Public\Get-SEPMDatabaseInfo.ps1' 71 #Region '.\Public\Get-SEPMDomain.ps1' -1 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 # Add a PSTypeName to the object $resp.PSObject.TypeNames.Insert(0, 'SEPM.DomainInfo') return $resp } } #EndRegion '.\Public\Get-SEPMDomain.ps1' 64 #Region '.\Public\Get-SEPMEventInfo.ps1' -1 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 # Add a PSTypeName to the object $resp.criticalEventsInfoList | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'SEPM.EventInfo') } return $resp.criticalEventsInfoList } } #EndRegion '.\Public\Get-SEPMEventInfo.ps1' 70 #Region '.\Public\Get-SEPMExceptionPolicy.ps1' -1 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 List List a specific exception category Valid values are "files", "directories", "webdomains" # TODO : add all the other exception types in example .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 "Workstations Exception Policy .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMExceptionPolicy -PolicyName "AB - Testing - API" -List files | Format-Table SONAR rulestate.enabled rulestate.source scancategory pathvariable path applicationcontrol securityrisk recursive Platform ----- ----------------- ---------------- ------------ ------------ ---- ------------------ ------------ --------- -------- False True PSSymantecSEPM AutoProtect [NONE] C:\Temp\File5.exe False True False Windows True True PSSymantecSEPM AutoProtect [NONE] C:\Temp\File.exe True True False Windows True [NONE] /applications/test/SONAR Mac True [NONE] /Applications/test/TestFolder Mac Gets Exception details for the policy named "Workstations Exception Policy" and listing only the files exceptions .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMExceptionPolicy -PolicyName "AB - Testing - API" -List directories | Format-Table rulestate.enabled scancategory scantype pathvariable directory recursive Platform ----------------- ------------ -------- ------------ --------- --------- -------- True AutoProtect SecurityRisk [NONE] C:\Temp\SecurityRiskAP\ False Windows True AllScans SONAR [NONE] C:\Temp\SonarWithSubfolders\ True Windows True AllScans ApplicationControl [NONE] C:\Temp\AppControlException\ True Windows True AllScans All [NONE] C:\Temp\FolderWithSubfoldersAllScans\ True Windows True AllScans [NONE] /home/user1/ExcludedFolderWithSubfolders True Linux Gets Exception details for the policy named "Workstations Exception Policy" and listing only the directories exceptions .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMExceptionPolicy -PolicyName "AB - Testing - API" -List webdomains | Format-Table rulestate.enabled rulestate.source domain ----------------- ---------------- ------ True PSSymantecSEPM HTTPS://test.com True HTTP://test.com True PSSymantecSEPM HTTP://8.8.8.8 Gets Exception details for the policy named "Workstations Exception Policy" and listing only the webdomains exceptions .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMExceptionPolicy -PolicyName "Workstations Exception Policy" -List extensions | Format-Table rulestate.enabled scancategory extensions.1 extensions.2 Platform ----------------- ------------ ------------ ------------ -------- True AllScans tmp extension2 Windows True AutoProtect dk.tmp extension2 Linux #> [CmdletBinding()] Param ( # PolicyName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [Alias("Policy_Name")] [String] $PolicyName, # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # List a specific exception category [Parameter()] [ValidateSet("files", "directories", "webdomains", "extensions", "tamper")] [String] $List ) 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 # Add a PSTypeName to the object $resp.PSObject.TypeNames.Insert(0, 'SEPM.ExceptionPolicy') # Access the ScriptProperties of 'SEPM.ExceptionPolicy' to force them to run at least once # This is to ensure that the properties are available when the object is returned # refer to PSType SEPM.ExceptionPolicy for more details $null = $resp.lastModifiedTimeDate # If a specific list is requested, return only that list switch ($List) { "files" { $files = @() # Add the Windows platform to the files foreach ($f in $resp.configuration.files) { $f["Platform"] = "Windows" $files += $f } # Add the Mac platform to the files foreach ($f in $resp.configuration.mac.files) { $f["Platform"] = "Mac" $files += $f } # TODO : 01/23/2024 - Linux file exception is not a supported exception type in SEPM # Add the Linux platform to the files # foreach ($f in $resp.configuration.linux.files) { # $f["Platform"] = "Linux" # $files += $f # } return $files | ConvertTo-FlatObject } "directories" { $directories = @() # Add the Windows platform to the directories foreach ($d in $resp.configuration.directories) { $d["Platform"] = "Windows" $directories += $d } # TODO : 01/23/2024 - Mac directory exception is not a supported exception type in SEPM # Add the Mac platform to the directories # foreach ($d in $resp.configuration.mac.directories) { # $d["Platform"] = "Mac" # $directories += $d # } # Add the Linux platform to the directories foreach ($d in $resp.configuration.linux.directories) { $d["Platform"] = "Linux" $directories += $d } return $directories | ConvertTo-FlatObject } "webdomains" { return $resp.configuration.webdomains | ConvertTo-FlatObject } "extensions" { $extensions = @() # Add the Windows platform to the extensions foreach ($e in $resp.configuration.extension_list) { $e["Platform"] = "Windows" $extensions += $e } # Add the Linux platform to the extensions foreach ($e in $resp.configuration.linux.extension_list) { $e["Platform"] = "Linux" $extensions += $e } return $extensions | ConvertTo-FlatObject } "tamper" { return $resp.configuration.tamper_files | ConvertTo-FlatObject } Default { return $resp } } } } #EndRegion '.\Public\Get-SEPMExceptionPolicy.ps1' 225 #Region '.\Public\Get-SEPMFileFingerprintList.ps1' -1 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' -1 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(DefaultParameterSetName = 'PolicyName')] Param ( # PolicyName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'PolicyName' )] [Alias("Policy_Name")] [String] $PolicyName, # Policy ID [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'PolicyID' )] [Alias("Policy_ID")] [String] $PolicyID, # 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' } } process { if ($PolicyName) { # Get Policy ID from policy name $policies = Get-SEPMPoliciesSummary $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 } try { $resp = Invoke-ABRestMethod -params $params } catch { Write-Warning -Message "Error: $_" } # Add a PSTypeName to the object $resp.PSObject.TypeNames.Insert(0, 'SEPM.FirewallPolicy') return $resp } } #EndRegion '.\Public\Get-SEPMFirewallPolicy.ps1' 108 #Region '.\Public\Get-SEPMGroups.ps1' -1 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) # Add a PSTypeName to the object $allResults | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'SEPM.GroupInfo') } # return the response return $allResults } } #EndRegion '.\Public\Get-SEPMGroups.ps1' 100 #Region '.\Public\Get-SEPMGroupSettings.ps1' -1 function Get-SEPMGroupSettings { <# .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, [Parameter(Mandatory = $true)] $locationId, [Parameter(Mandatory = $true)] $groupId ) 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 { # Location ID $URI = $script:BaseURLv1 + "/groups/$groupId/locations/$locationId/settings" # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } # Invoke the request try { # Invoke the request params $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params } catch { Write-Warning -Message "Error: $_" } # Add a PSTypeName to the object # $resp | ForEach-Object { # $_.PSObject.TypeNames.Insert(0, 'SEPM.GroupInfo') # } # return the response return $resp } } #EndRegion '.\Public\Get-SEPMGroupSettings.ps1' 95 #Region '.\Public\Get-SEPMIpsPolicy.ps1' -1 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' -1 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 # Add a PSTypeName to the object $resp.PSObject.TypeNames.Insert(0, 'SEPM.LatestDefinitionInfo') return $resp } } #EndRegion '.\Public\Get-SEPMLatestDefinition.ps1' 59 #Region '.\Public\Get-SEPMLicense.ps1' -1 function Get-SEPMLicense { <# .SYNOPSIS Get SEP License Info .DESCRIPTION Get SEP License Info .PARAMETER SkipCertificateCheck Skip certificate check .PARAMETER Summary Get the summary of the license information .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMLicense serialNumber : BXXXXXXXXXX licenseType : 2 seats : 10000 startDate : 1641562400000 expireDate : 1757393999000 endDate : 1757393999000 associatedLicenses : productName : Symantec Endpoint Security Complete, Subscription License keyNames : {SES, SES_SVR, scs_content, SEP_APP_ISOLATION…} Gets the license information for the SEPM .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMLicense -Summary license_type : PAID ended : False service_end_date : 1757393999000 service_expiration_date : 1757393999000 serial_number : BXXXXXXXXXX ordered_quantity : 10000 unexpired_seats : 10000 Gets the summary of the license information for the SEPM #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Summary [Parameter()] [switch] $Summary ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/licenses" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { #If the -Summary switch is used, then we will only return the summary of the license information if ($Summary) { $URI = $script:BaseURLv1 + "/licenses/summary" } # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # Add a PSTypeName to the object if ($Summary) { $resp.PSObject.TypeNames.Insert(0, 'SEPM.LicenseSummaryInfo') } else { $resp.PSObject.TypeNames.Insert(0, 'SEPM.LicenseInfo') } return $resp } } #EndRegion '.\Public\Get-SEPMLicense.ps1' 93 #Region '.\Public\Get-SEPMLocation.ps1' -1 function Get-SEPMLocation { <# TODO update help for Location .SYNOPSIS Gets a list of locations for a specific group .DESCRIPTION Gets a list of locations for a specific group .PARAMETER SkipCertificateCheck Skip certificate check .PARAMETER GroupID Mandatory parameter for the group ID .INPUTS System.String .OUTPUTS System.Object with the following properties: locationName locationId groupName groupId groupFullPathName .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMLocation -GroupID "XXXXXXXX" name id ---- -- Default 33CE4894AC1485D12E3AAC763CF9A71B Location 1 - Internal 0CCB0536AC1485D1233F341B9495C3C5 Location 2 - VPN F5E857C9AC1485D13095A0D6E1CD5B25 Gets the list of location names and their IDs for the specified group .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMGroups | Get-SEPMLocation | ft locationName locationId groupName groupId groupFullPathName ------------ ---------- --------- ------- ----------------- Default 60B5C584AC17D44C6CC60471B7292FC4 My Company BDDDF2E6AC17D44C7007E1EA7851E110 My Company Default 60B5C584AC17D44C6CC60471B7292FC4 Default Group EA26D2D9AC17D44C532C651C62105B61 My Company\Default Group Default 60B5C584AC17D44C6CC60471B7292FC4 group 1 10314F0DAC1485D157E3089CEB47D5BC My Company\group 1 Default 60B5C584AC17D44C6CC60471B7292FC4 sub group 1 6CB7099BAC1485D118EA465F45C4AE54 My Company\group 1\sub group 1 Gets the list of location names and their IDs for all groups from the list of groups via pipeline #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # GroupID [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [String] $GroupID ) 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' } $allGroupsInfo = Get-SEPMGroups } process { # Get Group info $groupInfo = $allGroupsInfo | Where-Object { $_.id -eq $GroupID } $URI = $script:BaseURLv1 + "/groups" + "/$GroupID/locations" $locationList = @() # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } # QueryString parameters $QueryStrings = @{ hasName = $true } # Invoke the request $URI = Build-SEPMQueryURI -BaseURI $URI -QueryStrings $QueryStrings $params = @{ Method = 'GET' Uri = $URI headers = $headers } $resp = Invoke-ABRestMethod -params $params # parse response and add group information to the list foreach ($location in $resp) { $locationList += [PSCustomObject]@{ locationName = $location.split(":")[0] locationId = $location.split("/")[-1] groupName = $groupInfo.name groupId = $groupInfo.id groupFullPathName = $groupInfo.fullPathName } } # Add a PSTypeName to the object $locationList | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'SEPM.GroupLocationInfo') } # return the response return $locationList } } #EndRegion '.\Public\Get-SEPMLocation.ps1' 125 #Region '.\Public\Get-SEPMPoliciesSummary.ps1' -1 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 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 .EXAMPLE PS C:\PSSymantecSEPM> $csvPoliciesSummary = Get-SEPMPoliciesSummary | Select-Object -ExcludeProperty sources PS C:\PSSymantecSEPM> $csvPoliciesSummary | ConvertTo-FlatObject | Export-Csv C:\temp\test.csv PS C:\PSSymantecSEPM> $csvPoliciesSummary[0] enabled : True desc : name : Custom AV policy lastmodifiedtime : 1720096619241 id : DFDDEFB3AC1E4AEE7D3F8CC3968XXXXX domainid : 8D9FCF73C0A890F810856CF40E9XXXXX policytype : av subtype : assignedtocloudgroups : assignedtolocations.1.groupId : 4F492FD6AC15B5D55CA866FE6C3XXXXX assignedtolocations.1.defaultLocationId : FE77AFA8AC15B5D5607B79FDE94XXXXX assignedtolocations.1.locationIds.1 : FE77AFA8AC15B5D5607B79FDE94XXXXX assignedtolocations.1.groupNameFullPath : My Company\Workstations\Tamper\Block and do not log assignedtolocations.2.groupId : BF9A0D8CAC15B5D534DB17F34E5XXXXX assignedtolocations.2.defaultLocationId : 39F318ABAC15B5D559E1014F7B0XXXXX assignedtolocations.2.locationIds.1 : 39F318ABAC15B5D559E1014F7B0XXXXX assignedtolocations.2.groupNameFullPath : My Company\Workstations\Tamper\Block and Log lastModifiedDate : 04/07/2023 12:36:59 Export the list of all policies, its assigned groups, locations and other details to a CSV file Excludes the sources property from the output as it's empty by default #> [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 ($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: $_" } # Add a PSTypeName to the object $resp.content | ForEach-Object { $_.PSTypeNames.Insert(0, 'SEPM.PolicySummary') } # return the response return $resp.content } } #EndRegion '.\Public\Get-SEPMPoliciesSummary.ps1' 136 #Region '.\Public\Get-SEPMPolicyXML.ps1' -1 function Get-SEPMPolicyXML { <# .SYNOPSIS Gets policy details in XML format .DESCRIPTION Gets policy details in XML format. .PARAMETER PolicyName The name of the policy to get the details of Is a required parameter .PARAMETER SkipCertificateCheck Skip certificate check .OUTPUTS XML object of the policy details Typename: System.Xml.XmlDocument .EXAMPLE PS C:\PSSymantecSEPM> Get-SEPMPolicyXML -PolicyName "Standard Servers - Firewall policy" xml SchemaContainer --- --------------- version="1.0" encoding="UTF-8" SchemaContainer Gets a PowerShell XML object of the policy details #> [CmdletBinding(DefaultParameterSetName = 'PolicyName')] Param ( # PolicyName [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'PolicyName' )] [Alias("Policy_Name")] [String] $PolicyName, # Policy ID [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'PolicyID' )] [Alias("Policy_ID")] [String] $PolicyID, # 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/raw" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } } process { if ($PolicyName) { # Get Policy ID from policy name $policies = Get-SEPMPoliciesSummary $policyID = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty id $policy_type = $policies | Where-Object { $_.name -eq $PolicyName } | Select-Object -ExpandProperty policytype } # Updating URI with policy ID $URI = $URI + "/" + $policy_type + "/" + $policyID # prepare the parameters $params = @{ Method = 'GET' Uri = $URI headers = $headers } try { $resp = Invoke-RestMethod @params -SkipCertificateCheck } catch { Write-Warning -Message "Error: $_" } [xml]$xmlContent = $resp.policy_xml return $xmlContent } } #EndRegion '.\Public\Get-SEPMPolicyXML.ps1' 99 #Region '.\Public\Get-SEPMReplicationStatus.ps1' -1 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 # Add a PSTypeName to the object $resp.replicationStatus | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'SEPM.ReplicationStatus') # Add sub PSTypeName to the object foreach ($partner in $_.replicationPartnerStatusList) { $partner | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'SEPM.ReplicationPartnerStatus') } } } return $resp.replicationStatus } } #EndRegion '.\Public\Get-SEPMReplicationStatus.ps1' 67 #Region '.\Public\Get-SEPMThreatStats.ps1' -1 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.Stats } } #EndRegion '.\Public\Get-SEPMThreatStats.ps1' 55 #Region '.\Public\Get-SEPMVersion.ps1' -1 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\Move-SEPClientGroup.ps1' -1 function Move-SEPClientGroup { <# .SYNOPSIS Moves a computer to a different SEPM group .DESCRIPTION Moves a computer to a different SEPM group Gathers the hardwareKey of the computer and the group ID of the destination group and sends a PATCH request to the SEPM API .PARAMETER ComputerName Specifies the name of the computer for which you want to get the information .PARAMETER GroupName Specifies the group full path name for which you want to get the information Full path name is the group name with the parent groups separated by backslash "My Company\EMEA\Workstations" .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE Move-SEPClientGroup -ComputerName "MyComputer" -GroupName "My Company\EMEA\Workstations" Moves the computer MyComputer to the group My Company\EMEA\Workstations .EXAMPLE "MyComputer1","MyComputer2" | Move-SEPClientGroup -GroupName "My Company\EMEA\Workstations" Moves the computers MyComputer1 and MyComputer2 to the group My Company\EMEA\Workstations via pipeline .EXAMPLE Move-SEPClientGroup -ComputerName "MyComputer" -GroupName "My Company\EMEA\Workstations" -SkipCertificateCheck Moves the computer MyComputer to the group My Company\EMEA\Workstations and skips certificate check #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # ComputerName [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [Alias("Hostname", "DeviceName", "Device", "Computer")] [String] $ComputerName, # group name [Parameter( Mandatory = $true )] [Alias("Group")] [String] $GroupName ) begin { # initialize the configuration $test_token = Test-SEPMAccessToken if (-not $test_token) { Get-SEPMAccessToken | Out-Null } if ($SkipCertificateCheck) { $script:SkipCert = $true } $URI = $script:BaseURLv1 + "/computers" $headers = @{ "Authorization" = "Bearer " + $script:accessToken.token "Content" = 'application/json' } # Get all groups from SEPM $allGroups = Get-SEPMGroups } process { # Get the computer hardwareID $hardwareKey = Get-SEPComputers -ComputerName $ComputerName | Select-Object -ExpandProperty hardwareKey if ([string]::IsNullOrEmpty($hardwareKey)) { $message = "HardwareKey of computer $ComputerName not found. Please check the computer name and try again." Write-Error $message return } # Get the group ID of the destination group $groupID = $allGroups | Where-Object { $_.fullPathName -eq $GroupName } | Select-Object -ExpandProperty id if ([string]::IsNullOrEmpty($groupID)) { $message = "Group $GroupName not found. Please check the group name and try again." $message += "Following group format is expected: 'My Company\group\subgroup'" Write-Error $message return } # Body structure for the request $body = @( @{ "group" = @{ "id" = $groupID } "hardwareKey" = $hardwareKey } ) # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI headers = $headers contenttype = 'application/json' body = $body | ForEach-Object { ConvertTo-Json @( $_ ) } # This way converts to JSON as array } # Invoke the request try { $resp = Invoke-ABRestMethod -params $params } catch { Write-Warning -Message "Error: $_" } $fullResponse = [PSCustomObject]@{ computerName = $ComputerName computerHardwareKey = $hardwareKey targetGroup = $GroupName responseCode = $resp.responseCode responseMessage = $resp.responseMessage } # Add a PSTypeName to the object $fullResponse.PSObject.TypeNames.Insert(0, 'SEPM.MoveClientGroupResponse') # return the response return $fullResponse } } #EndRegion '.\Public\Move-SEPClientGroup.ps1' 134 #Region '.\Public\New-SEPMGroup.ps1' -1 function New-SEPMGroup { <# .SYNOPSIS Creates a new SEPM group .DESCRIPTION Creates a new SEPM group Requires the full path name of the parent group .PARAMETER GroupName Specifies the name of the group to create .PARAMETER ParentGroup Specifies the full path name of the parent group Full path name is the group name with the parent groups separated by backslash "My Company\EMEA\Workstations" .PARAMETER EnabledInheritance Specifies if the group should inherit the parent group's policies .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE New-SEPMGroup -GroupName "Win7" -ParentGroup "My Company\EMEA\Workstations" Creates a new group Win7 under the group My Company\EMEA\Workstations .EXAMPLE New-SEPMGroup -GroupName "Win 10" -ParentGroup "My Company\EMEA\Workstations" -EnabledInheritance Creates a new group Win 10 under the group My Company\EMEA\Workstations and enables inheritance #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # group name [Parameter( ValueFromPipelineByPropertyName = $true )] [Alias("Group")] [String] $GroupName, # Parent group [Parameter( ValueFromPipelineByPropertyName = $true )] [String] $ParentGroup, # Enabled inheritance [Parameter( ValueFromPipelineByPropertyName = $true )] [Alias("Inherit")] [switch] $EnabledInheritance, # Group Description [Parameter( ValueFromPipelineByPropertyName = $true )] [String] $Description ) 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' } # Get all groups from SEPM $allGroups = Get-SEPMGroups } process { # Get the group ID of the destination group $ParentGroupID = $allGroups | Where-Object { $_.fullPathName -eq $ParentGroup } | Select-Object -ExpandProperty id if ([string]::IsNullOrEmpty($ParentGroupID)) { $message = "Group $GroupName not found. Please check the parent group name and try again." $message += "Following group format is expected: 'My Company\group\subgroup'" Write-Error $message return } # Body structure for the request $body = @{ "inherits" = $EnabledInheritance.ToBool() "name" = $GroupName "description" = $Description } # prepare the parameters $params = @{ Method = 'POST' Uri = $URI + "/$ParentGroupID" headers = $headers contenttype = 'application/json' body = $body | ConvertTo-Json } # TODO For testing only - remove this # $body | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # Invoke the request try { $resp = Invoke-ABRestMethod -params $params } catch { Write-Warning -Message "Error: $_" } # return the response return $resp } } #EndRegion '.\Public\New-SEPMGroup.ps1' 124 #Region '.\Public\Remove-SEPMFileFingerprintList.ps1' -1 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\Remove-SEPMGroup.ps1' -1 function Remove-SEPMGroup { <# TODO Update the help .SYNOPSIS Creates a new SEPM group .DESCRIPTION Creates a new SEPM group Requires the full path name of the parent group .PARAMETER GroupName Specifies the name of the group to create .PARAMETER ParentGroup Specifies the full path name of the parent group Full path name is the group name with the parent groups separated by backslash "My Company\EMEA\Workstations" .PARAMETER EnabledInheritance Specifies if the group should inherit the parent group's policies .PARAMETER SkipCertificateCheck Skip certificate check .EXAMPLE Remove-SEPMGroup -GroupName "Win7" -ParentGroup "My Company\EMEA\Workstations" Creates a new group Win7 under the group My Company\EMEA\Workstations .EXAMPLE Remove-SEPMGroup -GroupName "Win 10" -ParentGroup "My Company\EMEA\Workstations" -EnabledInheritance Creates a new group Win 10 under the group My Company\EMEA\Workstations and enables inheritance #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # group name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [Alias("Group")] [String] $GroupName, # Parent group [Parameter( ValueFromPipelineByPropertyName = $true )] [String] $ParentGroup, # Enabled inheritance [Parameter( ValueFromPipelineByPropertyName = $true )] [Alias("Inherit")] [switch] $EnabledInheritance, # Group Description [Parameter( ValueFromPipelineByPropertyName = $true )] [String] $Description ) 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' } # Get all groups from SEPM $allGroups = Get-SEPMGroups } process { # Get the group ID of the destination group $ParentGroupID = $allGroups | Where-Object { $_.fullPathName -eq $ParentGroup } | Select-Object -ExpandProperty id if ([string]::IsNullOrEmpty($ParentGroupID)) { $message = "Group $GroupName not found. Please check the parent group name and try again." $message += "Following group format is expected: 'My Company\group\subgroup'" Write-Error $message return } # Body structure for the request $body = @{ "inherits" = $EnabledInheritance.ToBool() "name" = $GroupName "description" = $Description } # prepare the parameters $params = @{ Method = 'POST' Uri = $URI + "/$ParentGroupID" headers = $headers contenttype = 'application/json' body = $body | ConvertTo-Json } # TODO For testing only - remove this # $body | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # Invoke the request try { $resp = Invoke-ABRestMethod -params $params } catch { Write-Warning -Message "Error: $_" } # return the response return $resp } } #EndRegion '.\Public\Remove-SEPMGroup.ps1' 125 #Region '.\Public\Remove-SEPMWindowsExtensionException.ps1' -1 function Remove-SEPMWindowsExtensionException { <# .SYNOPSIS Add a Windows extension exception to a SEPM policy .DESCRIPTION Add a Windows extension exception to a SEPM policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Extension Extension to add to the exception list Accepts multiple values .PARAMETER ScanType Type of scan to apply the exception to Valid values are: AllScans AutoProtect ScheduledAndOndemand .EXAMPLE Add-SEPMWindowsExtensionException -PolicyName "Workstations Default Exception Policy" -Extension tmp,tmp2 Add 2 Windows extensions exception, tmp and tmp2 to the "Workstations Default Exception Policy" policy .EXAMPLE Add-SEPMWindowsExtensionException -PolicyName "Workstations Default Exception Policy" -Extension tmp,tmp2 -ScanType AutoProtect Add 2 Windows extensions exception, tmp and tmp2 to the "Workstations Default Exception Policy" policy, for AutoProtect scans #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Extension [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [string[]] $Extension, # Security Risk type [ValidateSet( 'AllScans', 'AutoProtect', 'ScheduledAndOndemand' )] [string] $ScanType = 'AllScans' ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Gather current extension exception list $PolicyExtList = (Get-SEPMExceptionPolicy -PolicyName $PolicyName).configuration.extension_list.extensions # Init & populate the mandatory parameters $ExceptionParams = @{} $ExtensionList = @() $ExceptionParams.RulestateSource = $script:ModuleName $ExceptionParams.scancategory = $ScanType # Populate extension list $ExtensionList += $PolicyExtList # Parse the extension list to remove foreach ($Ext in $Extension) { if ($ExtensionList -notcontains $Ext) { throw "Cannot remove Extension $Ext. It is not in the exception list" } else { $ExtensionList = $ExtensionList | Where-Object { $_ -ne $Ext } } } $ExceptionParams.extensions += $ExtensionList # Create the file exception object with CreateExtensionListHashtable # Method parameters have to be in the same order as in the method definition $ExtensionHashTable = $Policy.ObjBody.CreateExtensionListHashtable( $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.scancategory, $ExceptionParams.extensions ) # Add the file exception parameters to the body structure $Policy.ObjBody.AddExtensionsList($ExtensionHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this # $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Remove-SEPMWindowsExtensionException.ps1' 139 #Region '.\Public\Remove-SEPMWindowsFileException.ps1' -1 function Remove-SEPMWindowsFileException { <# .SYNOPSIS Remove a Windows File Exception from a Symantec Endpoint Protection Manager Policy .DESCRIPTION Remove a Windows File Exception from a Symantec Endpoint Protection Manager Policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Path Path to add to the exception list .PARAMETER PathVariable Path variable to use for the path .PARAMETER SkipCertificateCheck Skip the certificate check when connecting to the SEPM .EXAMPLE Remove-SEPMWindowsFileException -PolicyName "Default" -Path "C:\Temp\file1.exe" Remove the file C:\Temp\file1.exe from the exception list in the policy Default #> param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Pathvariable [Parameter(ParameterSetName = 'WindowsFileException')] [ValidateSet( '[NONE]', '[COMMON_APPDATA]', '[COMMON_DESKTOPDIRECTORY]', '[COMMON_DOCUMENTS]', '[COMMON_PROGRAMS]', '[COMMON_STARTUP]', '[PROGRAM_FILES]', '[PROGRAM_FILES_COMMON]', '[SYSTEM]', '[SYSTEM_DRIVE]', '[USER_PROFILE]', '[WINDOWS]' )] [Alias('WindowsPathVariable')] [string] $PathVariable = "[NONE]", # Path [Parameter(ParameterSetName = 'WindowsFileException', Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern("^[A-Za-z]:\\(?:[^\\/:*?""<>|\r\n]+\\)*[^\\/:*?""<>|\r\n]+\.[^\\/:*?""<>|\r\n]+$")] [Alias('WindowsPath')] [string] $Path ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Init & populate the mandatory parameters $ExceptionParams = @{} $ExceptionParams.path = $path $ExceptionParams.pathvariable = $PathVariable $ExceptionParams.RulestateSource = $script:ModuleName $ExceptionParams.deleted = $true # Create the file exception object with CreateFilesHashTable # Method parameters have to be in the same order as in the method definition $FilesHashTable = $Policy.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 $Policy.ObjBody.AddConfigurationFilesExceptions($FilesHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this # $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Remove-SEPMWindowsFileException.ps1' 130 #Region '.\Public\Remove-SEPMWindowsFolderException.ps1' -1 function Remove-SEPMWindowsFolderException { <# TODO update help .SYNOPSIS Add a Windows File Exception to a Symantec Endpoint Protection Manager Policy .DESCRIPTION Add a Windows File Exception to a Symantec Endpoint Protection Manager Policy .PARAMETER PolicyName Name of the policy to update .PARAMETER Path Path to add to the exception list .PARAMETER PathVariable Path variable to use for the path .PARAMETER Sonar Add the exception to the SONAR exclusions .PARAMETER SecurityRiskCategory Add the exception to the Security Risk exclusions Takes the following values: AllScans AutoProtect ScheduledAndOndemand .PARAMETER ApplicationControl Add the exception to the Application Control exclusions .PARAMETER ExcludeChildProcesses Exclude child processes from the Application Control exclusions Requires ApplicationControl to be set to true .PARAMETER SkipCertificateCheck Skip the certificate check when connecting to the SEPM .PARAMETER AllScans Add the exception to all scan types Equivalent to setting Sonar, SecurityRiskCategory and ApplicationControl to true If no scan type is provided, default to AllScans .EXAMPLE Remove-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -Sonar Exclude the file C:\Temp\file1.exe from SONAR scans in the policy Default .EXAMPLE Remove-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -SecurityRiskCategory "AllScans" Exclude the file C:\Temp\file1.exe from all Security Risk scans in the policy Default .EXAMPLE Remove-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -ApplicationControl Exclude the file C:\Temp\file1.exe from Application Control scans in the policy Default .EXAMPLE Remove-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -ApplicationControl -ExcludeChildProcesses Exclude the file C:\Temp\file1.exe from Application Control scans in the policy Default and exclude child processes .EXAMPLE Remove-SEPMWindowsFolderException -PolicyName "Workstations Default Exception Policy" -Path "C:\Temp\file1.exe" -AllScans Exclude the file C:\Temp\file1.exe from all scan types in the policy Default (SONAR / AutoProtect / Scheduled scans / Application Control) #> [CmdletBinding()] param ( # Skip certificate check [Parameter()] [switch] $SkipCertificateCheck, # Policy Name [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true )] [String] $PolicyName, # Pathvariable [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]' )] [Alias('WindowsPathVariable')] [string] $PathVariable = "[NONE]", # Path [Parameter(ParameterSetName = 'WindowsFolderException', Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern("^[A-Za-z]:\\(?:[^\\/:*?""<>|\r\n]+\\)*")] [Alias('WindowsPath')] [string] $Path ) 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' } } process { # Initialize the policy exception $Policy = Initialize-PolicyExceptionStructure -PolicyName $PolicyName # Init & populate the mandatory parameters $ExceptionParams = @{} $ExceptionParams.directory = $path $ExceptionParams.pathvariable = $PathVariable $ExceptionParams.RulestateSource = $script:ModuleName $ExceptionParams.deleted = $true # Create the file exception object with CreateFilesHashTable # Method parameters have to be in the same order as in the method definition $DirectoryHashTable = $Policy.ObjBody.CreateDirectoryHashtable( $ExceptionParams.deleted, $ExceptionParams.RulestateEnabled, $ExceptionParams.RulestateSource, $ExceptionParams.scancategory, $ExceptionParams.scantype, $ExceptionParams.pathvariable, $ExceptionParams.directory, $ExceptionParams.recursive ) # Add the file exception parameters to the body structure $Policy.ObjBody.AddConfigurationDirectoriesExceptions($DirectoryHashTable) # Optimize the body structure (remove empty properties) $Policy.ObjBody = Optimize-ExceptionPolicyStructure -obj $Policy.ObjBody # TODO For testing only - remove this # $Policy.ObjBody | ConvertTo-Json -Depth 100 | Out-File .\Data\PolicyStructure.json -Force # prepare the parameters $params = @{ Method = 'PATCH' Uri = $URI + "/" + $Policy.PolicyID headers = $headers contenttype = 'application/json' Body = $Policy.ObjBody | ConvertTo-Json -Depth 100 } $resp = Invoke-ABRestMethod -params $params return $resp } } #EndRegion '.\Public\Remove-SEPMWindowsFolderException.ps1' 163 #Region '.\Public\Reset-SepmConfiguration.ps1' -1 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' -1 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' -1 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-SEPMCommandActiveScan.ps1' -1 function Send-SEPMCommandActiveScan { <# .SYNOPSIS Send an active scan command to SEP endpoints .DESCRIPTION Send an active scan command to SEP endpoints This will scan the specified computer(s) or group(s) for threats .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 SkipCertificateCheck Skip certificate check .EXAMPLE Send-SEPMCommandActiveScan -ComputerName "Computer1" Sends an active scan command to Computer1 .EXAMPLE "Computer1", "Computer2" | Send-SEPMCommandActiveScan Sends an active scan command to Computer1 and Computer2 #> [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, # 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 { # Init $URI = $script:BaseURLv1 + "/command-queue/activescan" 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 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() } # If group name is specified elseif ($GroupName) { # Get group ID(s) from group name(s) $GroupIDList = @() foreach ($G in $GroupName) { $GroupID = Get-SEPMGroups | Where-Object { $_.fullPathName -eq $G } | Select-Object -ExpandProperty id -First 1 $GroupIDList += $GroupID } # URI query strings $QueryStrings = @{ group_ids = $GroupIDList } # 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 Body = $body | ConvertTo-Json ContentType = 'application/json' } $resp = Invoke-ABRestMethod -params $params # return the response return $resp } } #EndRegion '.\Public\Send-SEPMCommandActiveScan.ps1' 136 #Region '.\Public\Send-SEPMCommandClearIronCache.ps1' -1 function Send-SEPMCommandClearIronCache { <# .SYNOPSIS Send a clear IRON cache command to SEP endpoints .DESCRIPTION Send a clear IRON cache command to SEP endpoints This will remove cached information about a specific hash from the SEP client .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 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 clean IRON cache command Computer1 .EXAMPLE "Computer1", "Computer2" | Send-SEPMCommandClearIronCache Sends a clean IRON cache command Computer1 and Computer2 .EXAMPLE Send-SEPMCommandClearIronCache -GroupName "My Company\EMEA\Workstations\Site1" Sends a clean IRON cache command 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, # 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 { # Init $URI = $script:BaseURLv1 + "/command-queue/ironcache" # Build body and add correct hash to body $body = @{ data = @() } $hashParameter = $PSCmdlet.MyInvocation.BoundParameters.Keys | Where-Object { $_ -in @('SHA256', 'MD5', 'SHA1') } switch ($hashParameter) { 'SHA256' { $body.Add("hashType", "sha256") $body.data += $SHA256 } 'MD5' { $body.Add("hashType", "md5") $body.data += $MD5 } 'SHA1' { $body.Add("hashType", "sha1") $body.data += $SHA1 } } 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 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() } # 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 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 Body = $body | ConvertTo-Json ContentType = 'application/json' } $resp = Invoke-ABRestMethod -params $params # return the response return $resp } } #EndRegion '.\Public\Send-SEPMCommandClearIronCache.ps1' 196 #Region '.\Public\Send-SEPMCommandFullScan.ps1' -1 function Send-SEPMCommandFullScan { <# .SYNOPSIS Send a full scan command to SEP endpoints .DESCRIPTION Send a full scan command to SEP endpoints This will scan the specified computer(s) or group(s) for threats .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 SkipCertificateCheck Skip certificate check .EXAMPLE Send-SEPMCommandFullScan -ComputerName "Computer1" Sends an active scan command to Computer1 .EXAMPLE "Computer1", "Computer2" | Send-SEPMCommandFullScan Sends a full scan command to Computer1 and Computer2 #> [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, # 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 { # Init $URI = $script:BaseURLv1 + "/command-queue/fullscan" 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 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() } # If group name is specified elseif ($GroupName) { # Get group ID(s) from group name(s) $GroupIDList = @() foreach ($G in $GroupName) { $GroupID = Get-SEPMGroups | Where-Object { $_.fullPathName -eq $G } | Select-Object -ExpandProperty id -First 1 $GroupIDList += $GroupID } # URI query strings $QueryStrings = @{ group_ids = $GroupIDList } # 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 Body = $body | ConvertTo-Json ContentType = 'application/json' } $resp = Invoke-ABRestMethod -params $params # return the response return $resp } } #EndRegion '.\Public\Send-SEPMCommandFullScan.ps1' 136 #Region '.\Public\Send-SEPMCommandGetFile.ps1' -1 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' -1 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' -1 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 # Test if the credential path exists if (-not (Test-Path -Path $script:credentialsFilePath)) { New-Item -Path $script:credentialsFilePath -ItemType File -Force | Out-Null } # Saving credentials to disk $Credentials | Export-Clixml -Path $script:credentialsFilePath -Force } #EndRegion '.\Public\Set-SepmAuthentication.ps1' 52 #Region '.\Public\Set-SepmConfiguration.ps1' -1 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' -1 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' -1 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' -1 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-SEPMFileFingerprintList.ps1' -1 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' -1 #################################### # 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 # Update the data types when loading the module Update-TypeData -PrependPath (Join-Path -Path $PSScriptRoot -ChildPath 'PSSymantecSEPM.Types.ps1xml') # 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 # Module name [string] $script:ModuleName = "PSSymantecSEPM" # 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' 96 |