Workloads/SecurityCompliance.ps1

function Connect-MSCloudLoginSecurityCompliance
{
    [CmdletBinding()]
    param()

    $InformationPreference = 'SilentlyContinue'
    $ProgressPreference = 'SilentlyContinue'
    $source = 'Connect-MSCloudLoginSecurityCompliance'

    Add-MSCloudLoginAssistantEvent -Message "Connection Profile: $($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter | Out-String)" -Source $source

    # Only a known token expiry ends the connection, certificate and credential sessions renew their tokens.
    if ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected)
    {
        $null = Test-MSCloudLoginConnectionReusable -WorkloadProfile $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter `
            -TokenBasedAuthTypes @() `
            -Source $source
    }

    if ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected)
    {
        if ($Script:MSCloudLoginCurrentLoadedModule -eq 'SC' -and
            $null -ne (Get-Command -Name 'Get-ComplianceSearch' -ErrorAction SilentlyContinue))
        {
            return
        }

        # Shared commands such as Get-Group must resolve to the Security & Compliance proxy module.
        if (Restore-MSCloudLoginProxyModule -ProbeCommand 'Get-ComplianceSearch' -Source $source)
        {
            $Script:MSCloudLoginCurrentLoadedModule = 'SC'
            return
        }

        Add-MSCloudLoginAssistantEvent -Message 'Security & Compliance proxy module is no longer loaded, reconnecting' -Source $source
        $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
    }

    $loadedModules = Get-Module
    Add-MSCloudLoginAssistantEvent -Message "The following modules are already loaded: $loadedModules" -Source $source

    Remove-MSCloudLoginProxyModule -ProbeCommand 'Get-ComplianceSearch' -Source $source

    [array]$activeSessions = Get-PSSession | Where-Object -FilterScript { $_.ComputerName -like '*ps.compliance.protection*' -and $_.State -eq 'Opened' }

    if ($activeSessions.Length -ge 1)
    {
        Add-MSCloudLoginAssistantEvent -Message "Found {$($activeSessions.Length)} existing Security and Compliance Session" -Source $source
        $ProxyModule = Import-PSSession $activeSessions[0] `
            -DisableNameChecking `
            -AllowClobber `
            -Verbose:$false
        Add-MSCloudLoginAssistantEvent -Message "Imported session into $ProxyModule" -Source $source
        Import-Module $ProxyModule -Global `
            -Verbose:$false | Out-Null
        $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CompleteConnection($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.MultiFactorAuthentication)
        $Script:MSCloudLoginCurrentLoadedModule = 'SC'
        Add-MSCloudLoginAssistantEvent -Message 'Reloaded the Security & Compliance Module' -Source $source
        return
    }
    Add-MSCloudLoginAssistantEvent -Message 'No Active Connections to Security & Compliance were found.' -Source $source
    #endregion

    if ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AuthenticationType -eq 'ServicePrincipalWithThumbprint')
    {
        Add-MSCloudLoginAssistantEvent -Message "Attempting to connect to Security and Compliance using AAD App {$($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ApplicationID)}" -Source $source
        try
        {
            Add-MSCloudLoginAssistantEvent -Message 'Connecting to Security & Compliance with Service Principal and Certificate Thumbprint' -Source $source
            Connect-IPPSSession -AppId $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ApplicationId `
                -Organization $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.TenantId `
                -CertificateThumbprint $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CertificateThumbprint `
                -EnableSearchOnlySession:$Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.EnableSearchOnlySession `
                -ShowBanner:$false `
                -ConnectionUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ConnectionUrl `
                -AzureADAuthorizationEndpointUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AzureADAuthorizationEndpointUri `
                -ErrorAction Stop | Out-Null
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CompleteConnection()
        }
        catch
        {
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
            Add-MSCloudLoginAssistantEvent -Message "Failed to connect to Security & Compliance with Certificate Thumbprint: $($_.Exception.Message)" -Source $source -EntryType 'Error'
            throw
        }
    }
    elseif ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AuthenticationType -eq 'ServicePrincipalWithPath')
    {
        try
        {
            Add-MSCloudLoginAssistantEvent -Message 'Connecting to Security & Compliance with Service Principal and Certificate Path' -Source $source
            Connect-IPPSSession -AppId $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ApplicationId `
                -CertificateFilePath $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CertificatePath `
                -Organization $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.TenantId `
                -EnableSearchOnlySession:$Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.EnableSearchOnlySession `
                -CertificatePassword $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CertificatePassword `
                -ConnectionUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ConnectionUrl `
                -AzureADAuthorizationEndpointUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AzureADAuthorizationEndpointUri  `
                -ShowBanner:$false `
                -ErrorAction Stop | Out-Null
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CompleteConnection()
        }
        catch
        {
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
            Add-MSCloudLoginAssistantEvent -Message "Failed to connect to Security & Compliance with Certificate Path: $($_.Exception.Message)" -Source $source -EntryType 'Error'
            throw
        }
    }
    elseif ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AuthenticationType -eq 'CredentialsWithTenantId')
    {
        try
        {
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AzureADAuthorizationEndpointUri = `
                $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AzureADAuthorizationEndpointUri.Replace('/organizations', "/$($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.TenantId)")
            Add-MSCloudLoginAssistantEvent -Message 'Connecting to Security & Compliance with Credentials & TenantId' -Source $source
            Connect-IPPSSession -Credential $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Credentials `
                -ConnectionUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ConnectionUrl `
                -AzureADAuthorizationEndpointUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AzureADAuthorizationEndpointUri `
                -DelegatedOrganization $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.TenantId `
                -EnableSearchOnlySession:$Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.EnableSearchOnlySession `
                -ShowBanner:$false `
                -ErrorAction Stop | Out-Null
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CompleteConnection()
        }
        catch
        {
            if ((Test-MSCloudLoginMFARequiredError -ErrorRecord $_) -and -not (Assert-IsNonInteractiveShell))
            {
                Add-MSCloudLoginAssistantEvent -Message "Could not connect IPPSSession with Credentials & TenantId, account requires MFA: {$($_.Exception.Message)}" -Source $source
                Connect-MSCloudLoginSecurityComplianceMFA -TenantId $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.TenantId
            }
            else
            {
                $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
                Add-MSCloudLoginAssistantEvent -Message "Failed to connect to Security & Compliance with Credentials & TenantId: $($_.Exception.Message)" -Source $source -EntryType 'Error'
                throw
            }
        }
    }
    elseif ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AuthenticationType -eq 'AccessTokens')
    {
        Add-MSCloudLoginAssistantEvent -Message 'Connecting to Security & Compliance with Access Token' -Source $source
        try
        {
            $accessToken = Get-MSCloudLoginAccessTokenValue -Token $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AccessTokens[0]
            Connect-MSCloudLoginSecurityComplianceWithAccessToken -AccessToken $accessToken
        }
        catch
        {
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
            Add-MSCloudLoginAssistantEvent -Message "Failed to connect to Security & Compliance with Access Token: $($_.Exception.Message)" -Source $source -EntryType 'Error'
            throw
        }
    }
    elseif ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AuthenticationType -eq 'Identity')
    {
        Add-MSCloudLoginAssistantEvent -Message 'Connecting to Security & Compliance with Managed Identity' -Source $source
        try
        {
            if ([System.String]::IsNullOrEmpty($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ResourceUrl))
            {
                throw "No Security & Compliance resource URL is defined for environment '$($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.EnvironmentName)'."
            }

            $accessToken = Get-AuthToken -Resource $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ResourceUrl -Identity
            Connect-MSCloudLoginSecurityComplianceWithAccessToken -AccessToken $accessToken
        }
        catch
        {
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
            Add-MSCloudLoginAssistantEvent -Message "Failed to connect to Security & Compliance with Managed Identity: $($_.Exception.Message)" -Source $source -EntryType 'Error'
            throw
        }
    }
    elseif ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AuthenticationType -in @('Credentials', 'CredentialsWithApplicationId'))
    {
        try
        {
            Add-MSCloudLoginAssistantEvent -Message 'Connecting to Security & Compliance with Credentials' -Source $source
            Connect-IPPSSession -Credential $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Credentials `
                -ConnectionUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ConnectionUrl `
                -AzureADAuthorizationEndpointUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AzureADAuthorizationEndpointUri `
                -EnableSearchOnlySession:$Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.EnableSearchOnlySession `
                -ShowBanner:$false `
                -ErrorAction Stop | Out-Null
            $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CompleteConnection()
        }
        catch
        {
            if ((Test-MSCloudLoginMFARequiredError -ErrorRecord $_) -and -not (Assert-IsNonInteractiveShell))
            {
                Add-MSCloudLoginAssistantEvent -Message "Could not connect IPPSSession with Credentials, account requires MFA: {$($_.Exception.Message)}" -Source $source
                Connect-MSCloudLoginSecurityComplianceMFA
            }
            else
            {
                $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
                Add-MSCloudLoginAssistantEvent -Message "Failed to connect to Security & Compliance with Credentials: $($_.Exception.Message)" -Source $source -EntryType 'Error'
                throw
            }
        }
    }
    else
    {
        throw "Authentication type '$($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AuthenticationType)' is not supported for workload 'SecurityComplianceCenter'."
    }

    $Script:MSCloudLoginCurrentLoadedModule = 'SC'
}

<#
.SYNOPSIS
    Connects to Security & Compliance with an access token.
 
.DESCRIPTION
    Uses the TenantId of the workload as the organization. The workload is marked as
    connected with the expiry of the token only after Connect-IPPSSession succeeds.
 
.PARAMETER AccessToken
    The access token for the Security & Compliance resource, with or without the 'Bearer ' prefix.
#>

function Connect-MSCloudLoginSecurityComplianceWithAccessToken
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $AccessToken
    )

    $workloadProfile = $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter

    $tenantGuid = [System.Guid]::Empty
    if ([System.String]::IsNullOrEmpty($workloadProfile.TenantId) -or [System.Guid]::TryParse($workloadProfile.TenantId, [ref]$tenantGuid))
    {
        throw "TenantId must be the initial domain of the tenant (e.g. contoso.onmicrosoft.com) to connect to Security & Compliance with an access token."
    }

    $AccessToken = $AccessToken -replace '^Bearer\s+', ''
    $tokenExpiresOn = Get-MSCloudLoginAccessTokenExpiry -Token $AccessToken
    if ($null -ne $tokenExpiresOn -and $tokenExpiresOn -le [System.DateTime]::Now)
    {
        throw "The access token for Security & Compliance expired at {$tokenExpiresOn}. Provide a new access token."
    }

    Connect-IPPSSession -AccessToken $AccessToken `
        -Organization $workloadProfile.TenantId `
        -ConnectionUri $workloadProfile.ConnectionUrl `
        -AzureADAuthorizationEndpointUri $workloadProfile.AzureADAuthorizationEndpointUri `
        -EnableSearchOnlySession:$workloadProfile.EnableSearchOnlySession `
        -ShowBanner:$false `
        -ErrorAction Stop | Out-Null
    $workloadProfile.CompleteConnection($false, $tokenExpiresOn)
}

function Connect-MSCloudLoginSecurityComplianceMFA
{
    [CmdletBinding()]
    param(
        [Parameter()]
        [System.String]
        $TenantId
    )

    $ProgressPreference = 'SilentlyContinue'
    $InformationPreference = 'SilentlyContinue'
    $source = 'Connect-MSCloudLoginSecurityComplianceMFA'

    try
    {
        Add-MSCloudLoginAssistantEvent -Message 'Creating a new Security and Compliance Session using MFA' -Source $source
        if ([System.String]::IsNullOrEmpty($TenantId))
        {
            Connect-IPPSSession -UserPrincipalName $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Credentials.UserName `
                -ConnectionUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ConnectionUrl `
                -EnableSearchOnlySession:$Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.EnableSearchOnlySession `
                -ErrorAction Stop `
                -Verbose:$false  `
                -ShowBanner:$false | Out-Null
        }
        else
        {
            Connect-IPPSSession -UserPrincipalName $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Credentials.UserName `
                -ConnectionUri $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ConnectionUrl `
                -EnableSearchOnlySession:$Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.EnableSearchOnlySession `
                -ErrorAction Stop `
                -Verbose:$false `
                -DelegatedOrganization $TenantId `
                -ShowBanner:$false | Out-Null
        }
        Add-MSCloudLoginAssistantEvent -Message 'New Session with MFA created successfully' -Source $source
        $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CompleteConnection($true)
    }
    catch
    {
        $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
        Add-MSCloudLoginAssistantEvent -Message "Failed to connect to Security & Compliance using MFA: $($_.Exception.Message)" -Source $source -EntryType 'Error'
        throw
    }
}

function Disconnect-MSCloudLoginSecurityCompliance
{
    [CmdletBinding()]
    param()

    $source = 'Disconnect-MSCloudLoginSecurityCompliance'

    if ($Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected)
    {
        Add-MSCloudLoginAssistantEvent -Message 'Attempting to disconnect from Security & Compliance Center' -Source $source
        Disconnect-MSCloudLoginExchangeConnection -SecurityCompliance -Source $source
        $Script:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connected = $false
        Add-MSCloudLoginAssistantEvent -Message 'Successfully disconnected from Security & Compliance Center' -Source $source
    }
    else
    {
        Add-MSCloudLoginAssistantEvent -Message 'No connections to Security & Compliance Center were found.' -Source $source
    }
}