Cloud-App-Security.psm1
$ErrorActionPreference = 'Stop' #region ----------------------------Value Maps---------------------------- $AppValueMap = @{'Box'=10489;'Okta'=10980;'Salesforce'=11114;'Office 365'=11161;'Amazon Web Services'=11599;'Dropbox'=11627;'Google Apps'=11770;'ServiceNow'=14509;'Microsoft OneDrive for Business'=15600;'Microsoft Cloud App Security'=20595;'Microsoft Sharepoint Online'=20892;'Microsoft Exchange Online'=20893} $IpCategoryValueMap = @{'None'=0;'Internal'=1;'Administrative'=2;'Risky'=3;'VPN'=4;'Cloud Provider'=5} $SeverityValueMap = @{'High'=2;'Medium'=1;'Low'=0} $ResolutionStatusValueMap = @{'Resolved'=2;'Dismissed'=1;'Open'=0} $FileTypeValueMap = @{'Other'=0;'Document'=1;'Spreadsheet'=2; 'Presentation'=3; 'Text'=4; 'Image'=5; 'Folder'=6} $FileAccessLevelValueMap = @{'Private'=0;'Internal'=1;'External'=2;'Public'=3;'PublicInternet'=4} #endregion ----------------------------Value Maps---------------------------- function ConvertTo-CASJsonFilterString ($colFilters) # Private function that should not be exported { $colTemp = @() ForEach ($f in $colFilters) {$colTemp += ((($f | ConvertTo-Json -Depth 2 -Compress).TrimEnd('}')).TrimStart('{'))} # Convert filter set to JSON and trim outer curly braces Write-Output ('{'+($colTemp -join '},')+'}}') # Touch up the string just a little and return it } <# .Synopsis Gets user account information from your Cloud App Security tenant. .DESCRIPTION Gets user account information from your Cloud App Security tenant and requires a credential be provided. Without parameters, Get-CASAccount gets 100 account records and associated properties. You can specify a particular account GUID to fetch a single account's information or you can pull a list of accounts based on the provided filters. Get-CASAccount returns a single custom PS Object or multiple PS Objects with all of the account properties. Methods available are only those available to custom objects by default. .EXAMPLE Get-CASAccount -ResultSetSize 1 username : alice@contoso.com consolidatedTags : {} userDomain : contoso.com serviceData : @{20595=} lastSeen : 2016-05-13T20:23:47.210000Z _tid : 17000616 services : {20595} _id : 572caf4588011e452ec18ef0 firstSeen : 2016-05-06T14:50:44.762000Z external : False Identity : 572caf4588011e452ec18ef0 This pulls back a single user record and is part of the 'List' parameter set. .EXAMPLE Get-CASAccount -Identity 572caf4588011e452ec18ef0 username : alice@contoso.com consolidatedTags : {} userDomain : contoso.com serviceData : @{20595=} agents : {} lastSeen : 2016-05-13T20:23:47.210000Z _tid : 17000616 services : {20595} _id : 572caf4588011e452ec18ef0 firstSeen : 2016-05-06T14:50:44.762000Z external : False Identity : 572caf4588011e452ec18ef0 This pulls back a single user record using the GUID and is part of the 'Fetch' parameter set. .EXAMPLE (Get-CASAccount -UserDomain contoso.com).count 2 This pulls back all accounts from the specified domain and returns a count of the returned objects. .EXAMPLE Get-CASAccount -Affiliation External | select @{N='Unique Domains'; E={$_.userDomain}} -Unique Unique Domains -------------- gmail.com outlook.com yahoo.com This pulls back all accounts flagged as external to the domain and displays only unique records in a new property called 'Unique Domains'. .EXAMPLE (Get-CASAccount -ServiceNames 'Microsoft Cloud App Security').serviceData.20595 email lastLogin lastSeen ----- --------- -------- admin@mod.onmicrosoft.com 2016-06-13T21:17:40.821000Z 2016-06-13T21:17:40.821000Z This queries for any Cloud App Security accounts and displays the serviceData table containing the email, last login, and last seen properties. 20595 is the Service ID for Cloud App Security. .FUNCTIONALITY Get-CASAccount is intended to function as a query mechanism for obtaining account information from Cloud App Security. #> function Get-CASAccount { [CmdletBinding()] Param ( # Fetches an account object by its unique identifier. [Parameter(ParameterSetName='Fetch', Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [ValidatePattern({^[A-Fa-f0-9]{24}$})] [alias("_id")] [string]$Identity, # Specifies the URL of your CAS tenant, for example 'contoso.portal.cloudappsecurity.com'. [Parameter(Mandatory=$false)] [ValidateScript({(($_.StartsWith('https://') -eq $false) -and ($_.EndsWith('.adallom.com') -or $_.EndsWith('.cloudappsecurity.com')))})] [string]$TenantUri, # Specifies the CAS credential object containing the 64-character hexadecimal OAuth token used for authentication and authorization to the CAS tenant. [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential, # Limits the results to external, if true, or internal users, if false [Parameter(ParameterSetName='List', Mandatory=$false)] [bool[]]$External, # Limits the results to items related to the specified user names, such as 'alice@contoso.com','bob@contoso.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$UserName, # Limits the results to items related to the specified service IDs, such as 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$Services, # Limits the results to items related to the specified service names, such as 'Office 365' and 'Google Apps'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box','Okta','Salesforce','Office 365','Amazon Web Services','Dropbox','Google Apps','ServiceNow','Microsoft OneDrive for Business','Microsoft Cloud App Security','Microsoft Sharepoint Online','Microsoft Exchange Online')] [string[]]$ServiceNames, # Limits the results to items not related to the specified service ids, such as 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$ServicesNot, # Limits the results to items not related to the specified service names, such as 'Office 365' and 'Google Apps'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box','Okta','Salesforce','Office 365','Amazon Web Services','Dropbox','Google Apps','ServiceNow','Microsoft OneDrive for Business','Microsoft Cloud App Security','Microsoft Sharepoint Online','Microsoft Exchange Online')] [string[]]$ServiceNamesNot, # Limits the results to items found in the specified user domains, such as 'contoso.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$UserDomain, # Specifies the property by which to sort the results. Possible Values: 'UserName','LastSeen'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Username','LastSeen')] [string]$SortBy, # Specifies the direction in which to sort the results. Possible Values: 'Ascending','Descending'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Ascending','Descending')] [string]$SortDirection, # Specifies the maximum number of results (up to 5000) to retrieve when listing items matching the specified filter criteria. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateRange(1,5000)] [int]$ResultSetSize = 100, # Specifies the number of records, from the beginning of the result set, to skip. [Parameter(ParameterSetName='List', Mandatory=$false)] [int]$Skip = 0 ) Begin { If (!$TenantUri) # If -TenantUri specified, use it and skip these { If ($CASCredential) {$TenantUri = $CASCredential.GetNetworkCredential().username} # If well-known cred session var present, use it If ($Credential) {$TenantUri = $Credential.GetNetworkCredential().username} # If -Credential specfied, use it over the well-known cred session var } If (!$TenantUri) {Write-Error 'No tenant URI available. Please check the -TenantUri parameter or username of the supplied credential' -ErrorAction Stop} If ($CASCredential) {$Token = $CASCredential.GetNetworkCredential().Password.ToLower()} # If well-known cred session var present, use it If ($Credential) {$Token = $Credential.GetNetworkCredential().Password.ToLower()} # If -Credential specfied, use it over the well-known cred session var If (!$Token) {Write-Error 'No token available. Please check the OAuth token (password) of the supplied credential' -ErrorAction Stop} } Process { # Fetch mode should happen once for each item from the pipeline, so it goes in the 'Process' block If ($PSCmdlet.ParameterSetName -eq 'Fetch') { Try { # Fetch the item by its id $FetchResponse = (Invoke-WebRequest -Uri "https://$TenantUri/api/v1/accounts/$Identity/" -Headers @{Authorization = "Token $Token"} -Method Get -ErrorAction Stop) -replace '"Id":', '"Id_int":' | ConvertFrom-Json } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($FetchResponse) {Write-Output $FetchResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } End { If ($PSCmdlet.ParameterSetName -eq 'List') # Only run remainder of this end block if not in fetch mode { # List mode logic only needs to happen once, so it goes in the 'End' block for efficiency $Body = @{'skip'=$Skip;'limit'=$ResultSetSize} # Base request body #region ----------------------------SORTING---------------------------- If ($SortBy -xor $SortDirection) {Write-Error 'Error: When specifying either the -SortBy or the -SortDirection parameters, you must specify both parameters.' -ErrorAction Stop} # Add sort direction to request body, if specified If ($SortDirection -eq 'Ascending') {$Body.Add('sortDirection','asc')} If ($SortDirection -eq 'Descending') {$Body.Add('sortDirection','desc')} # Add sort field to request body, if specified If ($SortBy) { If ($SortBy -eq 'LastSeen') { $Body.Add('sortField','lastSeen') # Patch to convert 'LastSeen' to 'lastSeen' } Else { $Body.Add('sortField',$SortBy.ToLower()) } } #endregion ----------------------------SORTING---------------------------- #region ----------------------------FILTERING---------------------------- $FilterSet = @() # Filter set array # Additional parameter validations and mutual exclusions If ($ServiceNames -and ($Services -or $ServiceNamesNot -or $ServicesNot)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} If ($Services -and ($ServiceNames -or $ServiceNamesNot -or $ServicesNot)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} If ($ServiceNamesNot -and ($Services -or $ServiceNames -or $ServicesNot)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} If ($ServicesNot -and ($Services -or $ServiceNamesNot -or $ServiceNames)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} # Value-mapped filters If ($ServiceNames) {$FilterSet += @{'service'=@{'eq'=($ServiceNames.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} If ($ServiceNamesNot) {$FilterSet += @{'service'=@{'neq'=($ServiceNamesNot.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} # Simple filters If ($External) {$FilterSet += @{'affiliation'= @{'eq'=$External}}} If ($UserName) {$FilterSet += @{'user.username'= @{'eq'=$UserName}}} If ($Services) {$FilterSet += @{'service'= @{'eq'=$Services}}} If ($ServicesNot) {$FilterSet += @{'service'= @{'neq'=$ServicesNot}}} If ($UserDomain) {$FilterSet += @{'domain'= @{'eq'=$UserDomain}}} # Add filter set to request body as the 'filter' property If ($FilterSet) {$Body.Add('filters',(ConvertTo-CASJsonFilterString $FilterSet))} #endregion ----------------------------FILTERING---------------------------- # Get the matching alerts and handle errors Try { $ListResponse = ((Invoke-WebRequest -Uri "https://$TenantUri/api/v1/accounts/" -Body $Body -Headers @{Authorization = "Token $Token"} -Method Post -ErrorAction Stop) -replace '"Id":', '"Id_int":' | ConvertFrom-Json).data } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: Check to ensure the -TenantUri parameter is valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($ListResponse) {Write-Output $ListResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } } <# .Synopsis Gets user activity information from your Cloud App Security tenant. .DESCRIPTION Gets user activity information from your Cloud App Security tenant and requires a credential be provided. Without parameters, Get-CASActivity gets 100 activity records and associated properties. You can specify a particular activity GUID to fetch a single activity's information or you can pull a list of activities based on the provided filters. Get-CASActivity returns a single custom PS Object or multiple PS Objects with all of the activity properties. Methods available are only those available to custom objects by default. .EXAMPLE Get-CASActivity -ResultSetSize 1 This pulls back a single activity record and is part of the 'List' parameter set. .EXAMPLE Get-CASActivity -Identity 572caf4588011e452ec18ef0 This pulls back a single activity record using the GUID and is part of the 'Fetch' parameter set. .EXAMPLE (Get-CASActivity -AppName Box).rawJson | ?{$_.event_type -match "upload"} | select ip_address -Unique ip_address ---------- 69.4.151.176 98.29.2.44 This grabs the last 100 Box activities, searches for an event type called "upload" in the rawJson table, and returns a list of unique IP addresses. .FUNCTIONALITY Get-CASActivity is intended to function as a query mechanism for obtaining activity information from Cloud App Security. #> function Get-CASActivity { [CmdletBinding()] Param ( # Fetches an activity object by its unique identifier. [Parameter(ParameterSetName='Fetch', Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [ValidateLength(20,20)] [alias("_id")] [string]$Identity, # Specifies the URL of your CAS tenant, for example 'contoso.portal.cloudappsecurity.com'. [Parameter(Mandatory=$false)] [ValidateScript({(($_.StartsWith('https://') -eq $false) -and ($_.EndsWith('.adallom.com') -or $_.EndsWith('.cloudappsecurity.com')))})] [string]$TenantUri, # Specifies the CAS credential object containing the 64-character hexadecimal OAuth token used for authentication and authorization to the CAS tenant. [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential, # -User limits the results to items related to the specified user/users, for example 'alice@contoso.com','bob@contoso.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$User, # Limits the results to items related to the specified service ID's, such as 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$AppId, # Limits the results to items related to the specified app names, such as 'Office 365' and 'Google Apps'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box','Okta','Salesforce','Office 365','Amazon Web Services','Dropbox','Google Apps','ServiceNow','Microsoft OneDrive for Business','Microsoft Cloud App Security','Microsoft Sharepoint Online','Microsoft Exchange Online')] [string[]]$AppName, # Limits the results to items not related to the specified service ID's, for example 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$AppIdNot, # Limits the results to items not related to the specified app names, such as 'Office 365' and 'Google Apps'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box','Okta','Salesforce','Office 365','Amazon Web Services','Dropbox','Google Apps','ServiceNow','Microsoft OneDrive for Business','Microsoft Cloud App Security','Microsoft Sharepoint Online','Microsoft Exchange Online')] [string[]]$AppNameNot, # Limits the results to items of specified event type name, such as EVENT_CATEGORY_LOGIN,EVENT_CATEGORY_DOWNLOAD_FILE. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$EventTypeName, # Limits the results to items not of specified event type name, such as EVENT_CATEGORY_LOGIN,EVENT_CATEGORY_DOWNLOAD_FILE. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$EventTypeNameNot, # Limits the results by ip category. Possible Values: 'None','Internal','Administrative','Risky','VPN','Cloud Provider'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('None','Internal','Administrative','Risky','VPN','Cloud Provider')] [string[]]$IpCategory, # Limits the results to items with the specified IP leading digits, such as 10.0. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateLength(1,45)] [string[]]$IpStartsWith, # Limits the results to items without the specified IP leading digits, such as 10.0. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateLength(1,45)] [string]$IpDoesNotStartWith, # Limits the results by device type. Possible Values: 'Desktop','Mobile','Tablet','Other'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Desktop','Mobile','Tablet','Other')] [string[]]$DeviceType, # Limits the results to admin events if true, non-admin events, if false. [Parameter(ParameterSetName='List', Mandatory=$false)] [bool]$AdminEvents, # Specifies the property by which to sort the results. Possible Values: 'Date','Created'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Date','Created')] [string]$SortBy, # Specifies the direction in which to sort the results. Possible Values: 'Ascending','Descending'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Ascending','Descending')] [string]$SortDirection, # Specifies the maximum number of results (up to 10000) to retrieve when listing items matching the specified filter criteria. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateRange(1,10000)] [int]$ResultSetSize = 100, # Specifies the number of records, from the beginning of the result set, to skip. [Parameter(ParameterSetName='List', Mandatory=$false)] [int]$Skip = 0 ) Begin { #$ErrorActionPreference = 'Stop' If (!$TenantUri) # If -TenantUri specified, use it and skip these { If ($CASCredential) {$TenantUri = $CASCredential.GetNetworkCredential().username} # If well-known cred session var present, use it If ($Credential) {$TenantUri = $Credential.GetNetworkCredential().username} # If -Credential specfied, use it over the well-known cred session var } If (!$TenantUri) {Write-Error 'No tenant URI available. Please check the -TenantUri parameter or username of the supplied credential' -ErrorAction Stop} If ($CASCredential) {$Token = $CASCredential.GetNetworkCredential().Password.ToLower()} # If well-known cred session var present, use it If ($Credential) {$Token = $Credential.GetNetworkCredential().Password.ToLower()} # If -Credential specfied, use it over the well-known cred session var If (!$Token) {Write-Error 'No token available. Please check the OAuth token (password) of the supplied credential' -ErrorAction Stop} } Process { # Fetch mode should happen once for each item from the pipeline, so it goes in the 'Process' block If ($PSCmdlet.ParameterSetName -eq 'Fetch') { Try { # Fetch the activity by its id $FetchResponse = Invoke-RestMethod -Uri "https://$TenantUri/api/v1/activities/$Identity/" -Headers @{Authorization = "Token $Token"} -Method Get -ErrorAction Stop } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($FetchResponse) {Write-Output $FetchResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } End { If ($PSCmdlet.ParameterSetName -eq 'List') # Only run remainder of this end block if not in fetch mode { # List mode logic only needs to happen once, so it goes in the 'End' block for efficiency $Body = @{'skip'=$Skip;'limit'=$ResultSetSize} # Base request body #region ----------------------------SORTING---------------------------- If ($SortBy -xor $SortDirection) {Write-Error 'Error: When specifying either the -SortBy or the -SortDirection parameters, you must specify both parameters.' -ErrorAction Stop} # Add sort direction to request body, if specified If ($SortDirection -eq 'Ascending') {$Body.Add('sortDirection','asc')} If ($SortDirection -eq 'Descending') {$Body.Add('sortDirection','desc')} # Add sort field to request body, if specified If ($SortBy) { $Body.Add('sortField',$SortBy.ToLower()) } #endregion ----------------------------SORTING---------------------------- #region ----------------------------FILTERING---------------------------- $FilterSet = @() # Filter set array # Additional parameter validations and mutual exclusions If ($AppName -and ($AppId -or $AppNameNot -or $AppIdNot)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} If ($AppId -and ($AppName -or $AppNameNot -or $AppIdNot)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} If ($AppNameNot -and ($AppId -or $AppName -or $AppIdNot)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} If ($AppIdNot -and ($AppId -or $AppNameNot -or $AppName)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} # Value-mapped filters If ($IpCategory) {$FilterSet += @{'ip.category'=@{'eq'=($IpCategory.GetEnumerator() | ForEach-Object {$IpCategoryValueMap.Get_Item($_)})}}} If ($AppName) {$FilterSet += @{'service'= @{'eq'=($AppName.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} If ($AppNameNot) {$FilterSet += @{'service'= @{'neq'=($AppNameNot.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} # Simple filters If ($User) {$FilterSet += @{'user.username'= @{'eq'=$User}}} If ($AppId) {$FilterSet += @{'service'= @{'eq'=$AppId}}} If ($AppIdNot) {$FilterSet += @{'service'= @{'neq'=$AppIdNot}}} If ($EventTypeName) {$FilterSet += @{'activity.actionType'= @{'eq'=$EventTypeName}}} If ($EventTypeNameNot) {$FilterSet += @{'activity.actionType'= @{'neq'=$EventTypeNameNot}}} If ($DeviceType) {$FilterSet += @{'device.type'= @{'eq'=$DeviceType.ToUpper()}}} # CAS API expects upper case here If ($UserAgentContains) {$FilterSet += @{'userAgent.userAgent'= @{'contains'=$UserAgentContains}}} If ($UserAgentNotContains) {$FilterSet += @{'userAgent.userAgent'= @{'ncontains'=$UserAgentNotContains}}} If ($IpStartsWith) {$FilterSet += @{'ip.address'= @{'startswith'=$IpStartsWith}}} If ($IpDoesNotStartWith) {$FilterSet += @{'ip.address'= @{'doesnotstartwith'=$IpStartsWith}}} If ($AdminEvents) {$FilterSet += @{'activity.type'= @{'eq'=$AdminEvents}}} # Add filter set to request body as the 'filter' property If ($FilterSet) {$Body.Add('filters',(ConvertTo-CASJsonFilterString $FilterSet))} #endregion ----------------------------FILTERING---------------------------- # Get the matching alerts and handle errors Try { $ListResponse = (Invoke-RestMethod -Uri "https://$TenantUri/api/v1/activities/" -Body $Body -Headers @{Authorization = "Token $Token"} -Method Post -ErrorAction Stop).data } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: Check to ensure the -TenantUri parameter is valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($ListResponse) {Write-Output $ListResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } } <# .Synopsis Gets alert information from your Cloud App Security tenant. .DESCRIPTION Gets alert information from your Cloud App Security tenant and requires a credential be provided. Without parameters, Get-CASAlert gets 100 alert records and associated properties. You can specify a particular alert GUID to fetch a single alert's information or you can pull a list of activities based on the provided filters. Get-CASAlert returns a single custom PS Object or multiple PS Objects with all of the alert properties. Methods available are only those available to custom objects by default. .EXAMPLE Get-CASAlert -ResultSetSize 1 This pulls back a single alert record and is part of the 'List' parameter set. .EXAMPLE Get-CASAlert -Identity 572caf4588011e452ec18ef0 This pulls back a single alert record using the GUID and is part of the 'Fetch' parameter set. .EXAMPLE (Get-CASAlert -ResolutionStatus Open -Severity High | where{$_.title -match "system alert"}).descriptionTemplate.parameters.LOGRABBER_SYSTEM_ALERT_MESSAGE_BASE.functionObject.parameters.appName ServiceNow Box This command showcases the ability to expand nested tables of alerts. First, we pull back only Open alerts marked as High severity and filter down to only those with a title that matches "system alert". By wrapping the initial call in parentheses you can now extract the names of the affected services by drilling into the nested tables and referencing the appName property. .FUNCTIONALITY Get-CASAlert is intended to function as a query mechanism for obtaining alert information from Cloud App Security. #> function Get-CASAlert { [CmdletBinding()] Param ( # Fetches an alert object by its unique identifier. [Parameter(ParameterSetName='Fetch', Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [ValidatePattern({^[A-Fa-f0-9]{24}$})] [alias("_id")] [string]$Identity, # Specifies the URL of your CAS tenant, for example 'contoso.portal.cloudappsecurity.com'. [Parameter(Mandatory=$false)] [ValidateScript({(($_.StartsWith('https://') -eq $false) -and ($_.EndsWith('.adallom.com') -or $_.EndsWith('.cloudappsecurity.com')))})] [string]$TenantUri, # Specifies the CAS credential object containing the 64-character hexadecimal OAuth token used for authentication and authorization to the CAS tenant. [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential, # Limits the results by severity. Possible Values: 'High','Medium','Low'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('High','Medium','Low')] [string[]]$Severity, # Limits the results to items with a specific resolution status. Possible Values: 'Open','Dismissed','Resolved'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Open','Dismissed','Resolved')] [string[]]$ResolutionStatus, # Limits the results to items related to the specified user/users, such as 'alice@contoso.com','bob@contoso.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$User, # Limits the results to items related to the specified service ID's, such as 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$Service, # Limits the results to items related to the specified service names, such as 'Office 365' and 'Google Apps'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box','Okta','Salesforce','Office 365','Amazon Web Services','Dropbox','Google Apps','ServiceNow','Microsoft OneDrive for Business','Microsoft Cloud App Security','Microsoft Sharepoint Online','Microsoft Exchange Online')] [string[]]$ServiceName, # Limits the results to items not related to the specified service ID's, such as 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$ServiceNot, # Limits the results to items not related to the specified service names, such as 'Office 365' and 'Google Apps'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box','Okta','Salesforce','Office 365','Amazon Web Services','Dropbox','Google Apps','ServiceNow','Microsoft OneDrive for Business','Microsoft Cloud App Security','Microsoft Sharepoint Online','Microsoft Exchange Online')] [string[]]$ServiceNameNot, # Limits the results to items related to the specified policy, such as 'Contoso CAS Policy'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$Policy, # Limits the results to items with a specific risk score. The valid range is 1-10. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateRange(0,10)] [int[]]$Risk, # Limits the results to items from a specific source. [Parameter(ParameterSetName='List', Mandatory=$false)] [string]$Source, # Limits the results to read items, if true, unread items, if false. [Parameter(ParameterSetName='List', Mandatory=$false)] [bool]$Read, # Specifies the property by which to sort the results. Possible Values: 'Date','Severity', 'ResolutionStatus'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Date','Severity','ResolutionStatus')] [string]$SortBy, # Specifies the direction in which to sort the results. Possible Values: 'Ascending','Descending'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Ascending','Descending')] [string]$SortDirection, # Specifies the maximum number of results (up to 10000) to retrieve when listing items matching the specified filter criteria. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateRange(1,10000)] [int]$ResultSetSize = 100, # Specifies the number of records, from the beginning of the result set, to skip. [Parameter(ParameterSetName='List', Mandatory=$false)] [int]$Skip = 0 ) Begin { If (!$TenantUri) # If -TenantUri specified, use it and skip these { If ($CASCredential) {$TenantUri = $CASCredential.GetNetworkCredential().username} # If well-known cred session var present, use it If ($Credential) {$TenantUri = $Credential.GetNetworkCredential().username} # If -Credential specfied, use it over the well-known cred session var } If (!$TenantUri) {Write-Error 'No tenant URI available. Please check the -TenantUri parameter or username of the supplied credential' -ErrorAction Stop} If ($CASCredential) {$Token = $CASCredential.GetNetworkCredential().Password.ToLower()} # If well-known cred session var present, use it If ($Credential) {$Token = $Credential.GetNetworkCredential().Password.ToLower()} # If -Credential specfied, use it over the well-known cred session var If (!$Token) {Write-Error 'No token available. Please check the OAuth token (password) of the supplied credential' -ErrorAction Stop} } Process { # Fetch mode should happen once for each item from the pipeline, so it goes in the 'Process' block If ($PSCmdlet.ParameterSetName -eq 'Fetch') { Try { # Fetch the item by its id $FetchResponse = Invoke-RestMethod -Uri "https://$TenantUri/api/v1/alerts/$Identity/" -Headers @{Authorization = "Token $Token"} -Method Get -ErrorAction Stop } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($FetchResponse) {Write-Output $FetchResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } End { If ($PSCmdlet.ParameterSetName -eq 'List') # Only run remainder of this end block if not in fetch mode { # List mode logic only needs to happen once, so it goes in the 'End' block for efficiency $Body = @{'skip'=$Skip;'limit'=$ResultSetSize} # Base request body #region ----------------------------SORTING---------------------------- If ($SortBy -xor $SortDirection) {Write-Error 'Error: When specifying either the -SortBy or the -SortDirection parameters, you must specify both parameters.' -ErrorAction Stop} # Add sort direction to request body, if specified If ($SortDirection -eq 'Ascending') {$Body.Add('sortDirection','asc')} If ($SortDirection -eq 'Descending') {$Body.Add('sortDirection','desc')} # Add sort field to request body, if specified If ($SortBy) { If ($SortBy -eq 'ResolutionStatus') { $Body.Add('sortField','status') # Patch to convert 'resolutionStatus' to 'status', because the API is not using them consistently, but we are } Else { $Body.Add('sortField',$SortBy.ToLower()) } } #endregion ----------------------------SORTING---------------------------- #region ----------------------------FILTERING---------------------------- $FilterSet = @() # Filter set array # Additional parameter validations and mutual exclusions If ($ServiceName -and ($Service -or $ServiceNameNot -or $ServiceNot)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} If ($Service -and ($ServiceName -or $ServiceNameNot -or $ServiceNot)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} If ($ServiceNameNot -and ($Service -or $ServiceName -or $ServiceNot)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} If ($ServiceNot -and ($Service -or $ServiceNameNot -or $ServiceName)) {Write-Error 'Cannot reconcile service parameters. Only use one of them at a time.' -ErrorAction Stop} # Value-mapped filters If ($ServiceName) {$FilterSet += @{'service'= @{'eq'=($ServiceName.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} If ($ServiceNameNot) {$FilterSet += @{'service'= @{'neq'=($ServiceNameNot.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} If ($Severity) {$FilterSet += @{'severity'= @{'eq'=($Severity.GetEnumerator() | ForEach-Object {$SeverityValueMap.Get_Item($_)})}}} If ($ResolutionStatus) {$FilterSet += @{'resolutionStatus'=@{'eq'=($ResolutionStatus.GetEnumerator() | ForEach-Object {$ResolutionStatusValueMap.Get_Item($_)})}}} # Simple filters If ($User) {$FilterSet += @{'entity.user'= @{'eq'=$User}}} If ($Service) {$FilterSet += @{'entity.service'= @{'eq'=$Service}}} If ($ServiceNot) {$FilterSet += @{'entity.service'= @{'neq'=$ServiceNot}}} If ($Policy) {$FilterSet += @{'entity.policy'= @{'eq'=$Policy}}} If ($Risk) {$FilterSet += @{'risk'= @{'eq'=$Risk}}} If ($AlertType) {$FilterSet += @{'id'= @{'eq'=$AlertType}}} If ($Source) {$FilterSet += @{'source'= @{'eq'=$Source}}} If ($Read) {$FilterSet += @{'read'= @{'eq'=$Read}}} # Add filter set to request body as the 'filter' property If ($FilterSet) {$Body.Add('filters',(ConvertTo-CASJsonFilterString $FilterSet))} #endregion ----------------------------FILTERING---------------------------- # Get the matching alerts and handle errors Try { $ListResponse = (Invoke-RestMethod -Uri "https://$TenantUri/api/v1/alerts/" -Body $Body -Headers @{Authorization = "Token $Token"} -Method Post -ErrorAction Stop).data } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: Check to ensure the -TenantUri parameter is valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($ListResponse) {Write-Output $ListResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } } <# .Synopsis Gets a credential to be used by other Cloud App Security module cmdlets. .DESCRIPTION Get-CASCredential imports a set of credentials to be used by other Cloud App Security module cmdlets. When using Get-CASCredential you will be prompted to provide your Cloud App Security tenant URL as well as an OAuth Token that must be created manually in the console. Get-CASCredential takes the tenant URL and OAuth token and stores them in a special global session variable called $CASCredential and converts the OAuth token to a 64bit secure string while in memory. All CAS Module cmdlets reference that special global variable to pass requests to your Cloud App Security tenant. See the examples section for ways to automate setting your CAS credentials for the session. .EXAMPLE Get-CASCredential This prompts the user to enter both their tenant URL as well as their OAuth token. Username = Tenant URL without https:// (Example: contoso.portal.cloudappsecurity.com) Password = Tenant OAuth Token (Example: 432c1750f80d66a1cf2849afb6b10a7fcdf6738f5f554e32c9915fb006bd799a) C:\>$CASCredential To verify your credentials are set in the current session, run the above command. UserName Password -------- -------- contoso.portal.cloudappsecurity.com System.Security.SecureString .EXAMPLE Get-CASCredential -PassThru | Export-CliXml C:\Users\Alice\MyCASCred.credential -Force By specifying the -PassThru switch parameter, this will put the $CASCredential into the pipeline which can be exported to a .credential file that will store the tenant URL and encrypted version of the token in a file. We can use this newly created .credential file to automate setting our CAS credentials in the session by adding an import command to our profile. C:\>notepad $profile The above command will open our PowerShell profile, which is a set of commands that will run when we start a new session. By default it is empty. $CASCredential = Import-Clixml "C:\Users\Alice\MyCASCred.credential" By adding the above line to our profile and save, the next time we open a new PowerShell session, the credential file will automatically be imported into the $CASCredential which allows us to use other CAS cmdlets without running Get-CASCredential at the start of the session. .FUNCTIONALITY Get-CASCredential is intended to import the CAS tenant URL and OAuth Token into a global session variable to allow other CAS cmdlets to authenticate when passing requests. #> function Get-CASCredential { [CmdletBinding()] Param ( # Specifies the URL of your CAS tenant, for example 'contoso.portal.cloudappsecurity.com'. [Parameter(Mandatory=$false)] [ValidateScript({(($_.StartsWith('https://') -eq $false) -and ($_.EndsWith('.adallom.com') -or $_.EndsWith('.cloudappsecurity.com')))})] [string]$TenantUri, # Specifies that the credential should be returned into the pipeline for further processing. [Parameter(Mandatory=$false)] [switch]$PassThru ) Begin { } Process { # If tenant URI is specified, prompt for OAuth token and get it all into a global variable If ($TenantUri) {[System.Management.Automation.PSCredential]$Global:CASCredential = Get-Credential -UserName $TenantUri -Message "Enter the OAuth token for $TenantUri"} # Else, prompt for both the tenant and OAuth token and get it all into a global variable Else {[System.Management.Automation.PSCredential]$Global:CASCredential = Get-Credential -Message "Enter the CAS tenant and OAuth token"} # If -PassThru is specified, write the credential object to the pipeline (the global variable will also be exported to the calling session with Export-ModuleMember) If ($PassThru) {Write-Output $CASCredential} } End { } } <# .Synopsis Gets file information from your Cloud App Security tenant. .DESCRIPTION Gets file information from your Cloud App Security tenant and requires a credential be provided. Without parameters, Get-CASFile gets 100 file records and associated properties. You can specify a particular file GUID to fetch a single file's information or you can pull a list of activities based on the provided filters. Get-CASFile returns a single custom PS Object or multiple PS Objects with all of the file properties. Methods available are only those available to custom objects by default. .EXAMPLE Get-CASFile -ResultSetSize 1 This pulls back a single file record and is part of the 'List' parameter set. .EXAMPLE Get-CASFile -Identity 572caf4588011e452ec18ef0 This pulls back a single file record using the GUID and is part of the 'Fetch' parameter set. .EXAMPLE Get-CASFile -AppName Box -Extension pdf -Domains 'microsoft.com' | select name name dlpScanTime ---- ----------- pdf_creditcardnumbers.pdf 2016-06-08T19:00:36.534000Z mytestdoc.pdf 2016-06-12T22:00:45.235000Z powershellrules.pdf 2016-06-03T13:00:19.776000Z This searches Box files for any PDF documents owned by any user in the microsoft.com domain and returns the names of those documents and the last time they were scanned for DLP violations. .FUNCTIONALITY Get-CASFile is intended to function as a query mechanism for obtaining file information from Cloud App Security. #> function Get-CASFile { [CmdletBinding()] Param ( # Fetches a file object by its unique identifier. [Parameter(ParameterSetName='Fetch', Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [ValidatePattern({^[A-Fa-f0-9]{24}$})] [alias("_id")] [string]$Identity, # Specifies the URL of your CAS tenant, for example 'contoso.portal.cloudappsecurity.com'. [Parameter(Mandatory=$false)] [ValidateScript({(($_.StartsWith('https://') -eq $false) -and ($_.EndsWith('.adallom.com') -or $_.EndsWith('.cloudappsecurity.com')))})] [string]$TenantUri, # Specifies the CAS credential object containing the 64-character hexadecimal OAuth token used for authentication and authorization to the CAS tenant. [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential, # Limits the results to items of the specified file type. Possible Values: 'Other','Document','Spreadsheet', 'Presentation', 'Text', 'Image', 'Folder'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Other','Document','Spreadsheet', 'Presentation', 'Text', 'Image', 'Folder')] [string]$Filetype, # Limits the results to items not of the specified file type. Possible Values: 'Other','Document','Spreadsheet', 'Presentation', 'Text', 'Image', 'Folder'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Other','Document','Spreadsheet', 'Presentation', 'Text', 'Image', 'Folder')] [string]$FiletypeNot, # Limits the results to items of the specified sharing access level. Possible Values: 'Private','Internal','External','Public', 'PublicInternet'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Private','Internal','External','Public', 'PublicInternet')] [string[]]$FileAccessLevel, # Limits the results to items with the specified collaborator usernames, such as 'alice@contoso.com', 'bob@microsoft.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$Collaborators, # Limits the results to items without the specified collaborator usernames, such as 'alice@contoso.com', 'bob@microsoft.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$CollaboratorsNot, # Limits the results to items with the specified owner usernames, such as 'alice@contoso.com', 'bob@microsoft.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$Owner, # Limits the results to items without the specified owner usernames, such as 'alice@contoso.com', 'bob@microsoft.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$OwnerNot, # Limits the results to items with the specified MIME Type, such as 'text/plain', 'image/vnd.adobe.photoshop'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string]$MIMEType, # Limits the results to items without the specified MIME Type, such as 'text/plain', 'image/vnd.adobe.photoshop'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string]$MIMETypeNot, # Limits the results to items shared with the specified domains, such as 'contoso.com', 'microsoft.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$Domains, # Limits the results to items not shared with the specified domains, such as 'contoso.com', 'microsoft.com'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string[]]$DomainsNot, # Limits the results to items related to the specified service ID's, such as 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$AppId, # Limits the results to items not related to the specified service ID's, such as 11161,11770 (for Office 365 and Google Apps, respectively). [Parameter(ParameterSetName='List', Mandatory=$false)] [int[]]$AppIdNot, # Limits the results to items related to the specified service names, such as Microsoft OneDrive or Box. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box', 'Salesforce', 'Dropbox', 'Google Apps', 'ServiceNow', 'Microsoft OneDrive for Business', 'Microsoft Sharepoint Online')] [string[]]$AppName, # Limits the results to items not related to the specified service names, such as Microsoft OneDrive or Box. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Box', 'Salesforce', 'Dropbox', 'Google Apps', 'ServiceNow', 'Microsoft OneDrive for Business', 'Microsoft Sharepoint Online')] [string[]]$AppNameNot, # Limits the results to items with the specified file name with extension, such as 'My Microsoft File.txt'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string]$Name, # Limits the results to items with the specified file name without extension, such as 'My Microsoft File'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string]$NameWithoutExtension, # Limits the results to items with the specified file extensions, such as 'jpg', 'txt'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string]$Extension, # Limits the results to items without the specified file extensions, such as 'jpg', 'txt'. [Parameter(ParameterSetName='List', Mandatory=$false)] [string]$ExtensionNot, # Limits the results to items that CAS has marked as trashed, if true, not trashed, if false. [Parameter(ParameterSetName='List', Mandatory=$false)] [bool]$Trashed, # Limits the results to items that CAS has marked as quarantined, if true, non-quarantined, if false. [Parameter(ParameterSetName='List', Mandatory=$false)] [bool]$Quarantined, # Limits the results to items that CAS has marked as a folder, if true, non-folders, if false. [Parameter(ParameterSetName='List', Mandatory=$false)] [bool]$Folder, # Specifies the property by which to sort the results. Possible Value: 'DateModified'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('DateModified')] [string]$SortBy, # Specifies the direction in which to sort the results. Possible Values: 'Ascending','Descending'. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateSet('Ascending','Descending')] [string]$SortDirection, # Specifies the maximum number of results (up to 5000) to retrieve when listing items matching the specified filter criteria. [Parameter(ParameterSetName='List', Mandatory=$false)] [ValidateRange(1,5000)] [int]$ResultSetSize = 100, # Specifies the number of records, from the beginning of the result set, to skip. [Parameter(ParameterSetName='List', Mandatory=$false)] [int]$Skip = 0 ) Begin { If (!$TenantUri) # If -TenantUri specified, use it and skip these { If ($CASCredential) {$TenantUri = $CASCredential.GetNetworkCredential().username} # If well-known cred session var present, use it If ($Credential) {$TenantUri = $Credential.GetNetworkCredential().username} # If -Credential specfied, use it over the well-known cred session var } If (!$TenantUri) {Write-Error 'No tenant URI available. Please check the -TenantUri parameter or username of the supplied credential' -ErrorAction Stop} If ($CASCredential) {$Token = $CASCredential.GetNetworkCredential().Password.ToLower()} # If well-known cred session var present, use it If ($Credential) {$Token = $Credential.GetNetworkCredential().Password.ToLower()} # If -Credential specfied, use it over the well-known cred session var If (!$Token) {Write-Error 'No token available. Please check the OAuth token (password) of the supplied credential' -ErrorAction Stop} } Process { # Fetch mode should happen once for each item from the pipeline, so it goes in the 'Process' block If ($PSCmdlet.ParameterSetName -eq 'Fetch') { Try { # Fetch the item by its id $FetchResponse = Invoke-RestMethod -Uri "https://$TenantUri/api/v1/files/$Identity/" -Headers @{Authorization = "Token $Token"} -Method Get -ErrorAction Stop } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($FetchResponse) {Write-Output $FetchResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } End { If ($PSCmdlet.ParameterSetName -eq 'List') # Only run remainder of this end block if not in fetch mode { # List mode logic only needs to happen once, so it goes in the 'End' block for efficiency $Body = @{'skip'=$Skip;'limit'=$ResultSetSize} # Base request body #region ----------------------------SORTING---------------------------- If ($SortBy -xor $SortDirection) {Write-Error 'Error: When specifying either the -SortBy or the -SortDirection parameters, you must specify both parameters.' -ErrorAction Stop} # Add sort direction to request body, if specified If ($SortDirection -eq 'Ascending') {$Body.Add('sortDirection','asc')} If ($SortDirection -eq 'Descending') {$Body.Add('sortDirection','desc')} # Add sort field to request body, if specified If ($SortBy -eq 'DateModified') { $Body.Add('sortField','dateModified') # Patch to convert 'DateModified' to 'dateModified' for API compatibility. There is only one Sort Field today. } } #endregion ----------------------------SORTING---------------------------- #region ----------------------------FILTERING---------------------------- $FilterSet = @() # Filter set array # Additional parameter validations and mutual exclusions If ($AppName -and ($AppId -or $AppNameNot -or $AppIdNot)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} If ($AppId -and ($AppName -or $AppNameNot -or $AppIdNot)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} If ($AppNameNot -and ($AppId -or $AppName -or $AppIdNot)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} If ($AppIdNot -and ($AppId -or $AppNameNot -or $AppName)) {Write-Error 'Cannot reconcile app parameters. Only use one of them at a time.' -ErrorAction Stop} If ($Folder -and $FolderNot) {Write-Error 'Cannot reconcile -Folder and -FolderNot switches. Use zero or one of these, but not both.' -ErrorAction Stop} If ($Quarantined -and $QuarantinedNot) {Write-Error 'Cannot reconcile -Quarantined and -QuarantinedNot switches. Use zero or one of these, but not both.' -ErrorAction Stop} If ($Trashed -and $TrashedNot) {Write-Error 'Cannot reconcile -Trashed and -TrashedNot switches. Use zero or one of these, but not both.' -ErrorAction Stop} # Value-mapped filters If ($Filetype) {$FilterSet += @{'fileType'=@{'eq'= ($Filetype.GetEnumerator() | ForEach-Object {$FileTypeValueMap.Get_Item($_)})}}} If ($FiletypeNot) {$FilterSet += @{'fileType'=@{'neq'=($FiletypeNot.GetEnumerator() | ForEach-Object {$FileTypeValueMap.Get_Item($_)})}}} If ($FileAccessLevel) {$FilterSet += @{'sharing'= @{'eq'= ($FileAccessLevel.GetEnumerator() | ForEach-Object {$FileAccessLevelValueMap.Get_Item($_)})}}} If ($AppName) {$FilterSet += @{'service'= @{'eq'= ($AppName.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} If ($AppNameNot) {$FilterSet += @{'service'= @{'neq'=($AppNameNot.GetEnumerator() | ForEach-Object {$AppValueMap.Get_Item($_)})}}} # Simple filters If ($AppId) {$FilterSet += @{'service'= @{'eq'=$AppId}}} If ($AppIdNot) {$FilterSet += @{'service'= @{'neq'=$AppIdNot}}} If ($Extension) {$FilterSet += @{'extension'= @{'eq'=$Extension}}} If ($ExtensionNot) {$FilterSet += @{'extension'= @{'neq'=$ExtensionNot}}} If ($Domains) {$FilterSet += @{'collaborators.withDomain'= @{'eq'=$Domains}}} If ($DomainsNot) {$FilterSet += @{'collaborators.withDomain'= @{'neq'=$DomainsNot}}} If ($Collaborators) {$FilterSet += @{'collaborators.users'= @{'eq'=$Collaborators}}} If ($CollaboratorsNot) {$FilterSet += @{'collaborators.users'= @{'neq'=$CollaboratorsNot}}} If ($Owner) {$FilterSet += @{'owner.username'= @{'eq'=$Owner}}} If ($OwnerNot) {$FilterSet += @{'owner.username'= @{'neq'=$OwnerNot}}} If ($MIMEType) {$FilterSet += @{'mimeType'= @{'eq'=$MIMEType}}} If ($MIMETypeNot) {$FilterSet += @{'mimeType'= @{'neq'=$MIMETypeNot}}} If ($Name) {$FilterSet += @{'filename'= @{'eq'=$Name}}} If ($NameWithoutExtension) {$FilterSet += @{'filename'= @{'text'=$NameWithoutExtension}}} If ($Folder) {$FilterSet += @{'folder'= @{'eq'=$Folder}}} If ($Quarantined) {$FilterSet += @{'quarantined'= @{'eq'=$Quarantined}}} If ($Trashed) {$FilterSet += @{'trashed'= @{'eq'=$Trashed}}} # Add filter set to request body as the 'filter' property If ($FilterSet) {$Body.Add('filters',(ConvertTo-CASJsonFilterString $FilterSet))} #endregion ----------------------------FILTERING---------------------------- # Get the matching alerts and handle errors Try { $ListResponse = (Invoke-RestMethod -Uri "https://$TenantUri/api/v1/files/" -Body $Body -Headers @{Authorization = "Token $Token"} -Method Post -ErrorAction Stop).data } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: Check to ensure the -TenantUri parameter is valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } If ($ListResponse) {Write-Output $ListResponse | Add-Member -MemberType AliasProperty -Name Identity -Value _id -PassThru} } } <# .Synopsis Uploads a proxy/firewall log file to a Cloud App Security tenant for discovery. .DESCRIPTION Send-CASDiscoveryLog uploads an edge device log file to be analyzed for SaaS discovery by Cloud App Security. When using Send-CASDiscoveryLog, you must provide a log file by name/path and a log file type, which represents the source firewall or proxy device type. Also required is the name of the discovery data source with which the uploaded log should be associated; this can be created in the console. Send-CASDiscoveryLog does not return any value .EXAMPLE Send-CASDiscoveryLog -LogFile C:\Users\Alice\MyFirewallLog.log -LogType CISCO_IRONPORT_PROXY -DiscoveryDataSource 'My CAS Discovery Data Source' This uploads the MyFirewallLog.log file to CAS for discovery, indicating that it is of the CISCO_IRONPORT_PROXY log format, and associates it with the data source name called 'My CAS Discovery Data Source' .FUNCTIONALITY Uploads a proxy/firewall log file to a Cloud App Security tenant for discovery. #> function Send-CASDiscoveryLog { [CmdletBinding()] Param ( # The full path of the Log File to be uploaded, such as 'C:\mylogfile.log'. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [Validatescript({Test-Path $_})] [string]$LogFile, # Specifies the source device type of the log file. Possible Values: 'BLUECOAT','CISCO_ASA','ZSCALER','FORTIGATE','PALO_ALTO','PALO_ALTO_SYSLOG','MCAFEE_SWG','CHECKPOINT','CISCO_SCAN_SAFE','CISCO_IRONPORT_PROXY','CHECKPOINT_OPSEC_LEA','SQUID','JUNIPER_SRX','SOPHOS_SG','MICROSOFT_ISA','WEBSENSE'. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=1)] [ValidateSet('BLUECOAT','CISCO_ASA','ZSCALER','FORTIGATE','PALO_ALTO','PALO_ALTO_SYSLOG','MCAFEE_SWG','CHECKPOINT','CISCO_SCAN_SAFE','CISCO_IRONPORT_PROXY','CHECKPOINT_OPSEC_LEA','SQUID','JUNIPER_SRX','SOPHOS_SG','MICROSOFT_ISA','WEBSENSE')] [string]$LogType, # Specifies the discovery data source name as reflected in your CAS console, such as 'US West Microsoft ASA'. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=2)] [string]$DiscoveryDataSource, # Specifies that the uploaded log file should be deleted after the upload operation completes. [alias("dts")] [switch]$Delete, # Specifies the URL of your CAS tenant, for example 'contoso.portal.cloudappsecurity.com'. [Parameter(Mandatory=$false)] [ValidateScript({(($_.StartsWith('https://') -eq $false) -and ($_.EndsWith('.adallom.com') -or $_.EndsWith('.cloudappsecurity.com')))})] [string]$TenantUri, # Specifies the CAS credential object containing the 64-character hexadecimal OAuth token used for authentication and authorization to the CAS tenant. [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential ) Begin { If (!$TenantUri) # If -TenantUri specified, use it and skip these { If ($CASCredential) {$TenantUri = $CASCredential.GetNetworkCredential().username} # If well-known cred session var present, use it If ($Credential) {$TenantUri = $Credential.GetNetworkCredential().username} # If -Credential specfied, use it over the well-known cred session var } If (!$TenantUri) {Write-Error 'No tenant URI available. Please check the -TenantUri parameter or username of the supplied credential' -ErrorAction Stop} If ($CASCredential) {$Token = $CASCredential.GetNetworkCredential().Password.ToLower()} # If well-known cred session var present, use it If ($Credential) {$Token = $Credential.GetNetworkCredential().Password.ToLower()} # If -Credential specfied, use it over the well-known cred session var If (!$Token) {Write-Error 'No token available. Please check the OAuth token (password) of the supplied credential' -ErrorAction Stop} } Process { # Get just the file name, for when full path is specified Try { $FileName = (Get-Item $LogFile).Name } Catch { Write-Error "Could not get $LogFile : $_" -ErrorAction Stop } #region GET UPLOAD URL Try { # Get an upload URL for the file $GetUploadUrlResponse = Invoke-RestMethod -Uri "https://$TenantUri/api/v1/discovery/upload_url/?filename=$FileName&source=$LogType" -Headers @{Authorization = "Token $Token"} -Method Get -ErrorAction Stop $UploadUrl = $GetUploadUrlResponse.url } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified credential is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } #endregion GET UPLOAD URL #region UPLOAD LOG FILE # Set appropriate transfer encoding header info based on log file size If (($GetUploadUrlResponse.provider -eq 'azure') -and ($LogFileBlob.Length -le 64mb)) { $FileUploadHeader = @{'x-ms-blob-type'='BlockBlob'} } ElseIf (($GetUploadUrlResponse.provider -eq 'azure') -and ($LogFileBlob.Length -gt 64mb)) { $FileUploadHeader = @{'Transfer-Encoding'='chunked'} } Try { # Upload the log file to the target URL obtained earlier, using appropriate headers If ($FileUploadHeader) { If (Test-Path $LogFile) {Invoke-RestMethod -Uri $UploadUrl -InFile $LogFile -Headers $FileUploadHeader -Method Put -ErrorAction Stop} } Else { If (Test-Path $LogFile) {Invoke-RestMethod -Uri $UploadUrl -InFile $LogFile -Method Put -ErrorAction Stop} } } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified credential is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "File upload failed: $_" } } #endregion UPLOAD LOG FILE #region FINALIZE UPLOAD Try { # Finalize the upload $FinalizeUploadResponse = Invoke-RestMethod -Uri "https://$TenantUri/api/v1/discovery/done_upload/" -Headers @{Authorization = "Token $Token"} -Body @{'uploadUrl'=$UploadUrl;'inputStreamName'=$DiscoveryDataSource} -Method Post -ErrorAction Stop } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified credential is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } ElseIf ($_ -match "The remote server returned an error: (400) Bad Request.") { Write-Error "400 - Bad Request: Ensure the -DiscoveryDataSource parameter specifies a valid data source name that you have created in the CAS web console." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } #endregion FINALIZE UPLOAD Try { # Delete the file If ($Delete) {Remove-Item $LogFile -Force -ErrorAction Stop} } Catch { Write-Error "Could not delete $LogFile : $_" -ErrorAction Stop } } End { } } <# .Synopsis Sets the status of alerts in Cloud App Security. .DESCRIPTION Sets the status of alerts in Cloud App Security and requires a credential be provided. There are two parameter sets: MarkAs: Used for marking an alert as 'Read' or 'Unread'. Dismiss: Used for marking an alert as 'Dismissed'. An alert identity is always required to be specified either explicity or implicitly from the pipeline. .EXAMPLE Set-CASAlert -Identity cac1d0ec5734e596e6d785cc -MarkAs Read This marks a single specified alert as 'Read'. .EXAMPLE Set-CASAlert -Identity cac1d0ec5734e596e6d785cc -Dismiss This will set the status of the specified alert as "Dismissed". .EXAMPLE Get-CASAlert -Unread -SortBy Date -SortDirection Descending -ResultSetSize 10 | Set-CASAlert -MarkAs Read This will pull the last 10 alerts that were generated with a status of 'Unread' and will mark them all as 'Read'. .FUNCTIONALITY Set-CASAlert is intended to function as a mechanism for setting the status of alerts Cloud App Security. #> function Set-CASAlert { Param ( # Specifies an alert object by its unique identifier. [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [ValidatePattern({^[A-Fa-f0-9]{24}$})] [alias("_id")] [string]$Identity, # Specifies how to mark the alert. Possible Values: 'Read', 'Unread'. [Parameter(ParameterSetName='MarkAs',Mandatory=$true, Position=1)] [ValidateSet('Read','Unread')] [string]$MarkAs, # Specifies that the alert should be dismissed. [Parameter(ParameterSetName='Dismiss',Mandatory=$true)] [switch]$Dismiss, # Specifies the URL of your CAS tenant, for example 'contoso.portal.cloudappsecurity.com'. [Parameter(Mandatory=$false)] [ValidateScript({(($_.StartsWith('https://') -eq $false) -and ($_.EndsWith('.adallom.com') -or $_.EndsWith('.cloudappsecurity.com')))})] [string]$TenantUri, # Specifies the CAS credential object containing the 64-character hexadecimal OAuth token used for authentication and authorization to the CAS tenant. [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential ) Begin { If (!$TenantUri) # If -TenantUri specified, use it and skip these { If ($CASCredential) {$TenantUri = $CASCredential.GetNetworkCredential().username} # If well-known cred session var present, use it If ($Credential) {$TenantUri = $Credential.GetNetworkCredential().username} # If -Credential specfied, use it over the well-known cred session var } If (!$TenantUri) {Write-Error 'No tenant URI available. Please check the -TenantUri parameter or username of the supplied credential' -ErrorAction Stop} If ($CASCredential) {$Token = $CASCredential.GetNetworkCredential().Password.ToLower()} # If well-known cred session var present, use it If ($Credential) {$Token = $Credential.GetNetworkCredential().Password.ToLower()} # If -Credential specfied, use it over the well-known cred session var If (!$Token) {Write-Error 'No token available. Please check the OAuth token (password) of the supplied credential' -ErrorAction Stop} } Process { If ($Dismiss) {$Action = 'dismiss'} If ($MarkAs) {$Action = $MarkAs.ToLower()} # Convert -MarkAs to lower case, as expected by the CAS API Try { # Set the alert's state by its id $SetResponse = Invoke-RestMethod -Uri "https://$TenantUri/api/v1/alerts/$Identity/$Action/" -Headers @{Authorization = "Token $Token"} -Method Post -ErrorAction Stop } Catch { If ($_ -like 'The remote server returned an error: (404) Not Found.') { Write-Error "404 - Not Found: $Identity. Check to ensure the -Identity and -TenantUri parameters are valid." } ElseIf ($_ -like 'The remote server returned an error: (403) Forbidden.') { Write-Error '403 - Forbidden: Check to ensure the -Credential and -TenantUri parameters are valid and that the specified token is authorized to perform the requested action.' } ElseIf ($_ -match "The remote name could not be resolved: ") { Write-Error "The remote name could not be resolved: '$TenantUri' Check to ensure the -TenantUri parameter is valid." } Else { Write-Error "Unknown exception when attempting to contact the Cloud App Security REST API: $_" } } } End { } } # Vars to export Export-ModuleMember -Variable CASCredential # Cmdlets to export Export-ModuleMember -Function Get-CASAccount Export-ModuleMember -Function Get-CASActivity Export-ModuleMember -Function Get-CASAlert Export-ModuleMember -Function Get-CASCredential Export-ModuleMember -Function Get-CASFile Export-ModuleMember -Function Send-CASDiscoveryLog Export-ModuleMember -Function Set-CASAlert |