DefenderAPI.psm1

$script:ModuleRoot = $PSScriptRoot
$script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\DefenderAPI.psd1").ModuleVersion

# Detect whether at some level dotsourcing was enforced
$script:doDotSource = Get-PSFConfigValue -FullName DefenderAPI.Import.DoDotSource -Fallback $false
if ($DefenderAPI_dotsourcemodule) { $script:doDotSource = $true }

<#
Note on Resolve-Path:
All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator.
This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS.
Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist.
This is important when testing for paths.
#>


# Detect whether at some level loading individual module files, rather than the compiled module was enforced
$importIndividualFiles = Get-PSFConfigValue -FullName DefenderAPI.Import.IndividualFiles -Fallback $false
if ($DefenderAPI_importIndividualFiles) { $importIndividualFiles = $true }
if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true }
if ("<was compiled>" -eq '<was not compiled>') { $importIndividualFiles = $true }
    
function Import-ModuleFile
{
    <#
        .SYNOPSIS
            Loads files into the module on module import.
         
        .DESCRIPTION
            This helper function is used during module initialization.
            It should always be dotsourced itself, in order to proper function.
             
            This provides a central location to react to files being imported, if later desired
         
        .PARAMETER Path
            The path to the file to load
         
        .EXAMPLE
            PS C:\> . Import-ModuleFile -File $function.FullName
     
            Imports the file stored in $function according to import policy
    #>

    [CmdletBinding()]
    Param (
        [string]
        $Path
    )
    
    $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath
    if ($doDotSource) { . $resolvedPath }
    else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) }
}

#region Load individual files
if ($importIndividualFiles)
{
    # Execute Preimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) {
        . Import-ModuleFile -Path $path
    }
    
    # Import all internal functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Import all public functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Execute Postimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) {
        . Import-ModuleFile -Path $path
    }
    
    # End it here, do not load compiled code below
    return
}
#endregion Load individual files

#region Load compiled code
<#
This file loads the strings documents from the respective language folders.
This allows localizing messages and errors.
Load psd1 language files for each language you wish to support.
Partial translations are acceptable - when missing a current language message,
it will fallback to English or another available language.
#>

Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'DefenderAPI' -Language 'en-US'

enum SubnetCategory {
    Unknown = 0
    Corporate = 1
    Administrative = 2
    Risky = 3
    VPN = 4
    CloudProvider = 5
    Other = 6
}

<#
# Example:
Register-PSFTeppScriptblock -Name "DefenderAPI.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' }
#>


<#
# Example:
Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name DefenderAPI.alcohol
#>

# Register-PSFTeppArgumentCompleter -Command Connect-DefenderAPIService -Parameter Service -Name DefenderAPI.Service

function ConvertFrom-RestSubnet {
    <#
    .SYNOPSIS
        Converts subnet objects to look nice.
     
    .DESCRIPTION
        Converts subnet objects to look nice.
     
    .PARAMETER InputObject
        The rest response representing a subnet
     
    .EXAMPLE
        PS C:\> Invoke-RestRequest -Path "subnet/$idString" -ErrorAction Stop | ConvertFrom-RestSubnet
 
        Retrieves the specified subnet and converts it into something userfriendly
    #>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        $InputObject
    )

    process {
        if (-not $InputObject) { return }

        [PSCustomObject]@{
            PSTypeName      = 'DefenderAPI.Mdca.Subnet'
            ID              = $InputObject._id
            Name            = $InputObject.Name
            Subnets         = $InputObject.subnets.originalString
            SubnetsOriginal = $InputObject.subnets
            Location        = $InputObject.location
            Organization    = $InputObject.organization
            Tags            = $InputObject.tags.name
            TagsOriginal    = $InputObject.tags
            Category        = $InputObject.Category -as [SubnetCategory]
            LastModified    = $InputObject.LastModified
        }
    }
}

function Set-MdcaToken {
    <#
    .SYNOPSIS
        Helper function that injects the MDCA ApiKey Token into the EntraAuth token cache.
     
    .DESCRIPTION
        Helper function that injects the MDCA ApiKey Token into the EntraAuth token cache.
        By default, EntraAuth only supports authentication flows via entra (hence the module name).
        With this command we can inject a fake EntraAuth token object that works for Invoke-EntraRequest.
     
    .PARAMETER Token
        The MDCA ApiKey / Token to inject into EntraAuth.
     
    .PARAMETER TenantName
        Name of the tenant, the token applies to.
        Used for building the API Service Url.
     
    .EXAMPLE
        PS C:\> Set-MdcaToken -Token $MdcaToken -TenantName $TenantName
         
        Injects the MDCA ApiKey Token into the EntraAuth token cache.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [SecureString]
        $Token,

        [string]
        $TenantName
    )
    process {
        $fakeToken = [PSCustomObject]@{
            ServiceUrl = "https://$TenantName.portal.cloudappsecurity.com/api/v1"
            ApiKey     = $Token
            Service    = 'DefenderAPI.MDCA'
        }
        Add-Member -InputObject $fakeToken -MemberType ScriptMethod -Name GetHeader -Value {
            return @{
                Authorization = "Token {0}" -f ([PSCredential]::new("Whatever", $this.ApiKey).GetNetworkCredential().Password)
                'content-type' = 'application/json'
            }
        }

        & (Get-Module EntraAuth) {
            param ($Token)

            $script:_EntraTokens['DefenderAPI.MDCA'] = $Token
        } $fakeToken
    }
}

function ConvertFrom-AdvancedQuery {
    <#
    .SYNOPSIS
        Converts the output of an Advanced Hunting Query into something PowerShell compatible.
     
    .DESCRIPTION
        Converts the output of an Advanced Hunting Query into something PowerShell compatible.
     
    .PARAMETER Result
        The result of the Invoke-MdeAdvancedQuery command.
     
    .EXAMPLE
        PS C:\> Invoke-MdeRequest -Path $__path -Method post -Body $__body -Query $__query -RequiredScopes 'AdvancedQuery.Read' | ConvertFrom-AdvancedQuery
 
        Processes the return values provided by the advanced query.
    #>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [AllowNull()]
        $Result
    )

    begin {
        $typeMapping = @{
            String   = '<direct>'
            Double   = '<direct>'
            SByte    = { $_ -as [bool] }
            DateTime = '<direct>'
            Object   = '<direct>'
        }
    }
    process {
        if (-not $Result) { return }

        $properties = foreach ($item in $Result.Schema) {
            if ((-not $typeMapping[$item.Type]) -or $typeMapping[$item.Type] -eq '<direct>') {
                $item.Name
                continue
            }
            @{
                Name       = $item.Name
                Expression = $typeMapping[$item.Type]
            }
        }
        $Result.Results | Select-Object $properties | ForEach-Object {
            $_.PSObject.TypeNames.Insert(0, 'DefenderAPI.AdvancedQuery.Result')
            $_
        }
    }
}

function ConvertTo-Hashtable {
    <#
    .SYNOPSIS
        Converts input objects into hashtables.
     
    .DESCRIPTION
        Converts input objects into hashtables.
        Allows explicitly including some properties only and remapping key-names as required.
     
    .PARAMETER Include
        Only select the specified properties.
     
    .PARAMETER Mapping
        Remap hashtable/property keys.
        This allows you to rename parameters before passing them through to other commands.
        Example:
        @{ Select = '$select' }
        This will map the "Select"-property/key on the input object to be '$select' on the output item.
     
    .PARAMETER InputObject
        The object to convert.
     
    .EXAMPLE
        PS C:\> $__body = $PSBoundParameters | ConvertTo-Hashtable -Include Name, UserID -Mapping $__mapping
 
        Converts the object $PSBoundParameters into a hashtable, including the keys "Name" and "UserID" and remapping them as specified in $__mapping
    #>

    [OutputType([hashtable])]
    [CmdletBinding()]
    param (
        [AllowEmptyCollection()]
        [string[]]
        $Include,

        [Hashtable]
        $Mapping = @{ },

        [Parameter(ValueFromPipeline = $true)]
        $InputObject
    )

    process {
        $result = @{ }
        if ($InputObject -is [System.Collections.IDictionary]) {
            foreach ($pair in $InputObject.GetEnumerator()) {
                if ($pair.Key -notin $Include) { continue }
                if ($Mapping[$pair.Key]) { $result[$Mapping[$pair.Key]] = $pair.Value }
                else { $result[$pair.Key] = $pair.Value }
            }
        }
        else {
            foreach ($property in $InputObject.PSObject.Properties) {
                if ($property.Name -notin $Include) { continue }
                if ($Mapping[$property.Name]) { $result[$Mapping[$property.Name]] = $property.Value }
                else { $result[$property.Name] = $property.Value }
            }
        }
        $result
    }
}

function ConvertTo-QueryString {
    <#
    .SYNOPSIS
        Convert conditions in a hashtable to a Query string to append to a webrequest.
     
    .DESCRIPTION
        Convert conditions in a hashtable to a Query string to append to a webrequest.
     
    .PARAMETER QueryHash
        Hashtable of query modifiers - usually filter conditions - to include in a web request.
     
    .EXAMPLE
        PS C:\> ConvertTo-QueryString -QueryHash $Query
 
        Converts the conditions in the specified hashtable to a Query string to append to a webrequest.
    #>

    [OutputType([string])]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Hashtable]
        $QueryHash
    )

    process {
        $elements = foreach ($pair in $QueryHash.GetEnumerator()) {
            '{0}={1}' -f $pair.Name, ($pair.Value -join ",")
        }
        '?{0}' -f ($elements -join '&')
    }
}

