├── HashesFromExcel.xlsx ├── Add_Devices_From_Excel_To_Zone.xlsx ├── HashesFromExcel.ps1 ├── Download_Trusted_Local_files.ps1 ├── Identify_Duplicate_Devices-README.md ├── Get-All-TDRs.md ├── Devices_With_Threats.ps1 ├── LICENSE ├── Waive_Device_Threats-README.md ├── Identify_NonDomain_Devices.ps1 ├── Backup-Tenant.ps1 ├── Add_Devices_From_Excel_To_Zone.ps1 ├── Get-All-TDRs.ps1 ├── Waive_Device_Threats.ps1 ├── README.md ├── Migration.ps1 ├── Report_AD_Coverage.ps1 ├── Identify_Duplicate_Devices_by_MAC.ps1 └── Identify_Duplicate_Devices.ps1 /HashesFromExcel.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jan-tee/cycli-examples/HEAD/HashesFromExcel.xlsx -------------------------------------------------------------------------------- /Add_Devices_From_Excel_To_Zone.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jan-tee/cycli-examples/HEAD/Add_Devices_From_Excel_To_Zone.xlsx -------------------------------------------------------------------------------- /HashesFromExcel.ps1: -------------------------------------------------------------------------------- 1 | (import-excel .\HashesFromExcel.xlsx).Hash | Add-CyHashToGlobalList -List GlobalQuarantineList -Category None -Reason "Test" -Verbose -------------------------------------------------------------------------------- /Download_Trusted_Local_files.ps1: -------------------------------------------------------------------------------- 1 | 2 | $threats = Get-CyDeviceList | Get-CyDeviceThreatList | Where-Object classification -eq Trusted 3 | $downloads = $threats | Get-CyThreatDownloadLink 4 | $wc = New-Object System.Net.WebClient 5 | $downloads | ForEach-Object { if ($_.url -match "([^/]+\.zip)") { Write-Host "Downloading: $($_.url) => $($matches[0])" ; $wc.DownloadFile($_.url, $matches[0]) } } 6 | -------------------------------------------------------------------------------- /Identify_Duplicate_Devices-README.md: -------------------------------------------------------------------------------- 1 | # IdentifyDuplicateDevices.ps1 2 | 3 | ## What it does 4 | 5 | This script will 6 | 7 | * Identify "duplicate" device records in the console (which can be caused by a variety of conditions; most commonly, this is caused when certain attributes of a device change that are used to build its unique fingerprint; the unique fingerprint then changes, which cause the device to register under a new record with the same hostname) 8 | * Identify whether there was any policy deviation as a result (e.g. after registration of the device under a new fingerprint, an automatic zone rule could have assigned a different policy than the one assigned to the original device) 9 | 10 | ## How to use it 11 | 12 | ```powershell 13 | get-help .\IdentifyDuplicateDevices.ps1 -detailed 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /Get-All-TDRs.md: -------------------------------------------------------------------------------- 1 | ### Download TDRs 2 | 3 | Fetch, store, and process TDR CSV a Cylance console's Threat Data Report (TDR) CSV files. 4 | 5 | Example: To download the current TDRs to the directory `$HOME\TDRs\myconsole\`, store and timestamp the CSV files, and convert them into an XLSX file: 6 | 7 | ```powershell 8 | Get-All-TDRs -Id myconsole -AccessToken 12983719283719283712973 9 | ``` 10 | 11 | Optionally, specify the TDR storage path and/or TDR URL (for non-EUC1 regions): 12 | ```powershell 13 | Get-All-TDRs -TDRPath . -Id myconsole -AccessToken 12983719283719283712973 -TDRUrl https://protect-euc1.cylance.com/Reports/ThreatDataReportV1/ 14 | ``` 15 | 16 | If you have configured your `Consoles.json` file, you can use auto-completion and refer to the console by name - this example would save to `$HOME\TDRs\myconsole`, and use the access token and (optionally, if it is configured) TDR Url from your `Consoles.json` file: 17 | ```powershell 18 | Get-All-TDRs -Console myconsole 19 | ``` -------------------------------------------------------------------------------- /Devices_With_Threats.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | Obtains a list of devices that have >0 threats and creates an Excel file overview. 4 | 5 | .LINK 6 | Blog: http://tietze.io/ 7 | Jan Tietze 8 | #> 9 | 10 | [CmdletBinding()] 11 | Param ( 12 | [Parameter(Mandatory=$true)] 13 | [String]$Console, 14 | [Parameter(Mandatory=$false)] 15 | [String]$OutFile = "$([System.IO.Path]::GetTempFileName()).xlsx" 16 | ) 17 | 18 | Import-Module CyCLI 19 | 20 | Get-CyAPI -Console $Console 21 | 22 | $Devices = Get-CyDeviceList | Get-CyDeviceDetail | Where-Object is_safe -eq $False 23 | $Devices | Export-Excel -Path $OutFile -WorkSheetname "Devices with threats" -AutoSize -TableName "DevicesWithThreats" -Show 24 | 25 | Foreach ($Device in $Devices) { 26 | Write-Host "Getting threats for $($Device.name) ($($Device.id))" 27 | $DeviceThreats = Get-CyDeviceThreatList -Device $Device 28 | $DeviceThreats | Export-Excel -Path $OutFile -WorkSheetname "Dev: $($Device.name)" -AutoSize -TableName "Dev$($Device.name)" 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jan Tietze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Waive_Device_Threats-README.md: -------------------------------------------------------------------------------- 1 | # Safelist via API 2 | 3 | This script allows to waive automatically based on classification "Trusted Local", either with device-local or global scope. 4 | 5 | Filtering by device and filtering by classification is supported. Filtering by zone is not yet available. 6 | 7 | To automatically globally-safelist all "Trusted-Local" hashes in the "TEST" console: 8 | 9 | ```powershell 10 | Waive_Device_Threats.ps1 -Console TEST -WaiveScope Global 11 | ``` 12 | 13 | To waive locally all "Trusted-Local" hashes in the TEST console devices matching a regular expression for all devices starting with "JTIETZE" for classifications starting with "PUP": 14 | 15 | ```powershell 16 | Waive_Device_Threats.ps1 -Console TEST -DeviceFilter "JTIETZE.*" 17 | ``` 18 | 19 | *Note: When you re-run the script shortly after you safelisted, errors may occur because the hash has already been safelisted or waived by your last run. The API has a server-side cache that will not serve the latest data, and presents device threat records that can be a minute or so out of date. Wait a few minutes before you re-run the script.* 20 | 21 | # TODO 22 | 23 | - add filtering by zone 24 | - add filtering based on classification -------------------------------------------------------------------------------- /Identify_NonDomain_Devices.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | Obtains a list of devices that are most likely NOT domain joined in a tenant. 4 | 5 | Outputs an Excel file with details. 6 | 7 | Requires "ImportExcel" module (can be installed using "Import-Module ImportExcel"). 8 | 9 | .LINK 10 | Blog: http://tietze.io/ 11 | Jan Tietze 12 | 13 | #> 14 | 15 | [CmdletBinding()] 16 | Param ( 17 | [Parameter(Mandatory=$true)] 18 | [String]$Console, 19 | [Parameter(Mandatory=$false)] 20 | [String]$OutFile = "$([System.IO.Path]::GetTempFileName()).xlsx" 21 | ) 22 | 23 | Import-Module CyCLI 24 | Import-Module ImportExcel 25 | 26 | Get-CyAPI -Console $Console 27 | 28 | $Joined = @() 29 | $NonJoined = @() 30 | 31 | Get-CyDeviceList | 32 | Get-CyDeviceDetail | 33 | ForEach-Object { 34 | Write-Verbose "Processing $($_.name) ($($_.id))" 35 | if (($_.last_logged_in_user -like "*\*") -and ($_.last_logged_in_user -notlike "$($_.name)\*")) { 36 | # this computer is likely domain joined 37 | Write-Verbose "Probably IS domain joined: $($_.name))" 38 | $_ | Add-Member DomainJoined YES 39 | } else { 40 | Write-Verbose "Probably NOT domain joined: $($_.name))" 41 | $_ | Add-Member DomainJoined NO 42 | } 43 | $_ 44 | } | Export-Excel -Path $OutFile -TableName "Devices" -WorkSheetname "Devices with domain join status" -AutoSize -Show 45 | -------------------------------------------------------------------------------- /Backup-Tenant.ps1: -------------------------------------------------------------------------------- 1 | $TenantID = "" 2 | 3 | Write-Host "Starting backup process for tenant $($TenantID)" 4 | Get-CyAPI -Console $TenantID 5 | 6 | if ((Get-CyAPIHandle) -eq $null) { 7 | throw "Could not connect." 8 | } 9 | 10 | $Timestamp = get-date -Format "yyyyMMdd-hhmmss" 11 | $File = "$($Timestamp)_$($TenantID)_Backup.json" 12 | $TenantID = (Get-CyAPIHandle).APITenantId 13 | 14 | Write-Host -NoNewline "Retrieving devices... " 15 | $Devices = Get-CyDeviceList 16 | Write-Host -ForegroundColor Green "Done" 17 | 18 | Write-Host -NoNewline "Retrieving policies... " 19 | $Policies = Get-CyPolicyList | Get-CyPolicy 20 | Write-Host -ForegroundColor Green "Done" 21 | 22 | Write-Host -NoNewline "Retrieving zones... " 23 | $Zones = Get-CyZoneList | Get-CyZone 24 | Write-Host -ForegroundColor Green "Done" 25 | 26 | Write-Host -NoNewline "Retrieving users... " 27 | $Users = Get-CyUserList | Get-CyUserDetail 28 | Write-Host -ForegroundColor Green "Done" 29 | 30 | $ZoneMembership = foreach($zone in $Zones) { 31 | Write-Host -NoNewline "Retrieving device membership for zone $($zone.name)... " 32 | $Members = Get-CyDeviceList -Zone $zone 33 | @{ 34 | "Zone" = $Zone 35 | "ZoneMembers" = $Members 36 | } 37 | Write-Host -ForegroundColor Green "Done" 38 | } 39 | 40 | $Backup = @{ 41 | Timestamp = "$($Timestamp)" 42 | TenantConsoleID = $TenantID 43 | TenantID = $TenantID 44 | Devices = $Devices 45 | Policies = $Policies 46 | Zones = $Zones 47 | ZoneMembership = $ZoneMembership 48 | Users = $Users 49 | } 50 | 51 | ConvertTo-Json $Backup -Depth 100 | Out-File $File 52 | -------------------------------------------------------------------------------- /Add_Devices_From_Excel_To_Zone.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | This tool will take an Excel file, look at its "Machine Name" column, and add all of the hosts in it that already exist 4 | in the console to a specific zone. 5 | 6 | This is useful in situations where you need to add arbitrary groups not based on some criteria that can be expressed via zone rules. 7 | 8 | The Excel file needs to have a column "Machine Name" on the active worksheet, and this has to contain the device names to add to the zone. 9 | 10 | .LINK 11 | Blog: http://tietze.io/ 12 | Jan Tietze 13 | 14 | #> 15 | [CmdletBinding()] 16 | Param ( 17 | [Parameter(Mandatory=$true)] 18 | [String]$Console, 19 | [Parameter(Mandatory=$false)] 20 | [String]$ZoneName = "Imported from Excel", 21 | [Parameter(Mandatory=$false)] 22 | [String]$ExcelFile = "Add_Devices_From_Excel_To_Zone.xlsx" 23 | ) 24 | 25 | Import-Module CyCLI 26 | Import-Module ImportExcel 27 | 28 | Get-CyAPI -Console $Console 29 | 30 | # Creates zone if it does not exist 31 | $Zone = Get-CyZone -Name $ZoneName 32 | if ($Zone -eq $null) { 33 | $Zone = New-CyZone -Name $ZoneName -Criticality Normal 34 | } 35 | 36 | # Get list of devices to add to zone 37 | $DevicesToAdd = @( Import-Excel -Path $ExcelFile | Select-Object "Machine Name") 38 | 39 | # Identify devices that already exist in tenant 40 | Write-Host -NoNewline "There were $($DevicesToAdd.Count) devices in the Excel file, of which " 41 | $ExistingDevices = @( Get-CyDeviceList | Where-Object { $DevicesToAdd."Machine Name" -Contains $_.name } ) 42 | Write-Host "$($ExistingDevices.Count) devices exist in the tenant." 43 | 44 | # Add those devices to zone 45 | Write-Host -NoNewline "Adding devices to the zone $($Zone.name)..." 46 | $ExistingDevices | Add-CyDeviceToZone -Zone $Zone 47 | Write-Host "done." 48 | -------------------------------------------------------------------------------- /Get-All-TDRs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads a number of Cylance console's TDR reports and converts them into Excel. 4 | 5 | .DESCRIPTION 6 | Downloads a pre-configured list of of Cylance console's TDR reports and converts them into Excel. 7 | 8 | Configure "Consoles.json" in the TDR path with your console data; e.g. 9 | 10 | .PARAMETER TDRPath 11 | Optional, the base path to store the TDR data. Defaults to $HOME\TDRs (use symoblic links!) 12 | 13 | .PARAMETER DefaultTDRUrl 14 | Optional. When no TDR URL is specified in the console profile, use this default TDR URL (default = EUC1 shard) 15 | 16 | .PARAMETER ConsoleId 17 | Optional. Name of a particular console to retrieve. 18 | 19 | .NOTES 20 | 21 | .LINK 22 | Blog: http://tietze.io/ 23 | Jan Tietze 24 | #> 25 | [CmdletBinding()] 26 | Param ( 27 | [parameter(Mandatory=$False)] 28 | [ValidateScript({Test-Path $_ -PathType Container })] 29 | [String]$TDRPath = "$($HOME)\TDRs", 30 | [parameter(Mandatory=$False)] 31 | [String]$DefaultTDRUrl = "https://protect-euc1.cylance.com/Reports/ThreatDataReportV1/", 32 | [parameter(Mandatory=$False)] 33 | [String]$ConsoleId = "" 34 | ) 35 | 36 | Import-Module CyCLI 37 | 38 | try { 39 | $Consoles = Get-CyConsoleConfig 40 | } catch { 41 | Write-Error "There was an error parsing or accessing the console JSON file: $($TDRPath)\Consoles.json" 42 | break 43 | } 44 | 45 | if ([String]::Empty -eq $ConsoleId) { 46 | ForEach ($Console in ($Consoles | Where "AutoRetrieve" -ne $false)) { 47 | Write-Host "Retrieving console $($Console.ConsoleId)..." 48 | $TDRUrl = if (([String]::Empty -eq $Console.TDRUrl) -or ($Console.TDRUrl -eq $null)) { $DefaultTDRUrl } else { $Console.TDRUrl } 49 | Get-CyTDRs -TDRPath $TDRPath -Id $Console.ConsoleId -AccessToken $Console.Token -TDRUrl $TDRUrl 50 | } 51 | } else { 52 | $Consoles | 53 | Where ConsoleId -eq $ConsoleId | ForEach-Object { 54 | Write-Host "Retrieving console $($_.ConsoleId)..." 55 | $TDRUrl = if (([String]::Empty -eq $_.TDRUrl) -or ($_.TDRUrl -eq $null)) { $DefaultTDRUrl } else { $_.TDRUrl } 56 | Get-CyTDRs -TDRPath $TDRPath -Id $_.ConsoleId -AccessToken $_.Token -TDRUrl $TDRUrl 57 | } 58 | } -------------------------------------------------------------------------------- /Waive_Device_Threats.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NAME 3 | WaiveDeviceThreats 4 | 5 | .SYNOPSIS 6 | Allows for automatic device-level waiving of files that match a classification. 7 | 8 | .DESCRIPTION 9 | To automatically waive all Trusted-Local files on computers that have "JTIETZE" in their name: 10 | 11 | WaiveDeviceThreats -DeviceFilter ".*JTIETZE.*" -ClassificationFilter PUP-Other 12 | 13 | .PARAMETER DeviceFilter 14 | Regular expression to only per form action on devices that match a regular expression. Defaults to ".*" 15 | 16 | .PARAMETER WaiveScope 17 | The scope at which to waive the hashes (device or global) 18 | 19 | .LINK 20 | Blog: http://tietze.io/ 21 | Jan Tietze 22 | 23 | #> 24 | [CmdletBinding()] 25 | Param ( 26 | [parameter(Mandatory=$True)] 27 | [String]$Console, 28 | [parameter(Mandatory=$False)] 29 | [String]$DeviceFilter = ".*", 30 | [parameter(Mandatory=$False)] 31 | [ValidateSet ("Device", "Global")] 32 | [String]$WaiveScope = "Device" 33 | ) 34 | 35 | Import-Module CyCLI 36 | 37 | # this will become a configurable parameter later 38 | $ClassificationFilter = "Trusted-Local" 39 | 40 | Get-CyAPI -Console $Console 41 | $Devices = Get-CyDeviceList | Where-Object name -Match $DeviceFilter | Get-CyDeviceDetail 42 | 43 | switch ($WaiveScope) { 44 | "Device" { 45 | # waive at device level 46 | foreach ($Device in $Devices) { 47 | $DeviceThreats = $Device | 48 | Get-CyDeviceThreatList | 49 | Where-Object file_status -ne Whitelisted | 50 | Where-Object classification -eq Trusted 51 | 52 | foreach ($Threat in $DeviceThreats) { 53 | Write-Host "Waiving threat on device $($Device.name) [$($Device.id)]: $($Threat.file_path), SHA: $($Threat.sha256)" 54 | Update-CyDeviceThreat -DeviceThreat $Threat -Device $Device -Action Waive 55 | } 56 | } 57 | } 58 | "Global" { 59 | # safelist globally 60 | $GloballySafelistedHashes = (Get-CyGlobalList -List GlobalSafeList).sha256 61 | 62 | $ActiveHashes = @( foreach ($Device in $Devices) { 63 | $DeviceThreats = $Device | 64 | Get-CyDeviceThreatList | 65 | Where-Object file_status -ne Whitelisted | 66 | Where-Object classification -eq Trusted 67 | foreach ($Threat in $DeviceThreats) { 68 | $Threat.sha256 69 | } 70 | } ) 71 | # safelist each hash only ONCE 72 | $ActiveHashes = $ActiveHashes | Select -Unique 73 | 74 | foreach ($hash in $ActiveHashes) { 75 | Write-Host "Globally safelisting hash: $($hash)" 76 | Add-CyHashToGlobalList -List GlobalSafeList -Category None -Reason "Safelisted via API" -SHA256 $hash 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cycli-examples 2 | 3 | CyCLI Powershell module usage examples 4 | 5 | ## One-liner examples 6 | 7 | # Add all hashes from an Excel file to Global Quarantine list 8 | 9 | ```powershell 10 | (import-excel .\HashesFromExcel.xlsx).Hash | Add-CyHashToGlobalList -List GlobalQuarantineList -Category None -Reason "Test" -Verbose 11 | ``` 12 | 13 | # Safelist all Trusted-Local files 14 | 15 | This will globally safelist all `Trusted-Local` classified detections that are currently quarantined. 16 | 17 | ```powershell 18 | (get-cydevicelist | %{ Get-CyDeviceThreatList -Device $_ | where classification -eq "Trusted" | where status -eq "Quarantined" }).sha256 | Sort-Object -Unique | Add-CyHashToGlobalList -List GlobalSafeList -Category None -Reason "Trusted-Local" 19 | ``` 20 | 21 | # List devices that have been offline for longer than 5 days 22 | 23 | ```powershell 24 | get-cydevicelist | Get-CyDeviceDetail | where date_offline -ne $null | where date_offline -lt (Get-Date).AddDays(-5) 25 | ``` 26 | 27 | # Get all script exclusions from all policies 28 | 29 | ```powershell 30 | Get-CyPolicyList | %{ (Get-CyPolicy -Policy $_) | script_control.global_settings.allowed_folders } 31 | ``` 32 | 33 | # Get all mem def exclusions from all policies 34 | ```powershell 35 | Get-CyPolicyList | Get-CyPolicy | %{ $_.memoryviolation_actions.memory_exclusion_list } 36 | ``` 37 | 38 | # Export all policies and all settings to JSON 39 | ```powershell 40 | get-cypolicylist | Get-CyPolicy | convertto-json | Out-File Policies.json 41 | ``` 42 | 43 | # Create a new policy 44 | ```powershell 45 | $p = New-CyPolicy -Name "Blank Policy" -User myconsoleuser@company.com 46 | $p | Update-CyPolicy -User myconsoleuser@company.com 47 | ``` 48 | 49 | # Assigning policy 50 | ```powershell 51 | $policy = get-cypolicylist | where name -eq "ALLOW (Files: Alert, Mem: Alert, Script: Alert)" 52 | $device = get-cydevicelist | where name -eq "JTIETZE-OPTICS1" 53 | Set-CyPolicyForDevice -Device $device -Policy $policy 54 | ``` 55 | 56 | # Add a memory and a scan exclusion to an existing policy, and commit policy to console 57 | ```powershell 58 | $p = (Get-CyPolicyList)[0] | Get-CyPolicy 59 | $p | Add-CyPolicyListSetting -Type MemDefExclusionPath -Value "\\some\app.exe" 60 | $p | Add-CyPolicyListSetting -Type ScanExclusion -Value "c:\\somedir\\somewhere\\" 61 | $p | Update-CyPolicy -User myconsoleuser@company.com 62 | ``` 63 | 64 | # Clone a policy 65 | ```powershell 66 | Copy-CyPolicy -SourcePolicyName "SCADA (Files: Block, Mem: Terminate, Script: Block, App Control: On)" -TargetPolicyName "SCADA2" -User myconsoleuser@company.com 67 | ``` 68 | # Get detections with severity "low" 69 | 70 | ```powershell 71 | Get-CyDetectionList | where severity -ne "Low" | ft 72 | ``` 73 | 74 | # Update detections 75 | 76 | Update all detections on one system: 77 | 78 | ```powershell 79 | (Get-CyDetectionList) | Where-Object { $_.Device.Name -eq "OCULEUS" } | Update-CyDetection -Status 'False Positive' 80 | ``` 81 | -------------------------------------------------------------------------------- /Migration.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Sample Migration script 3 | 4 | These are elements that must be transferred manually: 5 | 6 | * any OPTICS configuration (detection rules; detection rulesets; exception rules) 7 | * any certificate configuration (certificate whitelist) 8 | * any safelisted hashes for scripts 9 | 10 | ... 11 | #> 12 | 13 | $Source = Get-CyAPI SOURCE -Scope None 14 | $Dest = Get-CyAPI DEST -Scope None 15 | $User = "actual.console.user.id.that.exists@in.dest.tenant" 16 | 17 | <# 18 | Legacy consoles sometimes contain strings that cannot be written with current console releases 19 | #> 20 | function ToSafeString() { 21 | Param( 22 | [parameter(Mandatory=$true,Position=1)] 23 | [string]$String 24 | ) 25 | 26 | $String -replace "[&<>]","_" 27 | } 28 | 29 | # Migrate Global List 30 | Write-Host "Migrating global quarantine list" 31 | $DestList = Get-CyGlobalList -List GlobalQuarantineList -API $Dest 32 | Get-CyGlobalList -API $Source -List GlobalQuarantineList | ForEach-Object { 33 | if (! ($DestList.sha256 -contains $_.sha256)) { 34 | Write-Host "Adding hash $($_.sha256) to global quarantine list in destination" 35 | Add-CyHashToGlobalList -List GlobalQuarantineList -SHA256 $_.sha256 -Reason "M:$($_.reason)" -API $Dest 36 | } 37 | } 38 | 39 | Write-Host "Migrating global safe list" 40 | $DestList = Get-CyGlobalList -List GlobalSafeList -API $Dest 41 | Get-CyGlobalList -API $Source -List GlobalSafeList | ForEach-Object { 42 | if (! ($DestList.sha256 -contains $_.sha256)) { 43 | Write-Host "Adding hash $($_.sha256) to global safe list in destination" 44 | $Reason = "M:$(ToSafeString $_.reason)" 45 | Add-CyHashToGlobalList -List GlobalSafeList -SHA256 $_.sha256 -Reason $Reason -Category None -API $Dest 46 | } 47 | } 48 | 49 | Write-Host "Migrating policies" 50 | $DestPolicies = Get-CyPolicyList -API $Dest 51 | Get-CyPolicyList -API $Source | ForEach-Object { 52 | $DestPolicyName = "M:$(ToSafeString $_.name)" 53 | if (! ($DestPolicies.name -contains $DestPolicyName)) { 54 | Write-Host "Migrating policy $($_.name)" 55 | Write-Host " - reading original policy settings" 56 | $SourcePolicy = Get-CyPolicy -API $Source -Policy $_ 57 | Write-Host " - creating new policy" 58 | $DestPolicy = New-CyPolicy -API $Dest -Policy $SourcePolicy -Name $DestPolicyName -User $User 59 | } 60 | } 61 | 62 | Write-Host "Migrating zones" 63 | $SourcePolicies = Get-CyPolicyList -API $Source 64 | $DestZones = Get-CyZoneList -API $Dest 65 | $DestPolicies = Get-CyPolicyList -API $Dest 66 | Get-CyZoneList -API $Source | ForEach-Object { 67 | $DestZoneName = "M:$(ToSafeString $_.name)" 68 | if (! ($DestZones.name -contains $DestZoneName)) { 69 | Write-Host "Migrating zone '$($_.name)'" 70 | Write-Host " - reading original zone settings" 71 | $SourceZone = $_ | Get-CyZone -API $Source 72 | Write-Host " -- getting source policy name" 73 | $SourcePolicy = $SourcePolicies | Where-Object id -eq $SourceZone.policy_id 74 | Write-Host " source policy name: $($SourcePolicy.name)" 75 | Write-Host " - creating new zone $($DestZoneName)" 76 | $DestPolicyName = "M:$($SourcePolicy.name)" 77 | Write-Host "SEARCHING FOR $DestPolicyName in $($DestPolicies.name)" 78 | $DestPolicy = $DestPolicies | Where-Object name -eq $DestPolicyName 79 | Write-Host " -- assigning policy $($DestPolicy.name) with id $($DestPolicy.id)" 80 | $NewDestZone = New-CyZone -Name $DestZoneName -Policy $DestPolicy -API $Dest 81 | } 82 | } -------------------------------------------------------------------------------- /Report_AD_Coverage.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Reports coverage of devices (agents installed vs. not installed) for AD joined devices 4 | 5 | .DESCRIPTION 6 | This script will take a list of all devices that are AD joined, and compare it to devices in the Cylance 7 | console, based on NetBIOS name of the device. 8 | 9 | Results are presented as raw data as well as a Pivot table/chart in Excel. 10 | 11 | Also, any devices that has not checked in with the console in 30 days will be reported. 12 | 13 | .PARAMETER Path 14 | Excel file to write results to. 15 | 16 | .PARAMETER Console 17 | The console ID 18 | 19 | .PARAMETER MinimumAgeInDays 20 | Only ever consider a device if it has been offline at least this many days 21 | 22 | .LINK 23 | Blog: http://tietze.io/ 24 | Jan Tietze 25 | 26 | #> 27 | 28 | [CmdletBinding()] 29 | Param ( 30 | [Parameter(Mandatory=$true)] 31 | [String]$Console, 32 | [Parameter(Mandatory=$false)] 33 | [int]$MinimumAgeInDays = 30, 34 | [Parameter(Mandatory=$true)] 35 | [ValidateScript({ 36 | if (Test-Path -Path $_) { 37 | Throw "The output file $($_) exists." 38 | } else { return $true } 39 | })] 40 | [String]$Path, 41 | [Switch]$Show 42 | ) 43 | 44 | Get-CyAPI -Console $Console 45 | 46 | $OutFile = $Path 47 | $CutoffDate= (Get-Date).AddDays(-$MinimumAgeInDays) 48 | 49 | $devicesAD = ` 50 | Get-ADComputer -Filter * -Property name,DistinguishedName 51 | 52 | $devicesCylance = ` 53 | Get-CyDeviceList | Get-CyDeviceDetail | ForEach-Object { 54 | # NetBIOS names are 15 characters or less 55 | $hostname = $_.host_name 56 | if ($hostname -match "\.") { 57 | $hostname = $hostname.split(".")[0] 58 | } 59 | $_ | Add-Member NetBIOSName $hostname 60 | $_ 61 | } 62 | 63 | $devicesInstallStatus = $devicesAD | 64 | ForEach-Object { 65 | $netbios_name = $_.name 66 | $cylanceStatus = if ($netbios_name -in $devicesCylance.NetBIOSName) { "Installed" } else { "Not installed" } 67 | $cylanceRecord = $devicesCylance | Where-Object { $netbios_name -eq $_.NetBIOSName } 68 | $DNpath = $_.distinguishedname -split ',' 69 | $Container = $DNpath[1..($DN.count -1)] -join ',' 70 | [pscustomobject]@{ 71 | Name = $_.name 72 | DNSHostName = $_.DNSHostName 73 | Container = $Container 74 | DistinguishedName = $_.DistinguishedName 75 | CylanceInstallStatus = $cylanceStatus 76 | CylanceRegistration = $cylanceRecord.date_first_registered 77 | CylanceVersion = $cylanceRecord.agent_version 78 | CylanceState = $cylanceRecord.state 79 | CylanceOS = $cylanceRecord.os_version 80 | } 81 | } 82 | 83 | $devicesOfflineOverThreshold = $devicesCylance | 84 | Where { $_.date_offline -ne $null -and $_.date_offline -lt $CutoffDate } 85 | 86 | $pivot= @{ 87 | AutoSize = $true 88 | AutoFilter = $true 89 | IncludePivotTable = $true 90 | PivotRows = @('CylanceInstallStatus', 'Container') 91 | PivotData = 'Name' 92 | IncludePivotChart = $true 93 | ChartType = "Pie" 94 | ShowPercent = $true 95 | } 96 | 97 | $pivot2 = @{ 98 | PivotTableName = "DevicesTable" 99 | PivotData = @{"Name" = "Count"} 100 | SourceWorkSheet = "Devices" 101 | PivotRows = "CylanceInstallStatus" 102 | IncludePivotChart = $true 103 | ChartType = "ColumnClustered" 104 | NoLegend = $true 105 | } 106 | 107 | 108 | $xl = $devicesInstallStatus | Export-Excel -WorkSheetname "Devices" -AutoSize -TableName "DevicesTable" -Path $OutFile -PassThru 109 | $xl = $devicesOfflineOverThreshold | Export-Excel -ExcelPackage $xl -WorkSheetname "Offline Devices" -AutoSize -TableName "OfflineDevicesTable" 110 | 111 | if ($Show) { 112 | Export-Excel -Path $OutFile -Show 113 | } -------------------------------------------------------------------------------- /Identify_Duplicate_Devices_by_MAC.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | This tool will identify duplicate devices in a CylancePROTECT tenant environment based on their MAC address. 4 | 5 | This is useful in situations when you encounter device duplication as a result of certain updates to the operating 6 | system, Windows edition changes or in-place upgrades affecting the serial number of the operating system, or changes 7 | to other components used for fingerprinting the device (all on Windows agents prior to version 1470). 8 | 9 | .LINK 10 | Blog: http://tietze.io/ 11 | Jan Tietze 12 | 13 | #> 14 | [CmdletBinding()] 15 | Param ( 16 | [Parameter(Mandatory=$true)] 17 | [String]$Console, 18 | [Parameter(Mandatory=$false)] 19 | [String]$ZoneName = "DUPLICATES" 20 | ) 21 | 22 | Get-CyAPI -Console $Console 23 | 24 | # "duplicate" entries must be at least 30 days offline in order to be removed 25 | $MinimalAgeToRemove = [DateTime]::Now.AddDays(-30) 26 | 27 | # create potential zone 28 | $Zone = (Get-CyZoneList | Where-Object Name -eq $ZoneName) 29 | if ($null -eq $Zone) { 30 | $Zone = New-CyZone -Name $ZoneName -Criticality Low 31 | } 32 | 33 | if ($null -eq $Zone) { 34 | Throw ("Zone could not be found or created; this can be API caching related; try again in a minute.") 35 | } 36 | 37 | # get list of devices 38 | Write-Host "Getting device list" 39 | $Devices = Get-CyDeviceList 40 | # | Where-Object name -like DEFDHLT3181 41 | 42 | # enrich device objects by adding field "mac0" with first MAC address 43 | Write-Host "Enriching device list with MAC attributes" 44 | $Devices | ForEach-Object { $_ | Add-Member mac0 $_.mac_addresses[0] } 45 | 46 | # list of all device objects where the MAC address is registered more than once 47 | Write-Host "Grouping device list by unique MAC address" 48 | $DevicesGroupedByMAC = $Devices | Group-Object -Property mac0 | Where-Object Count -gt 1 49 | 50 | # MAC addresses to ignore... 51 | # These MAC addresses were observed in customer environments and were found to NOT be unique. 52 | # 53 | # 58-2C-80-13-92-63 = HUAWEI USB Ethernet adapter with default MAC address 54 | $MACIgnoreList = @("58-2C-80-13-92-63") 55 | 56 | # loop through each possible duplicate 57 | $DevicesGroupedByMAC | ForEach-Object { 58 | $MAC = $_.Name 59 | if (![String]::IsNullOrEmpty($MAC) -and !($MACIgnoreList -contains $MAC)) { 60 | # expand device objects so that date_offline etc. become available 61 | $Elements = $_.Group | Get-CyDeviceDetail 62 | $NumberOfDistinctNames = ($Elements.Name | Select-Object -Unique).Count 63 | $ElementsOnline = $Elements | Where-Object state -eq Online 64 | if ($NumberOfDistinctNames -gt 1) 65 | { 66 | # more than 1 distinct name - not a duplicate, but a strange occurence? 67 | Write-Host "ERROR: More than 1 distinct name for MAC: $($MAC); names: $($Elements.Name | Select-Object -Unique)" 68 | } 69 | elseif ($NumberOfDistinctNames -eq 1) 70 | { 71 | # multiple device records, but 1 MAC = same device. Youngest device should be kept, oldest devices will be added to "remove" Zone 72 | if ($ElementsOnline.Count -eq 1) { 73 | # remove all offline device records, because there is one that is online 74 | $Candidates = $Elements | Sort-Object -Descending -Property date_first_registered 75 | $Survivor = $ElementsOnline 76 | $CandidatesToRemove = $Candidates | Where-Object state -eq Offline | Where-Object date_offline -lt $MinimalAgeToRemove 77 | if ($CandidatesToRemove.Count -gt 0) 78 | { 79 | Write-Host "DUPE1: Survivor $($Survivor.id), adding duplicate devices to zone: $($CandidatesToRemove.id)" 80 | $CandidatesToRemove | Add-CyDeviceToZone -Zone $Zone 81 | } 82 | } elseif ($ElementsOnline.Count -eq 0) { 83 | # remove all but the device record that connected most recently 84 | $Candidates = $Elements | Sort-Object -Descending -Property date_offline 85 | $Survivor = $Candidates[0] 86 | $CandidatesToRemove = $Candidates[1..($Candidates.Count-1)] | Where-Object date_offline -lt $MinimalAgeToRemove 87 | if ($CandidatesToRemove.Count -gt 0) { 88 | Write-Host "DUPE2: Survivor $($Survivor.id), adding duplicate devices to zone: $($CandidatesToRemove.id)" 89 | $CandidatesToRemove | Add-CyDeviceToZone -Zone $Zone 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Identify_Duplicate_Devices.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Identifies duplicate devices in a console. 4 | 5 | .DESCRIPTION 6 | Under certain circumstances, the same physical or virtual device can self-register in the console 7 | with a new fingerprint. This orphans the old device entry, and creates a new, mostly identical 8 | (except for the fingerprint, and 'last connected' etc. dates) device entry. This scripts helps to 9 | identify duplicates, to identify which devices to keep, and which devices have policy deviations 10 | between the "original" and "duplicate" agents. 11 | 12 | This script will put candidates for deletion as duplicates into a custom zone. 13 | 14 | Candidates are identified by having the same name; the single surviving candidate is the one with 15 | the latest registration timestamp. Only devices that are currently offline will be added to the 16 | potential duplicates. 17 | 18 | .PARAMETER Console 19 | The console ID 20 | .PARAMETER ZoneName 21 | The zone to add the identified duplicate devices to 22 | .PARAMETER MinimumAgeInDays 23 | Only ever consider a device as duplicate if it registered more than this many days ago. 24 | .PARAMETER IncludeFilter 25 | A regular expression that filters for systems to include. Default value is ".*", which means all devices. 26 | .PARAMETER ExcludeFilter 27 | A regular expression that filters for systems to exclude. Default value is to exclude no devices. 28 | .PARAMETER PerformChanges 29 | This parameter guarantees you have read the docs before you make any changes to your environment... 30 | 31 | .LINK 32 | Blog: http://tietze.io/ 33 | Jan Tietze 34 | 35 | #> 36 | [CmdletBinding()] 37 | Param ( 38 | [Parameter(Mandatory=$true)] 39 | [String]$Console, 40 | [Parameter(Mandatory=$false)] 41 | [String]$ZoneName = "DUPLICATES", 42 | [Parameter(Mandatory=$false)] 43 | [int]$MinimumAgeInDays = 30, 44 | [Parameter(Mandatory=$false)] 45 | [String]$IncludeFilter = ".*", 46 | [Parameter(Mandatory=$false)] 47 | [String]$ExcludeFilter = "MAGICSTRINGTHATISEXCLUDEDBYDEFAULT", 48 | [Parameter(Mandatory=$false)] 49 | [Switch]$PerformChanges 50 | ) 51 | 52 | Import-Module CyCLI 53 | 54 | Get-CyAPI -Console $Console 55 | 56 | $DoNotDeleteIfYoungerThanDate = (Get-Date).AddDays(-$MinimumAgeInDays) 57 | 58 | $Zone = (Get-CyZoneList | Where-Object Name -eq $ZoneName) 59 | if ($null -eq $Zone) { 60 | $Zone = New-CyZone -Name $ZoneName -Criticality Low 61 | } 62 | 63 | if ($null -eq $Zone) { 64 | Throw ("Zone could not be found or created; this can be API caching related; try again in a minute.") 65 | } 66 | 67 | $duplicateNames = Get-CyDeviceList | Where-Object name -Match $IncludeFilter | Where-Object name -NotMatch $ExcludeFilter | Group -Property name | Where-Object { $_.Count -gt 1 } 68 | 69 | $duplicateNames | ForEach-Object { 70 | Write-Verbose "Processing $($_.Group.name) (= $($_.Group.Count) devices)" 71 | 72 | # sort descending by offline date - most recent first 73 | $candidates = @( ($_.Group) | 74 | ForEach-Object { 75 | Get-CyDeviceDetail -Device $_ 76 | } ) | 77 | Sort-Object -Property date_first_registered -Descending 78 | 79 | $keep = $candidates[0] 80 | $remove = @( $candidates[1..$candidates.Count] ) 81 | 82 | if ($keep.state -eq "Offline") { 83 | Write-Verbose " - Designated survivor: $($keep.id), reg. $($keep.date_first_registered), went OFFLINE on : $($keep.date_offline). Potentially removing $($remove.Count) duplicates." 84 | } else { 85 | Write-Verbose " - Designated survivor: $($keep.id), reg. $($keep.date_first_registered), device is ONLINE: Potentially removing $($remove.Count) duplicates." 86 | } 87 | 88 | foreach ($r in $remove) { 89 | # delete device 90 | # should have a check whether the device has any threats 91 | 92 | if ($r.state -ne "Offline") { 93 | # removee must be OFFLINE to remove; any device that is ONLINE definitely exists. 94 | Write-Verbose " - would NOT delete: $($r.name) ($($r.date_first_registered)), because device is ONLINE. Check manually!" 95 | } elseif ($r.date_first_registered -gt $DoNotDeleteIfYoungerThanDate) { 96 | # removee was registered earlier than "minimum age" days 97 | Write-Verbose " - would NOT delete: $($r.name) ($($r.date_first_registered)), because device is younger than $($MinimumAgeInDays) days $($DoNotDeleteIfYoungerThanDate). Check manually!" 98 | } elseif ($r.date_first_registered -gt $keep.date_first_registered) { 99 | # keeper must have been registered AFTER removee 100 | Write-Verbose " - would NOT delete: $($r.name) ($($r.date_first_registered))), because device was registered AFTER device to keep?! Check manually!" 101 | } elseif (($keep.date_offline -ne $null) -and ($r.date_offline -gt $keep.date_offline)) { 102 | # keeper must be either online, or offline, but offline date must be after removee 103 | Write-Verbose " - would NOT delete: $($r.name) ($($r.date_first_registered))), because device went offline AFTER device to keep?! Check manually!" 104 | } else { 105 | # Add to zone of to be deleted devices! 106 | Write-Verbose " - WOULD delete: $($r.name) ($($r.date_first_registered))), which went offline $($r.date_offline)" 107 | if ($PerformChanges) { 108 | Write-Host " - ADDING device $($r.name) to zone $($ZoneName)" 109 | Add-CyDeviceToZone -Device $r -Zone $Zone -Verbose 110 | } 111 | } 112 | 113 | if ($r.date_first_registered -eq $r.date_offline) { 114 | Write-Verbose" * OBSERVATION: $($r.name) (reg date $($r.date_first_registered) equals last offline date $($r.date_offline)" 115 | } 116 | 117 | if ($keep.policy.id -ne $r.policy.id) { 118 | Write-Verbose " * NOTE! Policy difference between keeper and removee: $($r.name) ($($r.date_first_registered)) has policy $($r.policy.name), keeper ($($keep.date_first_registered)) has policy $($keep.policy.name)" 119 | } 120 | } 121 | } 122 | --------------------------------------------------------------------------------