SophosCentral.psm1

# ./SophosCentral/private/Get-SophosCentralAuthHeader.ps1
function Get-SophosCentralAuthHeader {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param (
        [switch]$Initial,
        [switch]$PartnerInitial
    )

    if ((Test-SophosCentralAuth) -eq $true) {
        $header = @{
            Authorization = 'Bearer ' + (Unprotect-Secret -Secret $SCRIPT:SophosCentral.access_token)
        }
        if (($Initial -ne $true) -and ($PartnerInitial -ne $true)) {
            switch ($SCRIPT:SophosCentral.IDType) {
                'partner' {
                    $header.Add('X-Tenant-ID', $SCRIPT:SophosCentral.CustomerTenantID)
                }
                'tenant' {
                    $header.Add('X-Tenant-ID', $SCRIPT:SophosCentral.TenantID)
                }
            }
        }
        if ($PartnerInitial) {
            $header.Add('X-Partner-ID', $SCRIPT:SophosCentral.TenantID)
        }
        $header
    }
}
# ./SophosCentral/private/Invoke-SophosCentralWebRequest.ps1
function Invoke-SophosCentralWebRequest {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.URI]$Uri,
        
        [System.Collections.Hashtable]$CustomHeader,

        [ValidateSet('Get', 'Post')]
        [string]$Method = 'Get',

        [System.Collections.Hashtable]$Body
    )

    if ($PSVersionTable.PSVersion.Major -lt 7) {
        Write-Warning 'Unsupported version of PowerShell detected'
    }
    
    if ($null -ne $CustomHeader) {
        $header = $CustomHeader
    } else {
        try {
            $header = Get-SophosCentralAuthHeader
        } catch {
            throw $_
        }
    }
    $finished = $false

    #Get request
    if ($method -eq 'Get') {
        #query api and return the first page
        $response = Invoke-RestMethod -Uri $uri -Headers $header -UseBasicParsing
        if ($null -ne $response.items) {
            $response.items
        } else {
            $response
        }
        
        #loop through additional pages of results (if applicable)
        do {
            if ($response.pages.nextKey) {
                $nextUri = $uri.AbsoluteUri + '?pageFromKey=' + $response.pages.nextKey
                $response = Invoke-RestMethod -Uri $nextUri -Headers $header -UseBasicParsing
                $response.items
            } else {
                $finished = $true
            }
        } while ($finished -eq $false)
        
    }

    #Post request
    if ($method -eq 'Post') {
        #API endpoints that use a 'post' require a body. If no body is present it will give an error back, so supply an empty body
        if ($null -eq $body) {
            $body = @{}
        }
        $bodyJson = $body | ConvertTo-Json
        
        #query api and return the first page
        $response = Invoke-RestMethod -Uri $uri -Headers $header -Method Post -Body $bodyJson -ContentType 'application/json' -UseBasicParsing 
        if ($null -ne $response.items) {
            $response.items
        } else {
            $response
        }
        #loop through additional pages of results (if applicable)
        do {
            if ($response.pages.nextKey) {
                $nextUri = $uri.AbsoluteUri + '?pageFromKey=' + $response.pages.nextKey
                $response = Invoke-RestMethod -Uri $nextUri -Headers $header -Method Post -Body $bodyJson -ContentType 'application/json' -UseBasicParsing 
                $response.items
            } else {
                $finished = $true
            }
        } while ($finished -eq $false)
    }
}
# ./SophosCentral/private/Test-SophosCentralAuth.ps1
function Test-SophosCentralAuth {
    [CmdletBinding()]
    [OutputType([bool])]
    param (
        
    )
    if ($SCRIPT:SophosCentral) {
        $date = Get-Date
        if ($SCRIPT:SophosCentral.expires_at -le $date) {
            Write-Verbose 'Access token has expired'
            #request new token
            try {
                Write-Verbose 'Attempting to obtain new access token'
                Connect-SophosCentral -ClientID $SCRIPT:SophosCentral.client_id -ClientSecret $SCRIPT:SophosCentral.client_secret -AccessTokenOnly
                Write-Verbose 'Testing new access token'
                Test-SophosCentralAuth
            } catch {
                Write-Error 'error requesting new token'
                return $false
            }           
        } else {
            return $true
        }
    } else {
        Write-Error "You must connect with 'Connect-SophosCentral' before running any commands"
        return $false
    }
}
# ./SophosCentral/private/Test-SophosPartner.ps1
function Test-SophosPartner {
    if ($SCRIPT:SophosCentral.IDType -ne 'partner') {
        return $false
    } else {
        Write-Verbose 'currently logged in using a Sophos Central Partner Service Principal'
        return $true
    }
}
# ./SophosCentral/private/Unprotect-Secret.ps1
function Unprotect-Secret {
    <#
    .SYNOPSIS
        Convert a [SecureString] to a [String]
    .EXAMPLE
        $plaintextsecret = Unprotect-Secret -Secret $clientsecret
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param (
        [Parameter(Mandatory = $true,
            HelpMessage = 'The Secure String to convert to plain text')]
        [SecureString]$Secret
    )
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret)
    [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
}
# ./SophosCentral/public/Connect-SophosCentral.ps1
function Connect-SophosCentral {
    <#
    .SYNOPSIS
        Connect to Sophos Central using your client ID and client secret, from you API credentials/service principal
    .DESCRIPTION
        Connect to Sophos Central using your client ID and client secret, from you API credentials/service principal

        Sophos customers can connect to their tenant using a client id/secret. Follow Step 1 here to create it
        https://developer.sophos.com/getting-started-tenant

        Sophos partners can use a partner client id/secret to connect to their customer tenants. Follow Step 1 here to create it
        https://developer.sophos.com/getting-started
    .PARAMETER ClientID
        The client ID from the Sophos Central API credential/service principal
    .PARAMETER ClientSecret
        The client secret from the Sophos Central API credential/service principal
    .PARAMETER SecretVault
        Login using a client ID and client secret retrieved from Secret Vault (Microsoft.PowerShell.SecretManagement).
        Setup example found in https://github.com/simon-r-watson/SophosCentral/wiki/AzureKeyVaultExample
    .PARAMETER AzKeyVault
        Use when the Secret Vault is stored in Azure Key Vault
        Uses the Microsoft.PowerShell.SecretManagement and Az.KeyVault modules for retrieving secrets
        Setup example found in https://github.com/simon-r-watson/SophosCentral/wiki/AzureKeyVaultExample
    .PARAMETER AccessTokenOnly
        Internal use (for this module) only. Used to generate a new access token when the current one expires
    .EXAMPLE
        Connect-SophosCentral -ClientID "asdkjsdfksjdf" -ClientSecret (Read-Host -AsSecureString -Prompt "Client Secret:")
    .EXAMPLE
        Connect-SophosCentral -SecretVault -AzKeyVault

        Connect using default values for Secret Key Vault name, Client ID name, and Client Secret name, with secret vault configured to use Azure Key Vault
    .EXAMPLE
        Connect-SophosCentral -SecretVault -AzKeyVault -SecretVaultName 'secrets' -SecretVaultClientIDName 'sophosclientid' -SecretVaultClientSecretName 'sophosclientsecret'

        Connect using custom Secret Vault name, Client ID Name, and Client Secret Name, with secret vault configured to use Azure Key Vault
    .EXAMPLE
        Connect-SophosCentral -SecretVault -SecretVaultName 'secrets' -SecretVaultClientIDName 'sophosclientid' -SecretVaultClientSecretName 'sophosclientsecret'

        Connect using custom Secret Vault name, Client ID Name, and Client Secret Name when using a locally stored Secret Vault (or another Microsoft.PowerShell.SecretManagement integration)
    .LINK
        https://developer.sophos.com/getting-started-tenant
    .LINK
        https://developer.sophos.com/getting-started
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            ParameterSetName = 'StdAuth')]
        [String]$ClientID,

        [Parameter(ParameterSetName = 'StdAuth')]
        [SecureString]$ClientSecret,
        
        [Parameter(ParameterSetName = 'StdAuth')]
        [Switch]$AccessTokenOnly,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'SecretVaultAuth')]
        [Switch]$SecretVault,

        [Parameter(ParameterSetName = 'SecretVaultAuth')]
        [Switch]$AzKeyVault,

        [Parameter(ParameterSetName = 'SecretVaultAuth')]
        [String]$SecretVaultName = 'AzKV',

        [Parameter(ParameterSetName = 'SecretVaultAuth')]
        [String]$SecretVaultClientIDName = 'SophosCentral-Partner-ClientID',

        [Parameter(ParameterSetName = 'SecretVaultAuth')]
        [String]$SecretVaultClientSecretName = 'SophosCentral-Partner-ClientSecret'
    )

    if ($PSVersionTable.PSVersion.Major -lt 7) {
        Write-Warning 'Unsupported version of PowerShell detected'
    }

    if ($PsCmdlet.ParameterSetName -eq 'StdAuth') {
        if ($null -eq $ClientSecret) {
            $ClientSecret = Read-Host -AsSecureString -Prompt 'Client Secret:'
        }

        $loginUri = [System.Uri]::new('https://id.sophos.com/api/v2/oauth2/token')

        $body = @{
            grant_type    = 'client_credentials'
            client_id     = $ClientID
            client_secret = Unprotect-Secret -Secret $ClientSecret
            scope         = 'token'
        }
        try {
            $response = Invoke-WebRequest -Uri $loginUri -Body $body -ContentType 'application/x-www-form-urlencoded' -Method Post -UseBasicParsing
        } catch {
            throw "Error requesting access token: $($_)"
        }
    
        if ($response.Content) {
            $authDetails = $response.Content | ConvertFrom-Json
            $expiresAt = (Get-Date).AddSeconds($authDetails.expires_in - 60)

            if ($AccessTokenOnly -eq $true) {
                $SCRIPT:SophosCentral.access_token = $authDetails.access_token | ConvertTo-SecureString -AsPlainText -Force
                $SCRIPT:SophosCentral.expires_at = $expiresAt
            } else {
                $authDetails | Add-Member -MemberType NoteProperty -Name expires_at -Value $expiresAt
                $authDetails.access_token = $authDetails.access_token | ConvertTo-SecureString -AsPlainText -Force
                $SCRIPT:SophosCentral = $authDetails

                $tenantInfo = Get-SophosCentralTenantInfo
                $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name GlobalEndpoint -Value $tenantInfo.apiHosts.global
                $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name RegionEndpoint -Value $tenantInfo.apiHosts.dataRegion
                $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name TenantID -Value $tenantInfo.id
                $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name IDType -Value $tenantInfo.idType

                $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name client_id -Value $ClientID
                $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name client_secret -Value $ClientSecret
            }
        }
    } elseif ($PsCmdlet.ParameterSetName -eq 'SecretVaultAuth') {
        #verify modules installed
        if ($AzKeyVault -eq $true) {
            $modules = 'Microsoft.PowerShell.SecretManagement', 'Az', 'Az.KeyVault'
        } else {
            $modules = 'Microsoft.PowerShell.SecretManagement'
        }
        foreach ($module in $modules) {
            if (-not(Get-Module $module -ListAvailable)) {
                throw "$module PowerShell Module is not installed"
            }
        }
        #verify secret vault exists
        try {
            $vault = Get-SecretVault -Name $SecretVaultName
        } catch {
            throw "$SecretVaultName is not registered as a Secret Vault in Microsoft.PowerShell.SecretManagement"
        }
        #connect to Azure if using Key Vault for the Secret Vault
        if ($AzKeyVault -eq $true) {
            try { 
                Connect-AzAccount | Out-Null
            } catch {
                throw 'Error connecting to Azure PowerShell'
            }
        }
        #get secrets from vault
        try {
            #try twice, as sometimes the call silently fails
            $clientID = Get-Secret $SecretVaultClientIDName -Vault $SecretVaultName -AsPlainText
            $clientSecret = Get-Secret -Name $SecretVaultClientSecretName -Vault $SecretVaultName

            $clientID = Get-Secret $SecretVaultClientIDName -Vault $SecretVaultName -AsPlainText
            $clientSecret = Get-Secret -Name $SecretVaultClientSecretName -Vault $SecretVaultName
        } catch {
            throw "Error retrieving secrets from Azure Key Vault: $_"
        }
        #connect to Sophos Central
        Connect-SophosCentral -ClientID $clientID -ClientSecret $clientSecret
    }
}
# ./SophosCentral/public/Connect-SophosCentralCustomerTenant.ps1
function Connect-SophosCentralCustomerTenant {
    <#
    .SYNOPSIS
        Connect to a Customer tenant (for Sophos partners only)
    .DESCRIPTION
        Connect to a Customer tenant (for Sophos partners only). You must connect with "Connect-SophosCentral" first using a Partner service principal

        To find the customer tenant ID, use the "Get-SophosCentralCustomerTenants" function
    .PARAMETER CustomerTenantID
        The Customers tenant ID
    .PARAMETER CustomerNameSearch
        Search the tenants you have access to by their name in Sophos Central, use "*" as a wildcard. For example, if you want to connect to "Contoso Legal" `
        you could enter "Contoso*" here.
    .EXAMPLE
        Connect-SophosCentralCustomerTenant -CustomerTenantID "asdkjdfjkwetkjdfs"
    .EXAMPLE
        Connect-SophosCentralCustomerTenant -CustomerTenantID (Get-SophosCentralCustomerTenants | Where-Object {$_.Name -like "*Contoso*"}).ID
    .EXAMPLE
        Connect-SophosCentralCustomerTenant -CustomerNameSearch "Contoso*"
    .LINK
        https://developer.sophos.com/getting-started
    #>

    [CmdletBinding()]
    [Alias('Select-SophosCentralCustomerTenant')]
    param (
        [Parameter(Mandatory = $true,
            ParameterSetName = 'ByID'
        )]
        [string]$CustomerTenantID,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'BySearchString'
        )]
        [string]$CustomerNameSearch
    )
    if ($SCRIPT:SophosCentral.IDType -ne 'partner') {
        throw 'You are not currently logged in using a Sophos Central Partner Service Principal'
    } else {
        Write-Verbose 'currently logged in using a Sophos Central Partner Service Principal'
    }

    if (-not($CustomerTenantID)) {
        $tenantInfo = Get-SophosCentralCustomerTenant | Where-Object {
            $_.Name -like $CustomerNameSearch
        }
        switch ($tenantInfo.count) {
            { $PSItem -eq 1 } { Write-Verbose "1 customer tenants returned: $($tenantInfo.Name)" }
            { $PSItem -gt 1 } { throw "$PSItem customer tenants returned: " + (($tenantInfo).name -join ';') }
            { $PSItem -lt 1 } { throw "$PSItem customer tenants returned" }
        }
    } else {
        $tenantInfo = Get-SophosCentralCustomerTenant | Where-Object {
            $_.ID -eq $CustomerTenantID
        }
    }

    if ($null -ne $tenantInfo) {
        $SCRIPT:SophosCentral.RegionEndpoint = $tenantInfo.apiHost
        if ($SCRIPT:SophosCentral.CustomerTenantID) {
            $SCRIPT:SophosCentral.CustomerTenantID = $tenantInfo.id
        } else {
            $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name CustomerTenantID -Value $tenantInfo.id
        }
        if ($SCRIPT:SophosCentral.CustomerTenantName) {
            $SCRIPT:SophosCentral.CustomerTenantName = $tenantInfo.Name
        } else {
            $SCRIPT:SophosCentral | Add-Member -MemberType NoteProperty -Name CustomerTenantName -Value $tenantInfo.Name
        }
    }
}
# ./SophosCentral/public/Get-SophosCentralAlert.ps1
function Get-SophosCentralAlert {
    <#
    .SYNOPSIS
        Get alerts listed in Sophos Central
    .DESCRIPTION
        Get alerts listed in Sophos Central
    .PARAMETER Product
        Alerts for a product. You can query by product types.
    .PARAMETER Category
        Alert category. You can query by different categories.
    .PARAMETER Severity
        Alerts for a specific severity level. You can query by severity levels.
    .EXAMPLE
        Get-SophosCentralAlert

        Get all active alerts in the tenant
    .EXAMPLE
        Get-SophosCentralAlert -Product 'server' -Severity 'high

        Get all alerts relating to Sophos Central for Server protection wih High severity
    .LINK
        https://developer.sophos.com/docs/common-v1/1/routes/alerts/get
    .LINK
        https://developer.sophos.com/docs/common-v1/1/routes/alerts/search/post
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralAlerts')]
    param (
        [Parameter(ParameterSetName = 'search')]
        [ValidateSet('other', 'endpoint', 'server', 'mobile', 'encryption', 'emailGateway', 'webGateway', 'phishThreat', 'wireless', 'iaas', 'firewall')]
        [string[]]$Product,

        [Parameter(ParameterSetName = 'search')]
        [ValidateSet('azure', 'adSync', 'applicationControl', 'appReputation', 'blockListed', 'connectivity', 'cwg', 'denc', 'downloadReputation', 'endpointFirewall', 'fenc', 'forensicSnapshot', 'general', 'iaas', 'iaasAzure', 'isolation', 'malware', 'mtr', 'mobiles', 'policy', 'protection', 'pua', 'runtimeDetections', 'security', 'smc', 'systemHealth', 'uav', 'uncategorized', 'updating', 'utm', 'virt', 'wireless', 'xgEmail')]
        [string[]]$Category,

        [Parameter(ParameterSetName = 'search')]
        [ValidateSet('low', 'medium', 'high')]
        [string[]]$Severity
    )
    if ($PsCmdlet.ParameterSetName -ne 'search') {
        $uriChild = '/common/v1/alerts'
        $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)
        Invoke-SophosCentralWebRequest -Uri $uri

    } elseif ($PsCmdlet.ParameterSetName -eq 'search') {
        $uriChild = '/common/v1/alerts/search'
        $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)

        $searchParam = @{}
        if ($Product) { $searchParam.Add('product', $Product) }
        if ($Category) { $searchParam.Add('category', $Category) }
        if ($Severity) { $searchParam.Add('severity', $Severity) }

        Invoke-SophosCentralWebRequest -Uri $uri -Body $searchParam -Method Post
    }
}

# ./SophosCentral/public/Get-SophosCentralAllowedItem.ps1
function Get-SophosCentralAllowedItem {
    <#
    .SYNOPSIS
        Get Endpoint allowed Items
    .DESCRIPTION
        Get Endpoint allowed Items
    .EXAMPLE
        Get-SophosCentralAllowedItem
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/settings/allowed-items/get
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralAllowedItems')]
    param (
    )
    
    $uriChild = '/endpoint/v1/settings/allowed-items'
    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Get-SophosCentralBlockedItem.ps1
function Get-SophosCentralBlockedItem {
    <#
    .SYNOPSIS
        Get Endpoint blocked Items
    .DESCRIPTION
        Get Endpoint blocked Items
    .EXAMPLE
        Get-SophosCentralBlockedItem
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/settings/blocked-items/get
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralBlockedItems')]
    param (
    )

    $uriChild = '/endpoint/v1/settings/blocked-items'
    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Get-SophosCentralCustomerTenant.ps1
function Get-SophosCentralCustomerTenant {
    <#
    .SYNOPSIS
        List Sophos Central customer tenants that can be connected too (for Sophos partners only)
    .DESCRIPTION
        List Sophos Central customer tenants that can be connected too (for Sophos partners only)
    .EXAMPLE
        Get-SophosCentralCustomerTenant
    .LINK
        https://developer.sophos.com/docs/partner-v1/1/routes/tenants/get
    .LINK
        https://developer.sophos.com/getting-started
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralCustomerTenants')]
    param (
    )
    
    if ((Test-SophosPartner) -eq $false) {
        throw 'You are not currently logged in using a Sophos Central Partner Service Principal'
    }

    try {
        $header = Get-SophosCentralAuthHeader -PartnerInitial
    } catch {
        throw $_
    }
    $uri = [System.Uri]::New('https://api.central.sophos.com/partner/v1/tenants?pageTotal=true')
    Invoke-SophosCentralWebRequest -Uri $uri -CustomHeader $header
}
# ./SophosCentral/public/Get-SophosCentralEndpoint.ps1
function Get-SophosCentralEndpoint {
    <#
    .SYNOPSIS
        Get Endpoints in Sophos Central (Workstations, Servers)
    .DESCRIPTION
        Get Endpoints in Sophos Central (Workstations, Servers)
    .EXAMPLE
        Get-SophosCentralEndpoint
        List all endpoints in the tenant
    .EXAMPLE
        Get-SophosCentralEndpoint -HealthStatus 'bad'
        List all endpoints with a bad health status
    .EXAMPLE
        Get-SophosCentralEndpoint -TamperProtectionEnabled $false
        List all endpoints with tamper protection disabled
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/%7BendpointId%7D/get
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralEndpoints')]
    param (
        [ValidateSet('bad', 'good', 'suspicious', 'unknown')]
        [string]$HealthStatus,

        [ValidateSet('computer', 'server', 'securityVm')]
        [string]$Type,

        [System.Boolean]$TamperProtectionEnabled,

        [ValidateSet('creatingWhitelist', 'installing', 'locked', 'notInstalled', 'registering', 'starting', 'stopping', 'unavailable', 'uninstalled', 'unlocked')]
        [string]$LockdownStatus,

        [ValidateSet('isolated', 'notIsolated')]
        [string]$IsolationStatus,

        [string]$HostnameContains,

        [string]$IpAddresses,

        [string]$MacAddresses 
    )

    $uriBuilder = [System.UriBuilder]::New($SCRIPT:SophosCentral.RegionEndpoint + '/endpoint/v1/endpoints')

    foreach ($param in $PsBoundParameters.Keys) {
        if ($null -ne $PsBoundParameters[$param]) {
            $queryPart = $param + '=' + $PsBoundParameters[$param]
            if (($null -eq $uriBuilder.Query) -or ($uriBuilder.Query.Length -le 1 )) {
                $uriBuilder.Query = $queryPart
            } else {
                $uriBuilder.Query = $uriBuilder.Query.Substring(1) + '&' + $queryPart
            }
        }
    }

    $uri = [System.Uri]::New($uriBuilder.Uri)
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Get-SophosCentralEndpointGroup.ps1
function Get-SophosCentralEndpointGroup {
    <#
    .SYNOPSIS
        Get Endpoint groups in the directory
    .DESCRIPTION
        Get Endpoint groups in the directory
    .EXAMPLE
        Get-SophosCentralEndpointGroup
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoint-groups/get
    #>

    [CmdletBinding()]
    param (
    )

    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + '/endpoint/v1/endpoint-groups')
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Get-SophosCentralEndpointInstallerLink.ps1
function Get-SophosCentralEndpointInstallerLink {
    <#
    .SYNOPSIS
        Get all the endpoint installer links for a tenant.
    .DESCRIPTION
        Get all the endpoint installer links for a tenant.
    .EXAMPLE
        Get-SophosCentralEndpointInstallerLink
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/downloads/get
    #>

    [CmdletBinding()]
    param (
    )

    $uriChild = '/endpoint/v1/downloads'
    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Get-SophosCentralEndpointPolicy.ps1
function Get-SophosCentralEndpointPolicy {
    <#
    .SYNOPSIS
        Get Policies
    .DESCRIPTION
        Get Policies
    .EXAMPLE
        Get-SophosCentralEndpointPolicy -All

        Get all policies
    .EXAMPLE
        Get-SophosCentralEndpointPolicy -BasePolicy

        Get base policies
    .LINK
        https://developer.sophos.com/endpoint-policies#get-all-policies
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralEndpointPolicies')]
    param (
        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'ID')]
        [Alias('ID')]
        [string]$PolicyId,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'Base')]
        [switch]$BasePolicy,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'All')]
        [switch]$All
    )

    if ($BasePolicy) {
        $PolicyId = 'base'
    }

    $uriChild = '/endpoint/v1/policies'
    if (-not([string]::IsNullOrEmpty($PolicyId))) {
        $uriChild = "$($uriChild)/$($PolicyId)"
    }
    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Get-SophosCentralEndpointTamperProtection.ps1
function Get-SophosCentralEndpointTamperProtection {
    <#
    .SYNOPSIS
        Get Tamper Protection Status
    .DESCRIPTION
        Get Tamper Protection Status
    .PARAMETER EndpointID
        The ID of the Endpoint. Use Get-SophosCentralEndpoints to list them
    .EXAMPLE
        Get-SophosCentralEndpointTamperProtection -EndpointID '23a920fa-9a34-4869-bc3d-a1626e50f670'
    .EXAMPLE
        Get-SophosCentralEndpointTamperProtection -EndpointID (Get-SophosCentralEndpoints).ID
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/%7BendpointId%7D/tamper-protection/get
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [string[]]$EndpointID
    )
    begin {
        $uriChild = '/endpoint/v1/endpoints/{0}/tamper-protection'
        $uriString = $SCRIPT:SophosCentral.RegionEndpoint + $uriChild
    }
    process {
        foreach ($endpoint in $EndpointID) {
            $uri = [System.Uri]::New($uriString -f $endpoint)
            Invoke-SophosCentralWebRequest -Uri $uri
        }
    }
}
# ./SophosCentral/public/Get-SophosCentralPartnerAdmin.ps1
function Get-SophosCentralPartnerAdmin {
    <#
    .SYNOPSIS
        List all partner admins.
    .DESCRIPTION
        List all partner admins.
    .EXAMPLE
        Get-SophosCentralPartnerAdmin
    .LINK
        https://developer.sophos.com/docs/partner-v1/1/routes/admins/get
    #>

    [CmdletBinding()]
    param (
    )

    if ((Test-SophosPartner) -eq $false) {
        throw 'You are not currently logged in using a Sophos Central Partner Service Principal'
    }

    try {
        $header = Get-SophosCentralAuthHeader -PartnerInitial
    } catch {
        throw $_
    }
    
    $uri = [System.Uri]::New('https://api.central.sophos.com/partner/v1/admins')
    Invoke-SophosCentralWebRequest -Uri $uri -CustomHeader $header
}
# ./SophosCentral/public/Get-SophosCentralPartnerBilling.ps1
function Get-SophosCentralPartnerBilling {
    <#
    .SYNOPSIS
        Get billing usage report
    .DESCRIPTION
        Get billing usage report
    .EXAMPLE
        Get-SophosCentralPartnerBilling -LastMonth
    .EXAMPLE
        Get-SophosCentralPartnerBilling -Month 5 -Year 2022
    .LINK
        https://developer.sophos.com/docs/partner-v1/1/routes/billing/usage/%7Byear%7D/%7Bmonth%7D/get
    #>

    [CmdletBinding()]
    param (
        [Parameter(Position = 0,
            Mandatory = $true,
            ParameterSetName = 'Custom')]
        [ValidateRange(1, 12)]
        [int]$Month,
        
        [Parameter(Position = 1,
            Mandatory = $true,
            ParameterSetName = 'Custom')]
        [ValidateRange(2000, 2050)]
        [int]$Year,

        [Parameter(Position = 2,
            ParameterSetName = 'LastMonth')]
        [switch]$LastMonth
    )

    if ((Test-SophosPartner) -eq $false) {
        throw 'You are not currently logged in using a Sophos Central Partner Service Principal'
    }

    if ($LastMonth) {
        $date = (Get-Date).AddMonths(-1)
        $Year = $date.Year
        $Month = $date.Month
    }

    try {
        $header = Get-SophosCentralAuthHeader -PartnerInitial
    } catch {
        throw $_
    }
    
    $uri = [System.Uri]::New("https://api.central.sophos.com/partner/v1/billing/usage/$year/$month")
    Invoke-SophosCentralWebRequest -Uri $uri -CustomHeader $header
}
# ./SophosCentral/public/Get-SophosCentralTenantInfo.ps1
function Get-SophosCentralTenantInfo {
    <#
    .SYNOPSIS
        Get information of the current tenant
    .DESCRIPTION
        Get information of the current tenant
    .EXAMPLE
        Get-SophosCentralTenantInfo
    .LINK
        https://developer.sophos.com/docs/whoami-v1/1/routes/get
    #>

    $uri = [System.Uri]::New('https://api.central.sophos.com/whoami/v1')
    $header = Get-SophosCentralAuthHeader -Initial
    Invoke-SophosCentralWebRequest -Uri $uri -CustomHeader $header
}
# ./SophosCentral/public/Get-SophosCentralUser.ps1
function Get-SophosCentralUser {
    <#
    .SYNOPSIS
        Get users listed in Sophos Central
    .DESCRIPTION
        Get users listed in Sophos Central
    .EXAMPLE
        Get-SophosCentralUser
    .LINK
        https://developer.sophos.com/docs/common-v1/1/routes/directory/users/get
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralUsers')]
    param (
    )

    $uriChild = '/common/v1/directory/users'
    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Get-SophosCentralUserGroup.ps1
function Get-SophosCentralUserGroup {
    <#
    .SYNOPSIS
        Get User Groups listed in Sophos Central
    .DESCRIPTION
        Get User Groups listed in Sophos Central
    .EXAMPLE
        Get-SophosCentralUserGroup
    .LINK
        https://developer.sophos.com/docs/common-v1/1/routes/directory/user-groups/get
    #>

    [CmdletBinding()]
    [Alias('Get-SophosCentralUserGroups')]
    param (
    )
    
    $uriChild = '/common/v1/directory/user-groups'
    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)
    Invoke-SophosCentralWebRequest -Uri $uri
}
# ./SophosCentral/public/Invoke-SophosCentralEndpointInstallerDownload.ps1
function Invoke-SophosCentralEndpointInstallerDownload {
    <#
    .SYNOPSIS
        Download the endpoint installer.
    .DESCRIPTION
        Download the endpoint installer.
    .EXAMPLE
        Invoke-SophosCentralEndpointInstallerDownload -RequestedProduct interceptX -Platform windows -FilePath 'C:\SophosSetup.exe'
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/downloads/get
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [ValidateSet('coreAgent', 'interceptX', 'xdr', 'endpointProtection', 'deviceEncryption', 'mtr', 'mtd', 'ztna')]
        [string]$RequestedProduct = 'interceptX',

        [Parameter(Mandatory = $false)]
        [ValidateSet('windows', 'linux', 'macOS')]
        [string]$Platform = 'windows',

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
                if ($_ | Test-Path) {
                    throw 'File already exists'
                }
                return $true
            })]
        [System.IO.FileInfo]$FilePath
    )
    $links = Get-SophosCentralEndpointInstallerLink
    foreach ($installers in $links.installers) {
        if (($installers.supportedProducts -contains $RequestedProduct) -and ($installers.platform -eq $Platform)) {
            Invoke-WebRequest -Uri $installers.downloadUrl -UseBasicParsing -OutFile $FilePath
        }
    }
    if (-not(Test-Path $FilePath)) {
        Write-Error 'Installer was not downloaded. Ensure you selected the correct product/platform'
    }
}
# ./SophosCentral/public/Invoke-SophosCentralEndpointScan.ps1
function Invoke-SophosCentralEndpointScan {
    <#
    .SYNOPSIS
        Trigger a scan on Endpoints in Sophos Central
    .DESCRIPTION
        Trigger a scan on Endpoints in Sophos Central
    .PARAMETER EndpointID
        The ID of the Endpoint. Use Get-SophosCentralEndpoints to list them
    .EXAMPLE
        Invoke-SophosCentralEndpointScan -EndpointID "6d41e78e-0360-4de3-8669-bb7b797ee515"
    .EXAMPLE
        Invoke-SophosCentralEndpointScan -EndpointID (Get-SophosCentralEndpoint).ID
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/%7BendpointId%7D/scans/post
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [string[]]$EndpointID
    )
    begin {
        $uriChild = '/endpoint/v1/endpoints/{0}/scans'
        $uriString = $SCRIPT:SophosCentral.RegionEndpoint + $uriChild
    }
    process {
        foreach ($endpoint in $EndpointID) {
            $uri = [System.Uri]::New($uriString -f $endpoint)
            Invoke-SophosCentralWebRequest -Uri $uri -Method Post
        }
    }
}
# ./SophosCentral/public/Invoke-SophosCentralEndpointUpdate.ps1
function Invoke-SophosCentralEndpointUpdate {
    <#
    .SYNOPSIS
        Trigger an update on an Endpoint in Sophos Central
    .DESCRIPTION
        Trigger an update on an Endpoint in Sophos Central
    .PARAMETER EndpointID
        The ID of the Endpoint. Use Get-SophosCentralEndpoints to list them
    .EXAMPLE
        Invoke-SophosCentralEndpointUpdate -EndpointID "6d41e78e-0360-4de3-8669-bb7b797ee515"
    .EXAMPLE
        Invoke-SophosCentralEndpointUpdate -EndpointID (Get-SophosCentralEndpoint).ID
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/%7BendpointId%7D/update-checks/post
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [string[]]$EndpointID
    )
    begin {
        $uriChild = '/endpoint/v1/endpoints/{0}/update-checks'
        $uriString = $SCRIPT:SophosCentral.RegionEndpoint + $uriChild
    }
    process {
        foreach ($endpoint in $EndpointID) {
            $uri = [System.Uri]::New($uriString -f $endpoint)
            Invoke-SophosCentralWebRequest -Uri $uri -Method Post
        }
    }
}
# ./SophosCentral/public/New-SophosCentralCustomerTenant.ps1
function New-SophosCentralCustomerTenant {
    <#
    .SYNOPSIS
        Create a new tenant.
    .DESCRIPTION
        Create a new tenant.
    .PARAMETER Name
        Name of the tenant
    .EXAMPLE
        New-SophosCentralCustomerTenant -Name 'Test User' -DataGeography AU -BillingType 'trial' -FirstName 'John' -LastName 'Smith' -Email 'test@contoso.com' -Phone '+612000000' -Address1 '31 Bligh St' -City 'Sydney' -CountryCode 'AU' -PostalCode '2000' -State 'New South Whales'
    .LINK
        https://developer.sophos.com/docs/partner-v1/1/routes/tenants/post
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('US', 'IE', 'DE', 'CA', 'AU', 'JP')]
        [string]$DataGeography,

        [Parameter(Mandatory = $true)]
        [ValidateSet('trial', 'usage')]
        [string]$BillingType,
        
        [Parameter(Mandatory = $true)]
        [string]$FirstName,
        
        [Parameter(Mandatory = $true)]
        [string]$LastName,
        
        [Parameter(Mandatory = $true)]
        [string]$Email,
        
        [Parameter(Mandatory = $true)]
        [string]$Phone,
        
        [Parameter(Mandatory = $true)]
        [string]$Address1,
        
        [string]$Address2,
        
        [string]$Address3,
        
        [Parameter(Mandatory = $true)]
        [string]$City,
        
        [string]$State,
        
        [Parameter(Mandatory = $true)]
        [string]$CountryCode,
        
        [Parameter(Mandatory = $true)]
        [string]$PostalCode,
        
        [switch]$Force
    )

    if ((Test-SophosPartner) -eq $false) {
        throw 'You are not currently logged in using a Sophos Central Partner Service Principal'
    }

    $uri = [System.Uri]::New('https://api.central.sophos.com/partner/v1/tenants')

    $body = @{
        name          = $Name
        dataGeography = $DataGeography
        billingType   = $billingType
        contact       = @{
            firstName = $firstName
            lastName  = $lastName
            email     = $email
            phone     = $Phone
            address   = @{
                address1    = $address1
                city        = $City
                countryCode = $countryCode
                postalCode  = $postalCode
            }
        }
    }
    if ($address2) { $body['contact']['address'].Add('address2', $address2) }
    if ($address3) { $body['contact']['address'].Add('address3', $address3) }
    if ($state) { $body['contact']['address'].Add('state', $state) }

    try {
        $header = Get-SophosCentralAuthHeader -PartnerInitial
    } catch {
        throw $_
    }

    if ($Force -or $PSCmdlet.ShouldProcess($Name, ($body.keys -join ', '))) {
        Invoke-SophosCentralWebRequest -Uri $uri -Method Post -Body $body -CustomHeader $header
    }
}
# ./SophosCentral/public/New-SophosCentralUser.ps1
function New-SophosCentralUser {
    <#
    .SYNOPSIS
        Create a new user in Sophos Central
    .DESCRIPTION
        Create a new user in Sophos Central
    .PARAMETER Name
        This parameter is mandatory
    .PARAMETER GroupIDs
        A list/array of group ID's to add them to. To determine the ID of the groups use Get-SophosCentralUserGroups
    .EXAMPLE
        New-SophosCentralUser -Name "John Smith" -FirstName "John" -LastName "Smith" -Email "jsmith@contoso.com"
    .LINK
        https://developer.sophos.com/docs/common-v1/1/routes/directory/users/post
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [string]$FirstName,
        [string]$LastName,
        [string]$Email,
        [string]$ExchangeLogin,
        [string[]]$GroupIDs,
        [switch]$Force
    )

    $uriChild = '/common/v1/directory/users'
    $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + $uriChild)

    $body = @{
        name = $Name
    }
    if ($firstName) { $body.Add('firstName', $FirstName) }
    if ($LastName) { $body.Add('lastName', $LastName) }
    if ($email) { $body.Add('email', $email) }
    if ($exchangeLogin) { $body.Add('exchangeLogin', $exchangeLogin) }
    if ($groupIds) { $body.Add('groupIds', $groupIds) }

    if ($Force -or $PSCmdlet.ShouldProcess($EndpointID, ($body.keys -join ', '))) {
        Invoke-SophosCentralWebRequest -Uri $uri -Method Post -Body $body
    }
}
# ./SophosCentral/public/Set-SophosCentralAlertAction.ps1
function Set-SophosCentralAlertAction {
    <#
    .SYNOPSIS
        Update an alert in Sophos Central
    .DESCRIPTION
        Update an alert in Sophos Central
    .PARAMETER AlertID
        The ID of the alert. Use Get-SophosCentralAlerts to list them
    .PARAMETER Action
        The alert action to perform. To get the possible actions for an alert, check the results from Get-SophosCentralAlerts

        The action must be in the same capitalization as listed, otherwise it will fail

        Possible options: 'acknowledge', 'cleanPua', 'cleanVirus', 'authPua', 'clearThreat', 'clearHmpa', 'sendMsgPua', 'sendMsgThreat'
    .PARAMETER Message
        Message to send for the action.
    .EXAMPLE
        Set-SophosCentralAlertAction -AlertID "6d41e78e-0360-4de3-8669-bb7b797ee515" -Action "clearThreat"
    .LINK
        https://developer.sophos.com/docs/common-v1/1/routes/alerts/%7BalertId%7D/actions/post
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory = $true)]
        [Alias('ID')]
        [string[]]$AlertID,

        [Parameter(Mandatory = $true)]
        [ValidateSet('acknowledge', 'cleanPua', 'cleanVirus', 'authPua', 'clearThreat', 'clearHmpa', 'sendMsgPua', 'sendMsgThreat')]
        [string]$Action,

        [string]$Message,

        [switch]$Force
    )
    begin {        
        $uriChild = '/common/v1/alerts/{0}/actions'
        $uriString = $SCRIPT:SophosCentral.RegionEndpoint + $uriChild
    }
    process {
        foreach ($alert in $AlertID) {
            
            $uri = [System.Uri]::New($uriString -f $alert)
            $body = @{
                action = $Action
            }
            if ($Message) {
                $body.Add('message', $Message)
            }
            if ($Force -or $PSCmdlet.ShouldProcess($alert, $Action)) {
                Invoke-SophosCentralWebRequest -Uri $uri -Method Post -Body $body
            }
            
        }
    }
}
# ./SophosCentral/public/Set-SophosCentralEndpointIsolation.ps1
function Set-SophosCentralEndpointIsolation {
    <#
    .SYNOPSIS
        Turn on or off endpoint isolation for multiple endpoints.
    .DESCRIPTION
        Turn on or off endpoint isolation for multiple endpoints.
    .PARAMETER EndpointID
        The ID of the Endpoint. Use Get-SophosCentralEndpoints to list them
    .PARAMETER Enabled
        Use $true to enable isolation, $false to disable
    .PARAMETER Comment
        Reason the endpoints should be isolated or not
    .EXAMPLE
        Set-SophosCentralEndpointIsolation -EndpointID '23a920fa-9a34-4869-bc3d-a1626e50f670' -Enabled $false -Comment "disable iso"
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/isolation/post
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [string[]]$EndpointID,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'Update Status')]
        [System.Boolean]$Enabled,

        [Parameter(Mandatory = $true)]
        [string]$Comment,

        [switch]$Force
    )
    begin {
        $uri = [System.Uri]::New($SCRIPT:SophosCentral.RegionEndpoint + '/endpoint/v1/endpoints/isolation')
    }
    process {
        
        $body = @{
            enabled = $Enabled
            comment = $Comment
            ids     = @()
        }
        foreach ($endpoint in $EndpointID) {
            $body['ids'] += $endpoint
        }
        
        if ($Force -or $PSCmdlet.ShouldProcess('isolation', ($EndpointID -join ', '))) {
            Invoke-SophosCentralWebRequest -Uri $uri -Method Post -Body $body
        } 
    }
    
}
# ./SophosCentral/public/Set-SophosCentralEndpointTamperProtection.ps1
function Set-SophosCentralEndpointTamperProtection {
    <#
    .SYNOPSIS
        Update Tamper Protection settings
    .DESCRIPTION
        Update Tamper Protection settings
    .PARAMETER EndpointID
        The ID of the Endpoint. Use Get-SophosCentralEndpoints to list them
    .PARAMETER Enabled
        Use $true to enable Tamper Protection, $false to disable
    .PARAMETER RegeneratePassword
        Use this switch to generate a new Tamper Protection password
    .EXAMPLE
        Set-SophosCentralEndpointTamperProtection -EndpointID '23a920fa-9a34-4869-bc3d-a1626e50f670' -Enabled $false
    .EXAMPLE
        Set-SophosCentralEndpointTamperProtection -EndpointID '23a920fa-9a34-4869-bc3d-a1626e50f670' -RegeneratePassword
    .LINK
        https://developer.sophos.com/docs/endpoint-v1/1/routes/endpoints/%7BendpointId%7D/tamper-protection/post
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [string[]]$EndpointID,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'Update Status')]
        [System.Boolean]$Enabled,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'Regenerate Password')]
        [switch]$RegeneratePassword,

        [switch]$Force
    )
    begin {
        $uriChild = '/endpoint/v1/endpoints/{0}/tamper-protection'
        $uriString = $SCRIPT:SophosCentral.RegionEndpoint + $uriChild
    }
    process {
        foreach ($endpoint in $EndpointID) {
            $uri = [System.Uri]::New($uriString -f $endpoint)
            $body = @{}
            if ($Enabled) { $body.Add('enabled', $Enabled) }
            if ($RegeneratePassword) { $body.Add('regeneratePassword', $RegeneratePassword) }
            
            if ($Force -or $PSCmdlet.ShouldProcess($EndpointID, ($body.keys -join ', '))) {
                Invoke-SophosCentralWebRequest -Uri $uri -Method Post -Body $body
            }
        }
    }
}