function Invoke-TerminatingException
{
<#
    .SYNOPSIS
        Throw a terminating exception in the context of the caller.
     
    .DESCRIPTION
        Throw a terminating exception in the context of the caller.
        Masks the actual code location from the end user in how the message will be displayed.
     
    .PARAMETER Cmdlet
        The $PSCmdlet variable of the calling command.
     
    .PARAMETER Message
        The message to show the user.
     
    .PARAMETER Exception
        A nested exception to include in the exception object.
     
    .PARAMETER Category
        The category of the error.
     
    .PARAMETER ErrorRecord
        A full error record that was caught by the caller.
        Use this when you want to rethrow an existing error.
     
    .EXAMPLE
        PS C:\> Invoke-TerminatingException -Cmdlet $PSCmdlet -Message 'Unknown calling module'
     
        Terminates the calling command, citing an unknown caller.
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        $Cmdlet,
        
        [string]
        $Message,
        
        [System.Exception]
        $Exception,
        
        [System.Management.Automation.ErrorCategory]
        $Category = [System.Management.Automation.ErrorCategory]::NotSpecified,
        
        [System.Management.Automation.ErrorRecord]
        $ErrorRecord
    )
    
    process{
        if ($ErrorRecord -and -not $Message) {
            $Cmdlet.ThrowTerminatingError($ErrorRecord)
        }
        
        $exceptionType = switch ($Category) {
            default { [System.Exception] }
            'InvalidArgument' { [System.ArgumentException] }
            'InvalidData' { [System.IO.InvalidDataException] }
            'AuthenticationError' { [System.Security.Authentication.AuthenticationException] }
            'InvalidOperation' { [System.InvalidOperationException] }
        }
        
        
        if ($Exception) { $newException = $Exception.GetType()::new($Message, $Exception) }
        elseif ($ErrorRecord) { $newException = $ErrorRecord.Exception.GetType()::new($Message, $ErrorRecord.Exception) }
        else { $newException = $exceptionType::new($Message) }
        $record = [System.Management.Automation.ErrorRecord]::new($newException, (Get-PSCallStack)[1].FunctionName, $Category, $Target)
        $Cmdlet.ThrowTerminatingError($record)
    }
}

function Assert-DefenderAPIConnection {
    <#
    .SYNOPSIS
        Asserts a connection has been established.
     
    .DESCRIPTION
        Asserts a connection has been established.
        Fails the calling command in a terminating exception if not connected yet.
         
    .PARAMETER Service
        The service to which a connection needs to be established.
     
    .PARAMETER Cmdlet
        The $PSCmdlet variable of the calling command.
        Used to execute the terminating exception in the caller scope if needed.
 
    .PARAMETER RequiredScopes
        Scopes needed, for better error messages.
     
    .EXAMPLE
        PS C:\> Assert-DefenderAPIConnection -Service 'Endpoint' -Cmdlet $PSCmdlet
     
        Silently does nothing if already connected to the specified defender service.
        Kills the calling command if not yet connected.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Service,
        
        [Parameter(Mandatory = $true)]
        $Cmdlet,
        
        [AllowEmptyCollection()]
        [string[]]
        $RequiredScopes
    )
    
    begin {
        $serviceMap = @{
            Endpoint = 'DefenderAPI.Endpoint'
            Security = 'DefenderAPI.Security'
            MDCA     = 'DefenderAPI.MDCA'
        }
    }
    process {
        $actualServiceName = $Service
        if ($serviceMap[$Service]) { $actualServiceName = $serviceMap[$Service] }
        if (Get-EntraToken -Service $actualServiceName) { return }
        
        $message = "Not connected yet! Use Connect-DefenderAPIService to establish a connection to '$Service' first."
        if ($RequiredScopes) { $message = $message + " Scopes required for this call: $($RequiredScopes -join ', ')" }
        Invoke-TerminatingException -Cmdlet $Cmdlet -Message $message -Category ConnectionError
    }
}

function Connect-DefenderAPI {
    <#
    .SYNOPSIS
        Establish a connection to the defender APIs.
     
    .DESCRIPTION
        Establish a connection to the defender APIs.
        Prerequisite before executing any requests / commands.
     
    .PARAMETER ClientID
        ID of the registered/enterprise application used for authentication.
     
    .PARAMETER TenantID
        The ID of the tenant/directory to connect to.
     
    .PARAMETER Scopes
        Any scopes to include in the request.
        Only used for interactive/delegate workflows, ignored for Certificate based authentication or when using Client Secrets.
 
    .PARAMETER Browser
        Use an interactive logon in your default browser.
        This is the default logon experience.
 
    .PARAMETER BrowserMode
        How the browser used for authentication is selected.
        Options:
        + Auto (default): Automatically use the default browser.
        + PrintLink: The link to open is printed on console and user selects which browser to paste it into (must be used on the same machine)
 
    .PARAMETER DeviceCode
        Use the Device Code delegate authentication flow.
        This will prompt the user to complete login via browser.
     
    .PARAMETER Certificate
        The Certificate object used to authenticate with.
         
        Part of the Application Certificate authentication workflow.
     
    .PARAMETER CertificateThumbprint
        Thumbprint of the certificate to authenticate with.
        The certificate must be stored either in the user or computer certificate store.
         
        Part of the Application Certificate authentication workflow.
     
    .PARAMETER CertificateName
        The name/subject of the certificate to authenticate with.
        The certificate must be stored either in the user or computer certificate store.
        The newest certificate with a private key will be chosen.
         
        Part of the Application Certificate authentication workflow.
     
    .PARAMETER CertificatePath
        Path to a PFX file containing the certificate to authenticate with.
         
        Part of the Application Certificate authentication workflow.
     
    .PARAMETER CertificatePassword
        Password to use to read a PFX certificate file.
        Only used together with -CertificatePath.
         
        Part of the Application Certificate authentication workflow.
     
    .PARAMETER ClientSecret
        The client secret configured in the registered/enterprise application.
         
        Part of the Client Secret Certificate authentication workflow.
 
    .PARAMETER Credential
        The username / password to authenticate with.
 
        Part of the Resource Owner Password Credential (ROPC) workflow.
 
    .PARAMETER VaultName
        Name of the Azure Key Vault from which to retrieve the certificate or client secret used for the authentication.
        Secrets retrieved from the vault are not cached, on token expiration they will be retrieved from the Vault again.
        In order for this flow to work, please ensure that you either have an active AzureKeyVault service connection,
        or are connected via Connect-AzAccount.
 
    .PARAMETER SecretName
        Name of the secret to use from the Azure Key Vault specified through the '-VaultName' parameter.
        In order for this flow to work, please ensure that you either have an active AzureKeyVault service connection,
        or are connected via Connect-AzAccount.
 
    .PARAMETER Identity
        Log on as the Managed Identity of the current system.
        Only works in environments with managed identities, such as Azure Function Apps or Runbooks.
 
    .PARAMETER IdentityID
        ID of the User-Managed Identity to connect as.
        https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity
 
    .PARAMETER IdentityType
        Type of the User-Managed Identity.
 
    .PARAMETER Service
        The service to connect to.
        Individual commands using Invoke-MdeRequest specify the service to use and thus identify the token needed.
        Defaults to: Endpoint
 
    .PARAMETER ServiceUrl
        The base url to the service connecting to.
        Used for authentication, scopes and executing requests.
        Defaults to: https://api.securitycenter.microsoft.com/api
 
    .PARAMETER MdcaToken
        Legacy API Token for Microsoft Defender for Cloud Apps.
        Please stop using this and migrate to modern authentication.
 
    .PARAMETER TenantName
        The name of the tenant you are connecting for.
        Used solely for connections to Microsoft Defender for Cloud Apps connections.
        If your fully qualified tenant name is "contoso.onmicrosoft.com", the value to provide is "contoso".
        This is used as the first element in the service url for MDCA connections.
        If that name just won't work, check in the defender portal settings, under cloud apps in the overview what the API url is.
     
    .EXAMPLE
        PS C:\> Connect-DefenderAPI -ClientID $clientID -TenantID $tenantID
     
        Establish a connection to the defender for endpoint API, prompting the user for login on their default browser.
     
    .EXAMPLE
        PS C:\> Connect-DefenderAPI -ClientID $clientID -TenantID $tenantID -Certificate $cert
     
        Establish a connection to the defender APIs using the provided certificate.
     
    .EXAMPLE
        PS C:\> Connect-DefenderAPI -ClientID $clientID -TenantID $tenantID -CertificatePath C:\secrets\certs\mde.pfx -CertificatePassword (Read-Host -AsSecureString)
     
        Establish a connection to the defender APIs using the provided certificate file.
        Prompts you to enter the certificate-file's password first.
     
    .EXAMPLE
        PS C:\> Connect-DefenderAPI -ClientID $clientID -TenantID $tenantID -ClientSecret $secret
     
        Establish a connection to the defender APIs using a client secret.
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
    [CmdletBinding(DefaultParameterSetName = 'Browser')]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Browser')]
        [Parameter(Mandatory = $true, ParameterSetName = 'DeviceCode')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AppCertificate')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AppSecret')]
        [Parameter(Mandatory = $true, ParameterSetName = 'UsernamePassword')]
        [Parameter(Mandatory = $true, ParameterSetName = 'KeyVault')]
        [string]
        $ClientID,
        
        [Parameter(Mandatory = $true, ParameterSetName = 'Browser')]
        [Parameter(Mandatory = $true, ParameterSetName = 'DeviceCode')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AppCertificate')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AppSecret')]
        [Parameter(Mandatory = $true, ParameterSetName = 'UsernamePassword')]
        [Parameter(Mandatory = $true, ParameterSetName = 'KeyVault')]
        [string]
        $TenantID,
        
        [Parameter(ParameterSetName = 'Browser')]
        [Parameter(ParameterSetName = 'DeviceCode')]
        [Parameter(ParameterSetName = 'AppCertificate')]
        [Parameter(ParameterSetName = 'AppSecret')]
        [Parameter(ParameterSetName = 'UsernamePassword')]
        [string[]]
        $Scopes,

        [Parameter(ParameterSetName = 'Browser')]
        [switch]
        $Browser,

        [Parameter(ParameterSetName = 'Browser')]
        [ValidateSet('Auto','PrintLink')]
        [string]
        $BrowserMode = 'Auto',

        [Parameter(ParameterSetName = 'DeviceCode')]
        [switch]
        $DeviceCode,
        
        [Parameter(ParameterSetName = 'AppCertificate')]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]
        $Certificate,
        
        [Parameter(ParameterSetName = 'AppCertificate')]
        [string]
        $CertificateThumbprint,
        
        [Parameter(ParameterSetName = 'AppCertificate')]
        [string]
        $CertificateName,
        
        [Parameter(ParameterSetName = 'AppCertificate')]
        [string]
        $CertificatePath,
        
        [Parameter(ParameterSetName = 'AppCertificate')]
        [System.Security.SecureString]
        $CertificatePassword,
        
        [Parameter(Mandatory = $true, ParameterSetName = 'AppSecret')]
        [System.Security.SecureString]
        $ClientSecret,

        [Parameter(Mandatory = $true, ParameterSetName = 'UsernamePassword')]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory = $true, ParameterSetName = 'MdcaLegacyToken')]
        [securestring]
        $MdcaToken,

        [Parameter(Mandatory = $true, ParameterSetName = 'KeyVault')]
        [string]
        $VaultName,

        [Parameter(Mandatory = $true, ParameterSetName = 'KeyVault')]
        [string]
        $SecretName,

        [Parameter(Mandatory = $true, ParameterSetName = 'Identity')]
        [switch]
        $Identity,

        [Parameter(ParameterSetName = 'Identity')]
        [string]
        $IdentityID,

        [Parameter(ParameterSetName = 'Identity')]
        [ValidateSet('ClientID', 'ResourceID', 'PrincipalID')]
        [string]
        $IdentityType = 'ClientID',

        [Parameter(ParameterSetName = 'Browser')]
        [Parameter(ParameterSetName = 'DeviceCode')]
        [Parameter(ParameterSetName = 'AppCertificate')]
        [Parameter(ParameterSetName = 'AppSecret')]
        [Parameter(ParameterSetName = 'UsernamePassword')]
        [Parameter(ParameterSetName = 'KeyVault')]
        [Parameter(ParameterSetName = 'Identity')]
        [Parameter(Mandatory = $true, ParameterSetName = 'MdcaLegacyToken')]
        [string]
        $TenantName,

        [Parameter(ParameterSetName = 'Browser')]
        [Parameter(ParameterSetName = 'DeviceCode')]
        [Parameter(ParameterSetName = 'AppCertificate')]
        [Parameter(ParameterSetName = 'AppSecret')]
        [Parameter(ParameterSetName = 'UsernamePassword')]
        [Parameter(ParameterSetName = 'KeyVault')]
        [Parameter(ParameterSetName = 'Identity')]
        [ValidateSet('Endpoint', 'Security', 'MDCA')]
        [string[]]
        $Service = 'Endpoint',

        [Parameter(ParameterSetName = 'Browser')]
        [Parameter(ParameterSetName = 'DeviceCode')]
        [Parameter(ParameterSetName = 'AppCertificate')]
        [Parameter(ParameterSetName = 'AppSecret')]
        [Parameter(ParameterSetName = 'UsernamePassword')]
        [Parameter(ParameterSetName = 'KeyVault')]
        [Parameter(ParameterSetName = 'Identity')]
        [string]
        $ServiceUrl
    )
    
    begin {
        if ($Service -contains 'MDCA' -and -not $TenantName) {
            Stop-PSFFunction -String 'Connect-DefenderAPI.Error.MdcaNoTenant' -EnableException $true -Cmdlet $PSCmdlet
        }

        if ($Service -contains 'MDCA' -or $MdcaToken) {
            $param = @{
                Name          = 'DefenderAPI.MDCA'
                ServiceUrl    = "https://$TenantName.portal.cloudappsecurity.com/api/v1"
                Resource      = '05a65629-4c1b-48c1-a78b-804c4abdd4af'
                DefaultScopes = @()
                Header        = @{ 'content-type' = 'application/json' }
                HelpUrl       = 'https://learn.microsoft.com/en-us/defender-cloud-apps/api-introduction'
            }
            Register-EntraService @param
        }
    }
    process {
        $actualServiceNames = foreach ($serviceName in $Service) { "DefenderAPI.$serviceName" }

        if ($MdcaToken) {
            Set-MdcaToken -Token $MdcaToken -TenantName $TenantName
            return
        }

        $param = $PSBoundParameters | ConvertTo-PSFHashtable -ReferenceCommand Connect-EntraService
        $param.Service = $actualServiceNames
        Connect-EntraService @param
    }
}

function Get-MdcaAlert {
    <#
    .SYNOPSIS
        List/Retrieve alerts
     
    .DESCRIPTION
        List/Retrieve alerts
        Either a specific alert by ID or a list by filter.
     
    .PARAMETER ID
        ID of the event to retrieve.
     
    .PARAMETER SortField
        Sort results by the specified field.
        By default, results are however sorted as the API backend choses.
        Options: date, severity
     
    .PARAMETER Descending
        By default, results are sorted in an ascending order (if sorting at all).
        This parameter reverses the sorting order.
     
    .PARAMETER Skip
        Skip the first X results.
     
    .PARAMETER Limit
        Return only X results in total.
     
    .PARAMETER Filter
        A custom filter as defined here: https://learn.microsoft.com/en-us/defender-cloud-apps/api-alerts#filters
        Example filter for open alerts of high severity:
        @{
            alertOpen = @{ eq = $true }
            severity = @{ eq = 2 }
        }
     
    .EXAMPLE
        PS C:\> Get-MdcaAlert
 
        List all alerts.
 
    .EXAMPLE
        PS C:\> Get-MdcaAlert -ID 909cd095-1677-44eb-98a3-dda1e3c26733
 
        Retrieve the specified alert
    #>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'identity', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('_id')]
        [string[]]
        $ID,

        [Parameter(ParameterSetName = 'default')]
        [Parameter(ParameterSetName = 'Filter')]
        [ValidateSet('date', 'severity')]
        [string]
        $SortField,

        [Parameter(ParameterSetName = 'default')]
        [Parameter(ParameterSetName = 'Filter')]
        [switch]
        $Descending,

        [Parameter(ParameterSetName = 'default')]
        [Parameter(ParameterSetName = 'Filter')]
        [int]
        $Skip,

        [Parameter(ParameterSetName = 'default')]
        [Parameter(ParameterSetName = 'Filter')]
        [int]
        $Limit,

        [Parameter(ParameterSetName = 'Filter')]
        [hashtable]
        $Filter
    )

    begin {
        Assert-DefenderAPIConnection -Service MDCA -Cmdlet $PSCmdlet
    }
    process {
        #region ID
        if ($ID) {
            foreach ($idString in $ID) {
                try { Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Path "alerts/$idString" -ErrorAction Stop }
                catch { $PSCmdlet.WriteError($_) }
            }
            return
        }
        #endregion ID

        $body = @{ }
        if ($SortField) {
            $body.sortField = $SortField
            $body.sortDirection = 'asc'
            if ($Descending) { $body.sortDirection = 'dsc' }
        }
        $noPaging = $false
        if ($PSBoundParameters.ContainsKey("Skip")) { $body.skip = $Skip; $noPaging = $true }
        if ($PSBoundParameters.ContainsKey("Limit")) { $body.limit = $Limit; $noPaging = $true }
        if ($Filter) { $body.filters = $Filter }

        do {
            $result = Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Path alerts -Body $body -Raw
            $body.skip = @($result.data).Count + $body.skip
            $result.data
        }
        while ($result.hasNext -and -not $noPaging)
    }
}

function Get-MdcaFile {
    <#
    .SYNOPSIS
        List or retrieve files.
     
    .DESCRIPTION
        List or retrieve files.
     
    .PARAMETER ID
        ID of the file to retrieve.
     
    .PARAMETER Skip
        Skip the first X results when listing files.
     
    .PARAMETER Limit
        Maximum number of files to retrieve.
     
    .PARAMETER Filter
        A custom filter as defined here: https://learn.microsoft.com/en-us/defender-cloud-apps/api-files#filters
        Example filter for public documents:
        @{
            fileType = @{ eq = 1 }
            sharing = @{ eq = 3 }
        }
     
    .EXAMPLE
        PS C:\> Get-MdcaFile
 
        List all files.
 
    .EXAMPLE
        PS C:\> Get-MdcaFile -ID 25e86014-b08d-4d42-a685-ca00a230c458
 
        Retrieve the specified file.
    #>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'identity', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('_id')]
        [string[]]
        $ID,

        [Parameter(ParameterSetName = 'default')]
        [int]
        $Skip,

        [Parameter(ParameterSetName = 'default')]
        [int]
        $Limit,

        [hashtable]
        $Filter
    )

    begin {
        Assert-DefenderAPIConnection -Service MDCA -Cmdlet $PSCmdlet
    }
    process {
        #region ID
        if ($ID) {
            foreach ($idString in $ID) {
                try { Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Path "files/$idString" -ErrorAction Stop }
                catch { $PSCmdlet.WriteError($_) }
            }
            return
        }
        #endregion ID

        $body = @{ }
        $noPaging = $false
        if ($PSBoundParameters.ContainsKey("Skip")) { $body.skip = $Skip; $noPaging = $true }
        if ($PSBoundParameters.ContainsKey("Limit")) { $body.limit = $Limit; $noPaging = $true }
        if ($Filter) { $body.filters = $Filter }

        do {
            $result = Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Path files -Body $body -Raw
            $body.skip = @($result.data).Count + $body.skip
            $result.data
        }
        while ($result.hasNext -and -not $noPaging)
    }
}

function Get-MdcaSubnet {
    <#
    .SYNOPSIS
        Returns the available / configured subnets.
     
    .DESCRIPTION
        Returns the available / configured subnets.
 
        Note: Filter parameters are currently non-functional.
     
    .PARAMETER ID
        ID of a subnet to retrieve.
     
    .PARAMETER SortField
        Field by which to sort the results.
     
    .PARAMETER Descending
        Whether to sort in a descending order
     
    .PARAMETER Skip
        Skip the first X results
        Using this parameter disables the automatic paging feature.
     
    .PARAMETER Limit
        Maximum number of subnets to return.
        Using this parameter disables the automatic paging feature.
     
    .PARAMETER Filter
        A full filter to specify with the request
     
    .PARAMETER IncludeCategory
        Only return subnets that contain any of the specified categories.
     
    .PARAMETER ExcludeCategory
        Do not return subnets that contain any of the specified categories.
     
    .PARAMETER IncludeTag
        Only return subnets that contain any of the specified tags.
     
    .PARAMETER ExcludeTag
        Do not return subnets that contain any of the specified tags.
     
    .PARAMETER BuiltIn
        Specify whether built-in only or custom only subnets should be returned
     
    .EXAMPLE
        PS C:\> Get-MdcaSubnet
 
        Returns all subnets from MDCA
    #>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    Param (
        [Parameter(Mandatory = $true, ParameterSetName = 'identity', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('_id')]
        [string[]]
        $ID,

        [ValidateSet('category', 'tags', 'name')]
        [string]
        $SortField,

        [switch]
        $Descending,

        [int]
        $Skip,

        [int]
        $Limit,

        [Parameter(ParameterSetName = 'Filter')]
        [hashtable]
        $Filter,

        [Parameter(ParameterSetName = 'Condition')]
        [SubnetCategory[]]
        $IncludeCategory,

        [Parameter(ParameterSetName = 'Condition')]
        [SubnetCategory[]]
        $ExcludeCategory,

        [Parameter(ParameterSetName = 'Condition')]
        [string[]]
        $IncludeTag,

        [Parameter(ParameterSetName = 'Condition')]
        [string[]]
        $ExcludeTag,

        [Parameter(ParameterSetName = 'Condition')]
        [bool]
        $BuiltIn
    )
    
    begin {
        Assert-DefenderAPIConnection -Service MDCA -Cmdlet $PSCmdlet
    }
    process {

        if ($ID) {
            foreach ($idString in $ID) {
                try { Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Path "subnet/$idString" -ErrorAction Stop | ConvertFrom-RestSubnet }
                catch { $PSCmdlet.WriteError($_) }
            }
            return
        }

        $body = @{ }
        if ($SortField) {
            $body.sortField = $SortField
            $body.sortDirection = 'asc'
            if ($Descending) { $body.sortDirection = 'dsc' }
        }
        $noPaging = $false
        if ($PSBoundParameters.ContainsKey("Skip")) { $body.skip = $Skip; $noPaging = $true }
        if ($PSBoundParameters.ContainsKey("Limit")) { $body.limit = $Limit; $noPaging = $true }

        #region Filters
        # TODO: Figure out why filters are ignored
        switch ($PSCmdlet.ParameterSetName) {
            'Filter' { $body.filters = $Filter }
            'Condition' {
                $filterHash = @{}
                if ($IncludeCategory -or $ExcludeCategory) {
                    $filterHash.category = @{}
                    if ($IncludeCategory) {
                        $filterHash.category.eq = @($IncludeCategory | ForEach-Object { [int]$_ })
                    }
                    if ($ExcludeCategory) {
                        $filterHash.category.neq = @($ExcludeCategory | ForEach-Object { [int]$_ })
                    }
                }
                if ($IncludeTag -or $ExcludeTag) {
                    $filterHash.tags = @{ }
                    if ($IncludeTag) { $filterHash.tags.eq = $IncludeTag }
                    if ($ExcludeTag) { $filterHash.tags.neq = $ExcludeTag }
                }
                if ($PSBoundParameters.ContainsKey("BuiltIn")) {
                    $filterHash.builtIn = @{ eq = $BuiltIn }
                }
                $body.filters = $filterHash
            }
        }
        #endregion Filters

        do {
            $result = Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Path 'subnet/' -Body $body -Raw
            $body.skip = @($result.data).Count + $body.skip
            $result.data | ConvertFrom-RestSubnet
        }
        while ($result.hasNext -and -not $noPaging)
    }
}

function New-MdcaSubnet {
    <#
    .SYNOPSIS
        Create a new MDCA subnet
     
    .DESCRIPTION
        Create a new MDCA subnet
     
    .PARAMETER Name
        Name of the subnet to create
     
    .PARAMETER Category
        The category the subnet should have
     
    .PARAMETER Subnets
        IPRanges / subnets that should be part of this subnet
     
    .PARAMETER Organization
        The organization this subnet is part of
     
    .PARAMETER Tag
        Any tags this subnet should have.
     
    .PARAMETER EnableException
        This parameters disables user-friendly warnings and enables the throwing of exceptions.
        This is less user friendly, but allows catching exceptions in calling scripts.
 
    .PARAMETER WhatIf
        if this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
     
    .EXAMPLE
        PS C:\> New-MdcaSubnet -Name "europe" -Category Corporate -Subnets '10.1.0.0/16' -Organization ContosoEU -Tag europe, intranet
         
        Creates a new MDCA subnet
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','')]
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [SubnetCategory]
        $Category,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]
        $Subnets,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]
        $Organization,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('tags')]
        [string[]]
        $Tag,

        [switch]
        $EnableException
    )
    
    begin {
        Assert-DefenderAPIConnection -Service MDCA -Cmdlet $PSCmdlet
    }
    process {
        $body = @{
            name     = $Name
            category = [int]$Category
            subnets  = $Subnets
        }
        if ($Organization) { $body.organization = $Organization }
        if ($Tag) { $body.tags = $Tag }

        Invoke-PSFProtectedCommand -ActionString 'New-MdcaSubnet.Create' -ActionStringValues $Name -Target $Name -ScriptBlock {
            $newID = Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Method Post -Path "subnet/create_rule/" -Body $body
        } -EnableException $EnableException -PSCmdlet $PSCmdlet

        Get-MdcaSubnet -ID $newID
    }
}

function Remove-MdcaSubnet {
    <#
    .SYNOPSIS
        Remove a MDCA subnet
     
    .DESCRIPTION
        Remove a MDCA subnet
     
    .PARAMETER ID
        ID of the subnet to remove
     
    .PARAMETER EnableException
        This parameters disables user-friendly warnings and enables the throwing of exceptions.
        This is less user friendly, but allows catching exceptions in calling scripts.
 
    .PARAMETER WhatIf
        if this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
     
    .EXAMPLE
        PS C:\> Get-MdcaSubnet | Where-Object Name -eq na | Remove-MdcaSubnet
 
        Removes the subnet named "na"
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('_id')]
        [string[]]
        $ID,

        [switch]
        $EnableException
    )
    
    begin {
        Assert-DefenderAPIConnection -Service MDCA -Cmdlet $PSCmdlet
    }
    process {
        foreach ($subnetID in $ID) {
            Invoke-PSFProtectedCommand -ActionString 'Remove-MdcaSubnet.Deleting' -ActionStringValues $subnetID -Target $subnetID -ScriptBlock {
                $null = Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Method Delete -Path "subnet/$subnetID" -ErrorAction Stop
            } -EnableException $EnableException -Continue -PSCmdlet $PSCmdlet
        }
    }
}


function Set-MdcaSubnet {
    <#
    .SYNOPSIS
        Updates an existing MDCA subnet.
     
    .DESCRIPTION
        Updates an existing MDCA subnet.
 
        All properties will be overwritten each time!
        Not specifying tags equals to removing all existing tags.
 
        Note: Each time you update a subnet you must change its name.
     
    .PARAMETER ID
        ID of the subnet to modify.
     
    .PARAMETER Name
        The name to assign to the subnet.
        Note: Each time you update a subnet you must change its name.
     
    .PARAMETER Category
        The category the subnet should have.
     
    .PARAMETER Subnets
        The IP ranges assigned to this subnet
     
    .PARAMETER Organization
        The Organization the subnet is part of
     
    .PARAMETER Tag
        Any tags the subnet should include
     
    .PARAMETER PassThru
        Whether the result should be returned as output
     
    .PARAMETER EnableException
        This parameters disables user-friendly warnings and enables the throwing of exceptions.
        This is less user friendly, but allows catching exceptions in calling scripts.
 
    .PARAMETER WhatIf
        if this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
     
    .EXAMPLE
        PS C:\> Get-MdcaSubnet | Where-Object Name -match "^na_{0,1}$" | Set-MdcaSubnet -Subnets '66.66.66.0/24' -Name { ($_.Name + "_") -replace "__" }
         
        Updates the subnet na to _only_ include the iprange '66.66.66.0/24'.
        Alternates the name between "na" and "na_"
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('_id')]
        [string]
        $ID,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [SubnetCategory]
        $Category,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]
        $Subnets,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]
        $Organization,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('tags')]
        [string[]]
        $Tag,

        [switch]
        $PassThru,

        [switch]
        $EnableException
    )
    
    begin {
        Assert-DefenderAPIConnection -Service MDCA -Cmdlet $PSCmdlet
    }
    process {
        try { $subnet = Get-MdcaSubnet -ID $ID -ErrorAction Stop }
        catch {
            Stop-PSFFunction -String 'Set-MdcaSubnet.NotFound' -StringValues $ID -EnableException $EnableException
            return
        }

        if ($subnet.name -eq $Name) {
            Stop-PSFFunction -String 'Set-MdcaSubnet.DuplicateName' -StringValues $ID, $Name, $subnet.name -EnableException $EnableException
            return
        }

        $body = @{
            name = $Name
            category = [int]$Category
            subnets = $Subnets
        }
        if ($Organization) { $body.organization = $Organization }
        if ($Tag) { $body.tags = $Tag }

        Invoke-PSFProtectedCommand -ActionString 'Set-MdcaSubnet.Modify' -ActionStringValues $subnet.name, $Name -Target $ID -ScriptBlock {
            $null = Invoke-EntraRequest -Service 'DefenderAPI.MDCA' -Method Post -Path "subnet/$ID/update_rule/" -Body $body
        } -EnableException $EnableException -PSCmdlet $PSCmdlet

        if ($PassThru) {
            Get-MdcaSubnet -ID $ID
        }
    }
}

function Invoke-MdAdvancedQuery {
<#
.SYNOPSIS
    Advanced Hunting
 
.DESCRIPTION
    Run a custom query in Windows Defender ATP
 
    Scopes required (delegate auth): AdvancedQuery.Read
 
.PARAMETER Query
    The query to run
 
.EXAMPLE
    PS C:\> Invoke-MdAdvancedQuery -Query $query
 
    Run a custom query in Windows Defender ATP
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/run-advanced-query-api?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Query
    )
    process {
        $__mapping = @{
            'Query' = 'Query'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Query') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'advancedqueries/run'
            Method = 'post'
            RequiredScopes = 'AdvancedQuery.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param | ConvertFrom-AdvancedQuery }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdAdvancedQuerySchema {
<#
.SYNOPSIS
    Advanced Hunting Schema
 
.DESCRIPTION
    Gets the schema for a Windows Defender ATP custom query
 
.PARAMETER Query
    The query to run
 
.EXAMPLE
    PS C:\> Set-MdAdvancedQuerySchema -Query $query
 
    Gets the schema for a Windows Defender ATP custom query
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Query
    )
    process {
        $__mapping = @{
            'Query' = 'Query'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Query') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'advancedqueries/schema'
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdAlert {
<#
.SYNOPSIS
    Alerts - Get list of alerts
 
.DESCRIPTION
    Retrieve from Windows Defender ATP the most recent alerts
 
    Scopes required (delegate auth): Alert.Read
 
.PARAMETER Top
    Returns only the first n results.
 
.PARAMETER Orderby
    Sorts the results.
 
.PARAMETER Select
    Selects which properties to include in the response, defaults to all.
 
.PARAMETER Filter
    Filters the results, using OData syntax.
 
.PARAMETER AlertID
    The identifier of the alert to retrieve
 
.PARAMETER Expand
    Expands related entities inline.
 
.PARAMETER Count
    Includes a count of the matching results in the response.
 
.PARAMETER Skip
    Skips the first n results.
 
.EXAMPLE
    PS C:\> Get-MdAlert -AlertID $alertid
 
    Retrieve from Windows Defender ATP a specific alert
 
.EXAMPLE
    PS C:\> Get-MdAlert
 
    Retrieve from Windows Defender ATP the most recent alerts
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-alerts?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Top,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Orderby,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string[]]
        $Select,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Filter,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GetSingleAlert')]
        [string]
        $AlertID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Expand,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [boolean]
        $Count,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Skip
    )
    process {
        $__mapping = @{
            'Top' = '$top'
            'Orderby' = '$orderby'
            'Select' = '$select'
            'Filter' = '$filter'
            'Expand' = '$expand'
            'Count' = '$count'
            'Skip' = '$skip'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @('Top','Orderby','Select','Filter','Expand','Count','Skip') -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'alerts'
            Method = 'get'
            RequiredScopes = 'Alert.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        if ($AlertID) { $__param.Path += "/$AlertID" }
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function New-MdAlert {
<#
.SYNOPSIS
    Alerts - Create alert
 
.DESCRIPTION
    Create Alert based on specific Event
 
    Scopes required (delegate auth): Alert.ReadWrite
 
.PARAMETER ReportID
    Report Id of the event
 
.PARAMETER EventTime
    Time of the event as string, e.g. 2018-08-03T16:45:21.7115183Z
 
.PARAMETER MachineID
    ID of the machine on which the event was identified
 
.PARAMETER RecommendedAction
    Recommended action for the Alert
 
.PARAMETER Title
    Title of the Alert
 
.PARAMETER Category
    Category of the alert
 
.PARAMETER Severity
    Severity of the alert.
 
.PARAMETER Description
    Description of the Alert
 
.EXAMPLE
    PS C:\> New-MdAlert -Title $title -Category $category -Severity $severity -Description $description
 
    Create Alert based on specific Event
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/create-alert-by-reference?view=o365-worldwide
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $ReportID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $EventTime,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $MachineID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $RecommendedAction,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Title,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Category,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Severity,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Description
    )
    process {
        $__mapping = @{
            'ReportID' = 'Report ID'
            'EventTime' = 'Event Time'
            'MachineID' = 'Machine ID'
            'RecommendedAction' = 'Recommended Action'
            'Title' = 'Title'
            'Category' = 'Category'
            'Severity' = 'Severity'
            'Description' = 'Description'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('ReportID','EventTime','MachineID','RecommendedAction','Title','Category','Severity','Description') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'alerts/createAlertByReference'
            Method = 'post'
            RequiredScopes = 'Alert.ReadWrite'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdAlert {
<#
.SYNOPSIS
    Alerts - Update alert
 
.DESCRIPTION
    Update a Windows Defender ATP alert
 
    Scopes required (delegate auth): Alert.ReadWrite
 
.PARAMETER Comment
    A comment to associate to the alert
 
.PARAMETER AssignedTo
    Person to assign the alert to
 
.PARAMETER Status
    Status of the alert. One of 'New', 'InProgress' and 'Resolved'
 
.PARAMETER Classification
    Classification of the alert. One of 'Unknown', 'FalsePositive', 'TruePositive'
 
.PARAMETER AlertID
    The identifier of the alert to update
 
.PARAMETER Determination
    The determination of the alert. One of 'NotAvailable', 'Apt', 'Malware', 'SecurityPersonnel', 'SecurityTesting', 'UnwantedSoftware', 'Other'
 
.PARAMETER Confirm
    If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
.PARAMETER WhatIf
    If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
.EXAMPLE
    PS C:\> Set-MdAlert -AlertID $alertid
 
    Update a Windows Defender ATP alert
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/update-alert?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default', SupportsShouldProcess = $true)]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $AssignedTo,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Status,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Classification,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $AlertID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Determination
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
            'AssignedTo' = 'Assigned to'
            'Status' = 'Status'
            'Classification' = 'Classification'
            'Determination' = 'Determination'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment','AssignedTo','Status','Classification','Determination') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'alerts/{AlertID}' -Replace '{AlertID}',$AlertID
            Method = 'patch'
            RequiredScopes = 'Alert.ReadWrite'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'
        if (-not $PSCmdlet.ShouldProcess("$AlertID","Update existing Alert")) { return }
        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdDeviceSecureScore {
<#
.SYNOPSIS
    Retrieves your Microsoft Secure Score for Devices
 
.DESCRIPTION
    Retrieves your Microsoft Secure Score for Devices. A higher Microsoft Secure Score for Devices means your endpoints are more resilient from cybersecurity threat attacks.
 
    Scopes required (delegate auth): Score.Read
 
 
 
.EXAMPLE
    PS C:\> Get-MdDeviceSecureScore
 
    <insert description here>
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-device-secure-score?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (

    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'configurationScore'
            Method = 'get'
            RequiredScopes = 'Score.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdExposureScore {
<#
.SYNOPSIS
    Retrieves the organizational exposure score.
 
.DESCRIPTION
    Retrieves the organizational exposure score.
 
    Scopes required (delegate auth): Score.Read
 
 
 
.EXAMPLE
    PS C:\> Get-MdExposureScore
 
    <insert description here>
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-exposure-score?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (

    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'exposureScore'
            Method = 'get'
            RequiredScopes = 'Score.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdMachineGroupExposureScore {
<#
.SYNOPSIS
    Retrieves a collection of alerts related to a given domain address.
 
.DESCRIPTION
    Retrieves a collection of alerts related to a given domain address.
 
    Scopes required (delegate auth): Score.Read
 
 
 
.EXAMPLE
    PS C:\> Get-MdMachineGroupExposureScore
 
    <insert description here>
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-machine-group-exposure-score?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (

    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'exposureScore/byMachineGroups'
            Method = 'get'
            RequiredScopes = 'Score.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdFile {
<#
.SYNOPSIS
    Files - Get a single file
 
.DESCRIPTION
    Retrieve from Windows Defender ATP a specific file by identifier Sha1, or Sha256
 
.PARAMETER FileID
    The file identifier - Sha1, or Sha256
 
.EXAMPLE
    PS C:\> Get-MdFile -FileID $fileid
 
    Retrieve from Windows Defender ATP a specific file by identifier Sha1, or Sha256
 
.LINK
    <unknown>
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $FileID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'files/{FileID}' -Replace '{FileID}',$FileID
            Method = 'get'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdFileAlert {
<#
.SYNOPSIS
    Files - Get alerts related to a file
 
.DESCRIPTION
    Retrieve from Windows Defender ATP a collection of alerts related to a given file by identifier Sha1, or Sha256
 
.PARAMETER FileID
    The file identifier - Sha1, or Sha256
 
.EXAMPLE
    PS C:\> Get-MdFileAlert -FileID $fileid
 
    Retrieve from Windows Defender ATP a collection of alerts related to a given file by identifier Sha1, or Sha256
 
.LINK
    <unknown>
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $FileID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'files/{FileID}/alerts' -Replace '{FileID}',$FileID
            Method = 'get'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdFileMachine {
<#
.SYNOPSIS
    Files - Get machines related to a file
 
.DESCRIPTION
    Retrieve from Windows Defender ATP a collection of machines related to a given file by identifier Sha1, or Sha256
 
.PARAMETER FileID
    The file identifier - Sha1, or Sha256
 
.EXAMPLE
    PS C:\> Get-MdFileMachine -FileID $fileid
 
    Retrieve from Windows Defender ATP a collection of machines related to a given file by identifier Sha1, or Sha256
 
.LINK
    <unknown>
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $FileID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'files/{FileID}/machines' -Replace '{FileID}',$FileID
            Method = 'get'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdIndicator {
<#
.SYNOPSIS
    Indicators - Get list of all active indicators
 
.DESCRIPTION
    Retrieve from Windows Defender ATP list of all active indicators
 
.PARAMETER Top
    Returns only the first n results.
 
.PARAMETER Orderby
    Sorts the results.
 
.PARAMETER Select
    Selects which properties to include in the response, defaults to all.
 
.PARAMETER Filter
    Filters the results, using OData syntax.
 
.PARAMETER Count
    Includes a count of the matching results in the response.
 
.PARAMETER Skip
    Skips the first n results.
 
.EXAMPLE
    PS C:\> Get-MdIndicator
 
    Retrieve from Windows Defender ATP list of all active indicators
 
.LINK
    <unknown>
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Top,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Orderby,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string[]]
        $Select,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Filter,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [boolean]
        $Count,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Skip
    )
    process {
        $__mapping = @{
            'Top' = '$top'
            'Orderby' = '$orderby'
            'Select' = '$select'
            'Filter' = '$filter'
            'Count' = '$count'
            'Skip' = '$skip'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @('Top','Orderby','Select','Filter','Count','Skip') -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'indicators'
            Method = 'get'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function New-MdIndicator {
<#
.SYNOPSIS
    Indicators - Submit a new indicator
 
.DESCRIPTION
    Submit a new indicator
 
    Scopes required (delegate auth): Ti.ReadWrite
 
.PARAMETER IndicatorType
    The type of the indicator
 
.PARAMETER Title
    The indicator title
 
.PARAMETER ExpirationTime
    The expiration time of the indicator
 
.PARAMETER Application
    The application associated with the indicator
 
.PARAMETER Severity
    The severity of the indicator
 
.PARAMETER RecommendedActions
    Recommended actions for the indicator
 
.PARAMETER IndicatorValue
    The value of the indicator
 
.PARAMETER Description
    The indicator description
 
.PARAMETER Action
    The action that will be taken if the indicator will be discovered in the organization
 
.EXAMPLE
    PS C:\> New-MdIndicator -Title $title -Description $description -Action $action
 
    Submit a new indicator
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/post-ti-indicator?view=o365-worldwide
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $IndicatorType,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Title,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $ExpirationTime,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Application,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Severity,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $RecommendedActions,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $IndicatorValue,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Description,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Action
    )
    process {
        $__mapping = @{
            'IndicatorType' = 'Indicator type'
            'Title' = 'Title'
            'ExpirationTime' = 'Expiration time'
            'Application' = 'Application'
            'Severity' = 'Severity'
            'RecommendedActions' = 'Recommended Actions'
            'IndicatorValue' = 'Indicator Value'
            'Description' = 'Description'
            'Action' = 'Action'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('IndicatorType','Title','ExpirationTime','Application','Severity','RecommendedActions','IndicatorValue','Description','Action') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'indicators'
            Method = 'post'
            RequiredScopes = 'Ti.ReadWrite'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Remove-MdIndicator {
<#
.SYNOPSIS
    Indicators - Delete a single indicator by id
 
.DESCRIPTION
    Delete a single indicator by indicator id
 
.PARAMETER IndicatorID
    The identifier of the Indicator to delete
 
.EXAMPLE
    PS C:\> Remove-MdIndicator -IndicatorID $indicatorid
 
    Delete a single indicator by indicator id
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $IndicatorID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'indicators/{IndicatorID}' -Replace '{IndicatorID}',$IndicatorID
            Method = 'delete'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdInvestigation {
<#
.SYNOPSIS
    Actions - Get list of investigation
 
.DESCRIPTION
    Retrieve from Microsoft Defender ATP the most recent investigations
 
.PARAMETER Top
    Returns only the first n results.
 
.PARAMETER Orderby
    Sorts the results.
 
.PARAMETER Select
    Selects which properties to include in the response, defaults to all.
 
.PARAMETER Filter
    Filters the results, using OData syntax.
 
.PARAMETER Count
    Includes a count of the matching results in the response.
 
.PARAMETER InvestigationID
    The identifier of the investigation to retrieve
 
.PARAMETER Skip
    Skips the first n results.
 
.EXAMPLE
    PS C:\> Get-MdInvestigation -InvestigationID $investigationid
 
    Retrieve from Microsoft Defender ATP a specific investigation
 
.EXAMPLE
    PS C:\> Get-MdInvestigation
 
    Retrieve from Microsoft Defender ATP the most recent investigations
 
.LINK
    <unknown>
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Top,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Orderby,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string[]]
        $Select,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Filter,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [boolean]
        $Count,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GetSingleInvestigation')]
        [string]
        $InvestigationID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Skip
    )
    process {
        $__mapping = @{
            'Top' = '$top'
            'Orderby' = '$orderby'
            'Select' = '$select'
            'Filter' = '$filter'
            'Count' = '$count'
            'Skip' = '$skip'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @('Top','Orderby','Select','Filter','Count','Skip') -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'investigations'
            Method = 'get'
            
            Service = 'DefenderAPI.Endpoint'
        }
        if ($InvestigationID) { $__param.Path += "/$InvestigationID" }
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdLiveResponseResultDownloadLink {
<#
.SYNOPSIS
    Retrieves a specific live response command result by its index.
 
.DESCRIPTION
    Retrieves a specific live response command result by its index.
 
    Scopes required (delegate auth): Machine.LiveResponse
 
.PARAMETER MachineActionID
    The identifier of the machine action
 
.PARAMETER CommandIndex
    The index of the live response command to get the results download URI for
 
.EXAMPLE
    PS C:\> Get-MdLiveResponseResultDownloadLink -MachineActionID $machineactionid -CommandIndex $commandindex
 
    Get result download URI for a completed live response command
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-live-response-result?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $MachineActionID,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $CommandIndex
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machineactions/{MachineActionID}/GetLiveResponseResultDownloadLink(index={CommandIndex})' -Replace '{MachineActionID}',$MachineActionID -Replace '{CommandIndex}',$CommandIndex
            Method = 'get'
            RequiredScopes = 'Machine.LiveResponse'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdMachineAction {
<#
.SYNOPSIS
    Actions - Get list of machine actions
 
.DESCRIPTION
    Retrieve from Windows Defender ATP the most recent machine actions
 
    Scopes required (delegate auth): Machine.Read
 
.PARAMETER Top
    Returns only the first n results.
 
.PARAMETER Orderby
    Sorts the results.
 
.PARAMETER Select
    Selects which properties to include in the response, defaults to all.
 
.PARAMETER Filter
    Filters the results, using OData syntax.
 
.PARAMETER MachineActionID
    The identifier of the machine action to retrieve
 
.PARAMETER Count
    Includes a count of the matching results in the response.
 
.PARAMETER Skip
    Skips the first n results.
 
.EXAMPLE
    PS C:\> Get-MdMachineAction -MachineActionID $machineactionid
 
    Retrieve from Windows Defender ATP a specific machine action
 
.EXAMPLE
    PS C:\> Get-MdMachineAction
 
    Retrieve from Windows Defender ATP the most recent machine actions
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-machineaction-object?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Top,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Orderby,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string[]]
        $Select,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Filter,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GetSingleMachineAction')]
        [string]
        $MachineActionID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [boolean]
        $Count,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Skip
    )
    process {
        $__mapping = @{
            'Top' = '$top'
            'Orderby' = '$orderby'
            'Select' = '$select'
            'Filter' = '$filter'
            'Count' = '$count'
            'Skip' = '$skip'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @('Top','Orderby','Select','Filter','Count','Skip') -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machineactions'
            Method = 'get'
            RequiredScopes = 'Machine.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        if ($MachineActionID) { $__param.Path += "/$MachineActionID" }
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdMachineactionGetpackageuri {
<#
.SYNOPSIS
    Actions - Get investigation package download URI
 
.DESCRIPTION
    Get a URI that allows downloading of an investigation package
 
.PARAMETER MachineactionID
    The ID of the investigation package collection
 
.EXAMPLE
    PS C:\> Get-MdMachineactionGetpackageuri -MachineactionID $machineactionid
 
    Get a URI that allows downloading of an investigation package
 
.LINK
    <unknown>
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $MachineactionID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machineactions/{MachineactionID}/getPackageUri' -Replace '{MachineactionID}',$MachineactionID
            Method = 'get'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineactionCancel {
<#
.SYNOPSIS
    Actions - Cancel a single machine action
 
.DESCRIPTION
    Cancel a specific machine action
 
.PARAMETER MachineActionID
    The identifier of the machine action to cancel
 
.PARAMETER Comment
    A comment to associate to the machine action cancellation
 
.EXAMPLE
    PS C:\> Set-MdMachineactionCancel -MachineActionID $machineactionid -Comment $comment
 
    Cancel a specific machine action
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $MachineActionID,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machineactions/{MachineActionID}/cancel' -Replace '{MachineActionID}',$MachineActionID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Disable-MdMachineIsolation {
<#
.SYNOPSIS
    Undo isolation of a device.
 
.DESCRIPTION
    Undo isolation of a device.
 
    Scopes required (delegate auth): Machine.Isolate
 
.PARAMETER Comment
    A comment to associate to the unisolation
 
.PARAMETER MachineID
    The ID of the machine to unisolate
 
.EXAMPLE
    PS C:\> Disable-MdMachineIsolation -Comment $comment -MachineID $machineid
 
    Unisolate a machine from network
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/unisolate-machine?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/unisolate' -Replace '{MachineID}',$MachineID
            Method = 'post'
            RequiredScopes = 'Machine.Isolate'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Enable-MdMachineIsolation {
<#
.SYNOPSIS
    Isolates a device from accessing external network.
 
.DESCRIPTION
    Isolates a device from accessing external network.
 
    Scopes required (delegate auth): Machine.Isolate
 
.PARAMETER Comment
    A comment to associate to the isolation
 
.PARAMETER IsolationType
    Type of the isolation. Allowed values are 'Full' (for full isolation) or 'Selective' (to restrict only limited set of applications from accessing the network)
 
.PARAMETER MachineID
    The ID of the machine to isolate
 
.EXAMPLE
    PS C:\> Enable-MdMachineIsolation -Comment $comment -MachineID $machineid
 
    Isolate a machine from network
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/isolate-machine?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $IsolationType,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
            'IsolationType' = 'Isolation Type'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment','IsolationType') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/isolate' -Replace '{MachineID}',$MachineID
            Method = 'post'
            RequiredScopes = 'Machine.Isolate'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdMachine {
<#
.SYNOPSIS
    Machines - Get list of machines
 
.DESCRIPTION
    Retrieve from Windows Defender ATP the most recent machines
 
    Scopes required (delegate auth): Machine.Read
 
.PARAMETER Top
    Returns only the first n results.
 
.PARAMETER Orderby
    Sorts the results.
 
.PARAMETER Select
    Selects which properties to include in the response, defaults to all.
 
.PARAMETER Filter
    Filters the results, using OData syntax.
 
.PARAMETER Count
    Includes a count of the matching results in the response.
 
.PARAMETER MachineID
    The identifier of the machine to retrieve
 
.PARAMETER Skip
    Skips the first n results.
 
.EXAMPLE
    PS C:\> Get-MdMachine -MachineID $machineid
 
    Retrieve from Windows Defender ATP a specific machine
 
.EXAMPLE
    PS C:\> Get-MdMachine
 
    Retrieve from Windows Defender ATP the most recent machines
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-machines?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Top,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Orderby,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string[]]
        $Select,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Filter,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [boolean]
        $Count,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GetSingleMachine')]
        [Alias('Id')]
        [string]
        $MachineID,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [int32]
        $Skip
    )
    process {
        $__mapping = @{
            'Top' = '$top'
            'Orderby' = '$orderby'
            'Select' = '$select'
            'Filter' = '$filter'
            'Count' = '$count'
            'Skip' = '$skip'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @('Top','Orderby','Select','Filter','Count','Skip') -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines'
            Method = 'get'
            RequiredScopes = 'Machine.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        if ($MachineID) { $__param.Path += "/$MachineID" }
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdMachineRecommendation {
<#
.SYNOPSIS
    Retrieves a collection of security recommendations related to a given device ID.
 
.DESCRIPTION
    Retrieves a collection of security recommendations related to a given device ID.
 
    Scopes required (delegate auth): SecurityRecommendation.Read
 
.PARAMETER MachineID
    ID of the machine to get recommendations for.
 
.EXAMPLE
    PS C:\> Get-MdMachineRecommendation -MachineID $machineid
 
    Retrieves a collection of security recommendations related to the specified device ID.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-security-recommendations?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/recommendations' -Replace '{MachineID}',$MachineID
            Method = 'get'
            RequiredScopes = 'SecurityRecommendation.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdMachineSoftware {
<#
.SYNOPSIS
    Retrieves a collection of installed software related to a given device ID.
 
.DESCRIPTION
    Retrieves a collection of installed software related to a given device ID.
 
    Scopes required (delegate auth): Software.Read
 
.PARAMETER MachineID
    ID of the machine to read the installed software from.
 
.EXAMPLE
    PS C:\> Get-MdMachineSoftware -MachineID $machineid
 
    <insert description here>
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-installed-software?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/software' -Replace '{MachineID}',$MachineID
            Method = 'get'
            RequiredScopes = 'Software.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdMachineVulnerability {
<#
.SYNOPSIS
    Retrieves a collection of discovered vulnerabilities related to a given device ID.
 
.DESCRIPTION
    Retrieves a collection of discovered vulnerabilities related to a given device ID.
 
    Scopes required (delegate auth): Vulnerability.Read
 
.PARAMETER MachineID
    ID of the machine to read the detected vulnerabilities from.
 
.EXAMPLE
    PS C:\> Get-MdMachineVulnerability -MachineID $machineid
 
    <insert description here>
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-discovered-vulnerabilities?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/vulnerabilities' -Replace '{MachineID}',$MachineID
            Method = 'get'
            RequiredScopes = 'Vulnerability.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineCollectinvestigationpackage {
<#
.SYNOPSIS
    Actions - Collect investigation package
 
.DESCRIPTION
    Collect investigation package from a machine
 
.PARAMETER Comment
    A comment to associate to the collection
 
.PARAMETER MachineID
    The ID of the machine to collect the investigation from
 
.EXAMPLE
    PS C:\> Set-MdMachineCollectinvestigationpackage -Comment $comment -MachineID $machineid
 
    Collect investigation package from a machine
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/collectInvestigationPackage' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineOffboard {
<#
.SYNOPSIS
    Actions - Offboard machine from Microsoft Defender ATP
 
.DESCRIPTION
    Offboard machine from Microsoft Defender ATP
 
.PARAMETER Comment
    A comment to associate to the offboarding action
 
.PARAMETER MachineID
    The ID of the machine to offboard
 
.EXAMPLE
    PS C:\> Set-MdMachineOffboard -Comment $comment -MachineID $machineid
 
    Offboard machine from Microsoft Defender ATP
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/offboard' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineRestrictcodeexecution {
<#
.SYNOPSIS
    Actions - Restrict app execution
 
.DESCRIPTION
    Restrict execution of all applications on the machine except a predefined set
 
.PARAMETER Comment
    A comment to associate to the restriction
 
.PARAMETER MachineID
    The ID of the machine to restrict
 
.EXAMPLE
    PS C:\> Set-MdMachineRestrictcodeexecution -Comment $comment -MachineID $machineid
 
    Restrict execution of all applications on the machine except a predefined set
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/restrictCodeExecution' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineRunantivirusscan {
<#
.SYNOPSIS
    Actions - Run antivirus scan
 
.DESCRIPTION
    Initiate Windows Defender Antivirus scan on a machine
 
.PARAMETER Comment
    A comment to associate to the scan request
 
.PARAMETER ScanType
    Type of scan to perform. Allowed values are 'Quick' or 'Full'
 
.PARAMETER MachineID
    The ID of the machine to scan
 
.EXAMPLE
    PS C:\> Set-MdMachineRunantivirusscan -Comment $comment -MachineID $machineid
 
    Initiate Windows Defender Antivirus scan on a machine
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $ScanType,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
            'ScanType' = 'Scan Type'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment','ScanType') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/runAntiVirusScan' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineStartinvestigation {
<#
.SYNOPSIS
    Actions - Start automated investigation on a machine
 
.DESCRIPTION
    Start automated investigation on a machine
 
.PARAMETER Comment
    A comment to associate to the investigation
 
.PARAMETER MachineID
    The ID of the machine to investigate
 
.EXAMPLE
    PS C:\> Set-MdMachineStartinvestigation -Comment $comment -MachineID $machineid
 
    Start automated investigation on a machine
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/startInvestigation' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineStopandquarantinefile {
<#
.SYNOPSIS
    Actions - Stop and quarantine a file
 
.DESCRIPTION
    Stop execution of a file on a machine and delete it.
 
.PARAMETER Comment
    A comment to associate to the restriction removal
 
.PARAMETER MachineID
    The ID of the machine to unrestrict
 
.EXAMPLE
    PS C:\> Set-MdMachineStopandquarantinefile -Comment $comment -MachineID $machineid
 
    Stop execution of a file on a machine and delete it.
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/StopAndQuarantineFile' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineTag {
<#
.SYNOPSIS
    Machines - Tag machine
 
.DESCRIPTION
    Add or remove a tag to/from a machine
 
.PARAMETER Action
    The action to perform. Value should be one of 'Add' (to add a tag) or 'Remove' (to remove a tag)
 
.PARAMETER Value
    The tag to add or remove
 
.PARAMETER MachineID
    The ID of the machine to which the tag should be added or removed
 
.EXAMPLE
    PS C:\> Set-MdMachineTag -Action $action -Value $value -MachineID $machineid
 
    Add or remove a tag to/from a machine
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Action,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Value,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Action' = 'Action'
            'Value' = 'Value'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Action','Value') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/tags' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Set-MdMachineUnrestrictcodeexecution {
<#
.SYNOPSIS
    Actions - Remove app execution restriction
 
.DESCRIPTION
    Enable execution of any application on the machine
 
.PARAMETER Comment
    A comment to associate to the restriction removal
 
.PARAMETER MachineID
    The ID of the machine to unrestrict
 
.EXAMPLE
    PS C:\> Set-MdMachineUnrestrictcodeexecution -Comment $comment -MachineID $machineid
 
    Enable execution of any application on the machine
 
.LINK
    <unknown>
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/unrestrictCodeExecution' -Replace '{MachineID}',$MachineID
            Method = 'post'
            
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Start-MdMachineLiveResponse {
<#
.SYNOPSIS
    Runs a sequence of live response commands on a device
 
.DESCRIPTION
    Runs a sequence of live response commands on a device
 
    Scopes required (delegate auth): Machine.LiveResponse
 
.PARAMETER Comment
    A comment to associate to the isolation
 
.PARAMETER Commands
    The live response commands to execute.
Example:
@{
    type = "RunScript"
    params = @(
        @{
            key = "ScriptName"
            value = "minidump.ps1"
        },
        @{
            key = "Args"
            value = "OfficeClickToRun"
        }
    )
}
 
.PARAMETER MachineID
    ID of the machine to execute a live response script upon
 
.EXAMPLE
    PS C:\> Start-MdMachineLiveResponse -Comment $comment -Commands $commands -MachineID $machineid
 
    Run live response api commands for a single machine
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/run-live-response?view=o365-worldwide
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Comment,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [array]
        $Commands,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $MachineID
    )
    process {
        $__mapping = @{
            'Comment' = 'Comment'
            'Commands' = 'Commands'
        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @('Comment','Commands') -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'machines/{MachineID}/runliveresponse' -Replace '{MachineID}',$MachineID
            Method = 'post'
            RequiredScopes = 'Machine.LiveResponse'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdRecommendation {
<#
.SYNOPSIS
    Retrieves a list of all security recommendations affecting the organization.
 
.DESCRIPTION
    Retrieves a list of all security recommendations affecting the organization.
 
    Scopes required (delegate auth): SecurityRecommendation.Read
 
.PARAMETER RecommendationID
    ID of the recommendation to retrieve.
 
.EXAMPLE
    PS C:\> Get-MdRecommendation -RecommendationID $recommendationid
 
    <insert description here>
 
.EXAMPLE
    PS C:\> Get-MdRecommendation
 
    Lists all security recommendations
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-all-recommendations?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GetRecommendationById')]
        [Alias('Id')]
        [string]
        $RecommendationID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'recommendations'
            Method = 'get'
            RequiredScopes = 'SecurityRecommendation.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        if ($RecommendationID) { $__param.Path += "/$RecommendationID" }
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdRecommendationMachineReference {
<#
.SYNOPSIS
    Retrieves a list of devices associated with the security recommendation.
 
.DESCRIPTION
    Retrieves a list of devices associated with the security recommendation.
 
    Scopes required (delegate auth): SecurityRecommendation.Read
 
.PARAMETER RecommendationID
    ID of the recommendation for which to retrieve devices.
 
.EXAMPLE
    PS C:\> Get-MdRecommendationMachineReference -RecommendationID $recommendationid
 
    Retrieves a list of devices associated with the security recommendation.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-recommendation-machines?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $RecommendationID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'recommendations/{RecommendationID}/machineReferences' -Replace '{RecommendationID}',$RecommendationID
            Method = 'get'
            RequiredScopes = 'SecurityRecommendation.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdRecommendationSoftware {
<#
.SYNOPSIS
    Retrieves a security recommendation related to a specific software.
 
.DESCRIPTION
    Retrieves a security recommendation related to a specific software.
 
    Scopes required (delegate auth): SecurityRecommendation.Read
 
.PARAMETER RecommendationID
    ID of the recommendation for which to retrieve software information.
 
.EXAMPLE
    PS C:\> Get-MdRecommendationSoftware -RecommendationID $recommendationid
 
    Retrieves a security recommendation related to a specific software.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/list-recommendation-software?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $RecommendationID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'recommendations/{RecommendationID}/software' -Replace '{RecommendationID}',$RecommendationID
            Method = 'get'
            RequiredScopes = 'SecurityRecommendation.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdRecommendationVulnerability {
<#
.SYNOPSIS
    Retrieves a list of vulnerabilities associated with the security recommendation.
 
.DESCRIPTION
    Retrieves a list of vulnerabilities associated with the security recommendation.
 
    Scopes required (delegate auth): Vulnerability.Read
 
.PARAMETER RecommendationID
    ID of the recommendation for which to retrieve vulnerabilities.
 
.EXAMPLE
    PS C:\> Get-MdRecommendationVulnerability -RecommendationID $recommendationid
 
    Retrieves a list of vulnerabilities associated with the security recommendation.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-recommendation-vulnerabilities?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $RecommendationID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'recommendations/{RecommendationID}/vulnerabilities' -Replace '{RecommendationID}',$RecommendationID
            Method = 'get'
            RequiredScopes = 'Vulnerability.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdSoftware {
<#
.SYNOPSIS
    Retrieves the organization software inventory.
 
.DESCRIPTION
    Retrieves the organization software inventory.
 
    Scopes required (delegate auth): Software.Read
 
.PARAMETER SoftwareID
    ID of the software to retrieve.
 
.EXAMPLE
    PS C:\> Get-MdSoftware -SoftwareID $softwareid
 
    <insert description here>
 
.EXAMPLE
    PS C:\> Get-MdSoftware
 
    Lists all security recommendations
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-software?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GetProductById')]
        [Alias('Id')]
        [string]
        $SoftwareID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'software'
            Method = 'get'
            RequiredScopes = 'Software.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        if ($SoftwareID) { $__param.Path += "/$SoftwareID" }
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdSoftwareDistribution {
<#
.SYNOPSIS
    Shows the distribution of versions of a software in your organization.
 
.DESCRIPTION
    Shows the distribution of versions of a software in your organization.
 
    Scopes required (delegate auth): Software.Read
 
.PARAMETER SoftwareID
    ID of the software for which to retrieve distribution data.
 
.EXAMPLE
    PS C:\> Get-MdSoftwareDistribution -SoftwareID $softwareid
 
    Shows the distribution of versions of the specified software in your organization.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-software-ver-distribution?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $SoftwareID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'software/{SoftwareID}/distribution' -Replace '{SoftwareID}',$SoftwareID
            Method = 'get'
            RequiredScopes = 'Software.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdSoftwareMachinereference {
<#
.SYNOPSIS
    Retrieve a list of device references that has this software installed.
 
.DESCRIPTION
    Retrieve a list of device references that has this software installed.
 
    Scopes required (delegate auth): Software.Read
 
.PARAMETER SoftwareID
    ID of the software for which to retrieve devices that have it installed.
 
.EXAMPLE
    PS C:\> Get-MdSoftwareMachinereference -SoftwareID $softwareid
 
    Retrieve a list of device references that has this software installed.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-machines-by-software?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $SoftwareID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'software/{SoftwareID}/machineReferences' -Replace '{SoftwareID}',$SoftwareID
            Method = 'get'
            RequiredScopes = 'Software.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdSoftwareVulnerability {
<#
.SYNOPSIS
    Retrieve a list of vulnerabilities in the installed software.
 
.DESCRIPTION
    Retrieve a list of vulnerabilities in the installed software.
 
    Scopes required (delegate auth): Vulnerability.Read
 
.PARAMETER SoftwareID
    ID of the software for which to retrieve devices that have it installed.
 
.EXAMPLE
    PS C:\> Get-MdSoftwareVulnerability -SoftwareID $softwareid
 
    Retrieve a list of vulnerabilities in the installed software.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-vuln-by-software?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $SoftwareID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'software/{SoftwareID}/vulnerabilities' -Replace '{SoftwareID}',$SoftwareID
            Method = 'get'
            RequiredScopes = 'Vulnerability.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdVulnerability {
<#
.SYNOPSIS
    Retrieves a list of all the vulnerabilities.
 
.DESCRIPTION
    Retrieves a list of all the vulnerabilities.
 
    Scopes required (delegate auth): Vulnerability.Read
 
.PARAMETER VulnerabilityID
    ID of the vulnerability to retrieve.
 
.EXAMPLE
    PS C:\> Get-MdVulnerability -VulnerabilityID $vulnerabilityid
 
    <insert description here>
 
.EXAMPLE
    PS C:\> Get-MdVulnerability
 
    Retrieves a list of all the vulnerabilities.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-all-vulnerabilities?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'GetVulnerability')]
        [Alias('Id')]
        [string]
        $VulnerabilityID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'vulnerabilities'
            Method = 'get'
            RequiredScopes = 'Vulnerability.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        if ($VulnerabilityID) { $__param.Path += "/$VulnerabilityID" }
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Get-MdVulnerableMachine {
<#
.SYNOPSIS
    Retrieves a list of devices affected by a vulnerability.
 
.DESCRIPTION
    Retrieves a list of devices affected by a vulnerability.
 
    Scopes required (delegate auth): Vulnerability.Read
 
.PARAMETER VulnerabilityID
    ID of the vulnerability for which to retrieve affected devices.
 
.EXAMPLE
    PS C:\> Get-MdVulnerableMachine -VulnerabilityID $vulnerabilityid
 
    Retrieves a list of devices affected by a vulnerability.
 
.LINK
    https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/get-machines-by-vulnerability?view=o365-worldwide
#>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [Alias('Id')]
        [string]
        $VulnerabilityID
    )
    process {
        $__mapping = @{

        }

        $__param = @{
            Body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Header = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
            Path = 'vulnerabilities/{VulnerabilityID}/machineReferences' -Replace '{VulnerabilityID}',$VulnerabilityID
            Method = 'get'
            RequiredScopes = 'Vulnerability.Read'
            Service = 'DefenderAPI.Endpoint'
        }
        
        $__param += $PSBoundParameters | ConvertTo-HashTable -Include 'ErrorAction', 'WarningAction', 'Verbose'

        try { Invoke-EntraRequest @__param }
        catch { $PSCmdlet.ThrowTerminatingError($_) }
    }
}

function Invoke-MSecAdvancedHuntingQuery {
    <#
    .SYNOPSIS
        Execute an advanced hunting query.
     
    .DESCRIPTION
        Execute an advanced hunting query.
 
        Requires being connected to the "security" service.
        To establish a connection, use the "Connect-MdeService" command with the parameter '-Service "security"'
        Example:
        Connect-DefenderAPIService -Service security -DeviceCode -ClientID $ClientID -TenantID $TenantID
         
        Scopes required (delegate auth): AdvancedHunting.Read
     
    .PARAMETER Query
        The hunting query to execute.
     
    .EXAMPLE
        PS C:\> Invoke-MSecAdvancedHuntingQuery -Query 'DeviceProcessEvents | where InitiatingProcessFileName =~ \"powershell.exe\" | project Timestamp, FileName, InitiatingProcessFileName | order by Timestamp desc | limit 2'
 
        Executes the query, searching for the latest two powershell processes
     
    .LINK
        https://docs.microsoft.com/en-us/microsoft-365/security/defender/api-advanced-hunting?view=o365-worldwide
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')]
        [string]
        $Query
    )

    process {
        $__mapping = @{
            'Query' = 'Query'
        }
        $__body = $PSBoundParameters | ConvertTo-HashTable -Include @('Query') -Mapping $__mapping
        $__query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping
        $__path = 'advancedhunting/run'
    
        Invoke-EntraRequest -Service 'DefenderAPI.Security' -Path $__path -Method post -Body $__body -Query $__query -RequiredScopes 'AdvancedHunting.Read' | ConvertFrom-AdvancedQuery
    }
}

<#
This is an example configuration file
 
By default, it is enough to have a single one of them,
however if you have enough configuration settings to justify having multiple copies of it,
feel totally free to split them into multiple files.
#>


<#
# Example Configuration
Set-PSFConfig -Module 'DefenderAPI' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'"
#>


Set-PSFConfig -Module 'DefenderAPI' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging."
Set-PSFConfig -Module 'DefenderAPI' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments."

<#
Stored scriptblocks are available in [PsfValidateScript()] attributes.
This makes it easier to centrally provide the same scriptblock multiple times,
without having to maintain it in separate locations.
 
It also prevents lengthy validation scriptblocks from making your parameter block
hard to read.
 
Set-PSFScriptblock -Name 'DefenderAPI.ScriptBlockName' -Scriptblock {
     
}
#>


# Available Tokens
$script:_DefenderTokens = @{}

# Endpoint Configuration for Requests
$script:_DefenderEndpoints = @{}

$script:_strings = Get-PSFLocalizedString -Module DefenderAPI

# Registers the default service configurations
$endpointCfg = @{
    Name          = 'DefenderAPI.Endpoint'
    ServiceUrl    = 'https://api.securitycenter.microsoft.com/api'
    Resource      = 'https://api.securitycenter.microsoft.com'
    DefaultScopes = @()
    Header        = @{ 'Content-Type' = 'application/json' }
    HelpUrl       = 'https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/api/apis-intro?view=o365-worldwide'
}
Register-EntraService @endpointCfg

$securityCfg = @{
    Name          = 'DefenderAPI.Security'
    ServiceUrl    = 'https://api.security.microsoft.com/api'
    Resource      = 'https://security.microsoft.com/mtp/'
    DefaultScopes = @('AdvancedHunting.Read')
    Header        = @{ 'Content-Type' = 'application/json' }
    HelpUrl       = 'https://learn.microsoft.com/en-us/microsoft-365/security/defender/api-create-app-web?view=o365-worldwide'
}
Register-EntraService @securityCfg

New-PSFLicense -Product 'DefenderAPI' -Manufacturer 'Friedrich Weinmann' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2024-03-11") -Text @"
Copyright (c) 2024 Friedrich Weinmann
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"@

#endregion Load compiled code