SendGridTools.psm1

#Region '.\Classes\SGCustom.ps1' 0
class UnixTime {
    [datetime]$DateTime
    [int]$UnixTimestamp

    static [datetime]EpochStart() {
        return ([datetime]::new(1970, 1, 1, 0, 0, 0, ([DateTimeKind]::Utc)))
    }
    
    UnixTime () {
        $this.DateTime = Get-Date
        $this.UnixTimestamp = $this.ToUnixTime()
    }
    UnixTime([datetime]$DateTime) {
        $this.DateTime = $DateTime
        $this.UnixTimestamp = $this.ToUnixTime()
        #$EpochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, ([DateTimeKind]::Utc))
        #$EpochStart = [UnixTime]::EpochStart()
        #$this.UnixTimestamp = [int]([datetime]$DateTime - $EpochStart).TotalSeconds
        
    }

    UnixTime([int]$UnixTimestamp) {
        $this.UnixTimestamp = $UnixTimestamp
        $this.DateTime = $this.ToDateTime()
    }

    [datetime] ToDateTime() {
        return [datetime]::new(1970, 1, 1, 0, 0, 0, 0).AddSeconds($this.UnixTimestamp)
    }

    [datetime] ToDateTimeUTC() {
        return ([UnixTime]::EpochStart()).AddSeconds($this.UnixTimestamp)
    }

    [int] ToUnixTime() {
        $EpochStart = [UnixTime]::EpochStart()
        return [int]([datetime]$this.DateTime - $EpochStart).TotalSeconds
    }

    [string] ToString() {
        return $this.UnixTimestamp.ToString()
    }
}
class SGASM {
    [int]$GroupId
    [int[]]$GroupsToDisplay

    SGASM() {
        $this.GroupId = 0
        $this.GroupsToDisplay = @()
    }

    SGASM([int]$GroupId) {
        $this.GroupId = $GroupId
        $this.GroupsToDisplay = @()
    }
    SGASM([int]$GroupId, [int[]]$GroupsToDisplay) {
        $this.GroupId = $GroupId
        $this.GroupsToDisplay = @()
    }
    [string] ToString() {
        return $this.GroupId.ToString()
    }
}
#EndRegion '.\Classes\SGCustom.ps1' 65
#Region '.\Classes\SGScopes.ps1' 0
class SendGridScopes : System.Management.Automation.IValidateSetValuesGenerator {
    [string[]]$Scopes

    SendGridScopes () {
    }
    
    SendGridScopes ([string[]]$Scopes) {
        $this.ValidateScopes($Scopes)
    }
    
    hidden [void] ValidateScopes([string[]]$Scopes) {
        $InvalidScopes = $Scopes | Where-Object { $_ -notin $this.GetValidValues() }
        if ($InvalidScopes) {
            throw "Invalid scope(s): $($invalidScopes -join ', ')"
        }
        $this.Scopes = $Scopes
    }

    static [string[]] ValidScopes() {
        return [string[]]@('access_settings.activity.read',
            'access_settings.whitelist.create',
            'access_settings.whitelist.delete',
            'access_settings.whitelist.read',
            'access_settings.whitelist.update',
            'alerts.create',
            'alerts.delete',
            'alerts.read',
            'alerts.update',
            'api_keys.create',
            'api_keys.delete',
            'api_keys.read',
            'api_keys.update',
            'asm.groups.create',
            'asm.groups.delete',
            'asm.groups.read',
            'asm.groups.update',
            'billing.create',
            'billing.delete',
            'billing.read',
            'billing.update',
            'browsers.stats.read',
            'categories.create',
            'categories.delete',
            'categories.read',
            'categories.stats.read',
            'categories.stats.sums.read',
            'categories.update',
            'clients.desktop.stats.read',
            'clients.phone.stats.read',
            'clients.stats.read',
            'clients.tablet.stats.read',
            'clients.webmail.stats.read',
            'devices.stats.read',
            'email_activity.read',
            'geo.stats.read',
            'ips.assigned.read',
            'ips.pools.create',
            'ips.pools.delete',
            'ips.pools.ips.create',
            'ips.pools.ips.delete',
            'ips.pools.ips.read',
            'ips.pools.ips.update',
            'ips.pools.read',
            'ips.pools.update',
            'ips.read',
            'ips.warmup.create',
            'ips.warmup.delete',
            'ips.warmup.read',
            'ips.warmup.update',
            'mail_settings.address_whitelist.read',
            'mail_settings.address_whitelist.update',
            'mail_settings.bounce_purge.read',
            'mail_settings.bounce_purge.update',
            'mail_settings.footer.read',
            'mail_settings.footer.update',
            'mail_settings.forward_bounce.read',
            'mail_settings.forward_bounce.update',
            'mail_settings.forward_spam.read',
            'mail_settings.forward_spam.update',
            'mail_settings.plain_content.read',
            'mail_settings.plain_content.update',
            'mail_settings.read,',
            'mail_settings.template.read',
            'mail_settings.template.update',
            'mail.batch.create',
            'mail.batch.delete',
            'mail.batch.read',
            'mail.batch.update',
            'mail.send',
            'mailbox_providers.stats.read',
            'marketing_campaigns.create',
            'marketing_campaigns.delete',
            'marketing_campaigns.read',
            'marketing_campaigns.update',
            'partner_settings.new_relic.read',
            'partner_settings.new_relic.update',
            'partner_settings.read',
            'stats.global.read',
            'stats.read',
            'subusers.create',
            'subusers.credits.create',
            'subusers.credits.delete',
            'subusers.credits.read',
            'subusers.credits.remaining.create',
            'subusers.credits.remaining.delete',
            'subusers.credits.remaining.read',
            'subusers.credits.remaining.update',
            'subusers.credits.update',
            'subusers.delete',
            'subusers.monitor.create',
            'subusers.monitor.delete',
            'subusers.monitor.read',
            'subusers.monitor.update',
            'subusers.read',
            'subusers.reputations.read',
            'subusers.stats.monthly.read',
            'subusers.stats.read',
            'subusers.stats.sums.read',
            'subusers.summary.read',
            'subusers.update',
            'suppression.blocks.create',
            'suppression.blocks.delete',
            'suppression.blocks.read',
            'suppression.blocks.update',
            'suppression.bounces.create',
            'suppression.bounces.delete',
            'suppression.bounces.read',
            'suppression.bounces.update',
            'suppression.create',
            'suppression.delete',
            'suppression.invalid_emails.create',
            'suppression.invalid_emails.delete',
            'suppression.invalid_emails.read',
            'suppression.invalid_emails.update',
            'suppression.read',
            'suppression.spam_reports.create',
            'suppression.spam_reports.delete',
            'suppression.spam_reports.read',
            'suppression.spam_reports.update',
            'suppression.unsubscribes.create',
            'suppression.unsubscribes.delete',
            'suppression.unsubscribes.read',
            'suppression.unsubscribes.update',
            'suppression.update',
            'teammates.create',
            'teammates.read',
            'teammates.update',
            'teammates.delete',
            'templates.create',
            'templates.delete',
            'templates.read',
            'templates.update',
            'templates.versions.activate.create',
            'templates.versions.activate.delete',
            'templates.versions.activate.read',
            'templates.versions.activate.update',
            'templates.versions.create',
            'templates.versions.delete',
            'templates.versions.read',
            'templates.versions.update',
            'tracking_settings.click.read',
            'tracking_settings.click.update',
            'tracking_settings.google_analytics.read',
            'tracking_settings.google_analytics.update',
            'tracking_settings.open.read',
            'tracking_settings.open.update',
            'tracking_settings.read',
            'tracking_settings.subscription.read',
            'tracking_settings.subscription.update',
            'user.account.read',
            'user.credits.read',
            'user.email.create',
            'user.email.delete',
            'user.email.read',
            'user.email.update',
            'user.multifactor_authentication.create',
            'user.multifactor_authentication.delete',
            'user.multifactor_authentication.read',
            'user.multifactor_authentication.update',
            'user.password.read',
            'user.password.update',
            'user.profile.read',
            'user.profile.update',
            'user.scheduled_sends.create',
            'user.scheduled_sends.delete',
            'user.scheduled_sends.read',
            'user.scheduled_sends.update',
            'user.settings.enforced_tls.read',
            'user.settings.enforced_tls.update',
            'user.timezone.read',
            'user.username.read',
            'user.username.update',
            'user.webhooks.event.settings.read',
            'user.webhooks.event.settings.update',
            'user.webhooks.event.test.create',
            'user.webhooks.event.test.read',
            'user.webhooks.event.test.update',
            'user.webhooks.parse.settings.create',
            'user.webhooks.parse.settings.delete',
            'user.webhooks.parse.settings.read',
            'user.webhooks.parse.settings.update',
            'user.webhooks.parse.stats.read',
            'whitelabel.create',
            'whitelabel.delete',
            'whitelabel.read',
            'whitelabel.update')
    }

    [string[]] GetValidValues() {
        return [SendGridScopes]::ValidScopes()
    }
}
#EndRegion '.\Classes\SGScopes.ps1' 213
#Region '.\Classes\SGSession.ps1' 0
# The SendGridSession class manages a SendGrid session for the user.
class SendGridSession {
    # URL endpoint to the SendGrid API.
    [uri]$EndpointURL
    # Base URL to the SendGrid API.
    hidden [uri]$_BaseURL = 'https://api.sendgrid.com/v3'
    # Specifies if the session is connected.
    hidden [bool]$_Connected = $false
    # Stores the user's SendGrid API credentials.
    hidden [PSCredential]$_Credential
    # Tracks the time when the session was created.
    hidden [DateTime]$_CreateDateTime

    # Constructor function for SendGridSession.
    SendGridSession () {
        $null = [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    }
    
    <#
    .SYNOPSIS
        Constructs the URL for accessing the SendGrid API.
 
    .DESCRIPTION
        This private method builds a URL for accessing a specific resource in the SendGrid API.
 
    .PARAMETER Resource
        The specific resource to access in the SendGrid API.
    #>

    hidden [void]BuildEndpointURL([string]$Resource) {
        if ($null -eq $Resource -or $Resource -eq [String]::Empty) {
            # Remove old Endpoint URL to avoid old resource
            $this.EndpointURL = $null
        }
        else {
            [Text.StringBuilder]$URLBuild = [Text.StringBuilder]::new()
        
            $null = $URLBuild.Append($this._BaseURL)
            $null = $URLBuild.Append('/')
            $null = $URLBuild.Append($Resource)

            $this.EndpointURL = [uri]$URLBuild.ToString()
        }
    }

    <#
    .SYNOPSIS
        Connects to the SendGrid API.
 
    .DESCRIPTION
        This function establishes a connection to the SendGrid API by using the user's stored credentials.
    #>

    [void] Connect () {
        $SessionLifeTime = (Get-Date).AddHours(-12)
        if ($null -eq $this._CreateDateTime -or $SessionLifeTime -gt $this._CreateDateTime) {
            $this.Disconnect()
            throw 'Session lifetime exceeded, reconnect.'
        }
        if ($this._Credential -is [PSCredential]) {
            $this.BuildEndpointURL([string]'scopes')
            try {
                $Headers = @{
                    'Authorization' = ('Bearer {0}' -f $this._Credential.GetNetworkCredential().Password)
                    'Content-Type'  = 'application/json'
                }
                $null = Invoke-RestMethod -Method Get -Uri  $this.EndpointURL -Headers $Headers -ErrorAction Stop
                $this._Connected = $true
                $this._CreateDateTime = Get-Date # Used to refresh "session lifetime"
            }
            catch {
                $this._Connected = $false
                throw ('Unable to connect to Sendgrid. {0}' -f $_.Exception.Message)
            }
        }
        else {
            $this._Connected = $false
            throw ('Unable to connect to Sendgrid. No credentials saved in context.')
        }
    }
    <#
    .SYNOPSIS
        Connects to the SendGrid API with specified credentials.
 
    .DESCRIPTION
        This function establishes a connection to the SendGrid API by using the specified credentials.
 
    .PARAMETER Credential
        The user's SendGrid API credentials.
    #>

    [void] Connect ([PSCredential]$Credential) {
        $this._Credential = $Credential
        $this.BuildEndpointURL([string]'scopes')
        try {
            $Headers = @{
                'Authorization' = ('Bearer {0}' -f $this._Credential.GetNetworkCredential().Password)
                'Content-Type'  = 'application/json'
            }
            $null = Invoke-RestMethod -Method Get -Uri  $this.EndpointURL -Headers $Headers -ErrorAction Stop
            $this._Connected = $true
            $this._CreateDateTime = Get-Date
        }
        catch {
            $this._Connected = $false
            throw ('Unable to connect to Sendgrid. {0}' -f $_.Exception.Message)
        }
    }

    <#
    .SYNOPSIS
        Disconnects from the SendGrid API.
 
    .DESCRIPTION
        This function disconnects the current session from the SendGrid API.
    #>

    [void] Disconnect () {
        $this.BuildEndpointURL($null)
        $this._Credential = $null
        $this._Connected = $false
        $this._CreateDateTime = 0
    }

    <#
    .SYNOPSIS
        Sends a query to the SendGrid API.
 
    .DESCRIPTION
        This method sends a query to the SendGrid API and returns the results.
 
    .PARAMETER WebRequestMethod
        The HTTP method to use for the query (GET, POST, etc.).
 
    .PARAMETER Endpoint
        The specific endpoint in the SendGrid API to send the query to.
 
    .INPUTS
        Microsoft.PowerShell.Commands.WebRequestMethod, string.
 
    .OUTPUTS
        PSCustomObject[]
    #>

    [PSCustomObject[]] InvokeQuery ([Microsoft.PowerShell.Commands.WebRequestMethod]$WebRequestMethod, [string]$Endpoint) {
        if ($this._Connected -eq $false) {
            throw 'You must call the Connect-SendGrid cmdlet before calling any other cmdlets.'
        }
        $SessionLifeTime = (Get-Date).AddHours(-12)
        if ($null -eq $this._CreateDateTime -or $SessionLifeTime -gt $this._CreateDateTime) {
            $this.Disconnect()
            return 'Session lifetime exceeded, reconnect.'
        }
        else {
            $this.BuildEndpointURL($Endpoint)
            try {
                $Headers = @{
                    'Authorization' = "Bearer $($this._Credential.GetNetworkCredential().Password)"
                    'Content-Type'  = 'application/json'
                }
                $Query = (Invoke-RestMethod -Method $WebRequestMethod -Uri $this.EndpointURL -Headers $Headers -ErrorAction Stop)
                $this.BuildEndpointURL($null)
                $this._CreateDateTime = Get-Date
                return $Query
            }
            catch {
                $this.BuildEndpointURL($null)
                throw ('Unable to query Sendgrid: {0}' -f $_.Exception.Message)
            }
        }
    }
    
    <#
    .SYNOPSIS
        Sends a query to the SendGrid API.
 
    .DESCRIPTION
        This method sends a query to the SendGrid API and returns the results.
 
    .PARAMETER WebRequestMethod
        The HTTP method to use for the query (GET, POST, etc.).
 
    .PARAMETER Endpoint
        The specific endpoint in the SendGrid API to send the query to.
 
    .PARAMETER ContentBody
        The payload to send to the specified endpoint.
 
    .INPUTS
        Microsoft.PowerShell.Commands.WebRequestMethod, string, hashtable.
 
    .OUTPUTS
        PSCustomObject[]
    #>

    [PSCustomObject[]] InvokeQuery ([Microsoft.PowerShell.Commands.WebRequestMethod]$WebRequestMethod, [string]$Endpoint, [hashtable]$ContentBody) {
        $Body = $ContentBody | ConvertTo-Json -Depth 5 -ErrorAction Stop
        $SessionLifeTime = (Get-Date).AddHours(-12)
        if ($null -eq $this._CreateDateTime -or $SessionLifeTime -gt $this._CreateDateTime) {
            $this.Disconnect()
            return 'Session lifetime exceeded, reconnect.'
        }
        else {
            $this.BuildEndpointURL($Endpoint)
            try {
                $Headers = @{
                    'Authorization' = "Bearer $($this._Credential.GetNetworkCredential().Password)"
                    'Content-Type'  = 'application/json'
                }
                $Query = (Invoke-RestMethod -Method $WebRequestMethod -Uri $this.EndpointURL -Headers $Headers -Body $Body -ErrorAction Stop)
                $this.BuildEndpointURL($null)
                $this._CreateDateTime = Get-Date
                return $Query
            }
            catch {
                $this.BuildEndpointURL($null)
                throw ('Unable to query Sendgrid: {0}' -f $_.Exception.Message)
            }
        }
    }

    <#
    .SYNOPSIS
        Sends a query to the SendGrid API using on behalf of.
 
    .DESCRIPTION
        This method sends a query to the SendGrid API and returns the results using on behalf of.
 
    .PARAMETER WebRequestMethod
        The HTTP method to use for the query (GET, POST, etc.).
 
    .PARAMETER Endpoint
        The specific endpoint in the SendGrid API to send the query to.
 
    .PARAMETER OnBehalfOf
        The username of the subuser to send the query on behalf of.
 
    .INPUTS
        Microsoft.PowerShell.Commands.WebRequestMethod, string.
 
    .OUTPUTS
        PSCustomObject[]
    #>

    [PSCustomObject[]] InvokeQuery ([Microsoft.PowerShell.Commands.WebRequestMethod]$WebRequestMethod, [string]$Endpoint, [string]$OnBehalfOf) {
        if ($this._Connected -eq $false) {
            throw 'You must call the Connect-SendGrid cmdlet before calling any other cmdlets.'
        }
        $SessionLifeTime = (Get-Date).AddHours(-12)
        if ($null -eq $this._CreateDateTime -or $SessionLifeTime -gt $this._CreateDateTime) {
            $this.Disconnect()
            return 'Session lifetime exceeded, reconnect.'
        }
        else {
            $this.BuildEndpointURL($Endpoint)
            try {
                $Headers = @{
                    'Authorization' = "Bearer $($this._Credential.GetNetworkCredential().Password)"
                    'on-behalf-of'  = $OnBehalfOf
                    'Content-Type'  = 'application/json'
                }
                $Query = (Invoke-RestMethod -Method $WebRequestMethod -Uri $this.EndpointURL -Headers $Headers -ErrorAction Stop)
                $this.BuildEndpointURL($null)
                $this._CreateDateTime = Get-Date
                return $Query
            }
            catch {
                $this.BuildEndpointURL($null)
                throw ('Unable to query Sendgrid: {0}' -f $_.Exception.Message)
            }
        }
    }

    <#
    .SYNOPSIS
        Sends a query to the SendGrid API.
 
    .DESCRIPTION
        This method sends a query to the SendGrid API and returns the results.
 
    .PARAMETER WebRequestMethod
        The HTTP method to use for the query (GET, POST, etc.).
 
    .PARAMETER Endpoint
        The specific endpoint in the SendGrid API to send the query to.
 
    .PARAMETER ContentBody
        The payload to send to the specified endpoint.
     
    .PARAMETER OnBehalfOf
        The username of the subuser or account-id to send the query on behalf of.
 
    .INPUTS
        Microsoft.PowerShell.Commands.WebRequestMethod, string, hashtable.
 
    .OUTPUTS
        PSCustomObject[]
    #>

    [PSCustomObject[]] InvokeQuery ([Microsoft.PowerShell.Commands.WebRequestMethod]$WebRequestMethod, [string]$Endpoint, [hashtable]$ContentBody, [string]$OnBehalfOf) {
        $Body = $ContentBody | ConvertTo-Json -Depth 5 -ErrorAction Stop
        $SessionLifeTime = (Get-Date).AddHours(-12)
        if ($null -eq $this._CreateDateTime -or $SessionLifeTime -gt $this._CreateDateTime) {
            $this.Disconnect()
            return 'Session lifetime exceeded, reconnect.'
        }
        else {
            $this.BuildEndpointURL($Endpoint)
            try {
                $Headers = @{
                    'Authorization' = "Bearer $($this._Credential.GetNetworkCredential().Password)"
                    'on-behalf-of'  = $OnBehalfOf
                    'Content-Type'  = 'application/json'
                }
                $Query = (Invoke-RestMethod -Method $WebRequestMethod -Uri $this.EndpointURL -Headers $Headers -Body $Body -ErrorAction Stop)
                $this.BuildEndpointURL($null)
                $this._CreateDateTime = Get-Date
                return $Query
            }
            catch {
                $this.BuildEndpointURL($null)
                throw ('Unable to query Sendgrid: {0}' -f $_.Exception.Message)
            }
        }
    }

    [string]ToString() {
        return $this._Connected
    }
}
#EndRegion '.\Classes\SGSession.ps1' 323
#Region '.\Private\ConvertTo-SendGridAddress.ps1' 0
function ConvertTo-SendGridAddress {
    [CmdletBinding()]
    param (
        [Parameter(
            ValueFromPipeline = $true,
            Position = 0
        )]
        [Alias("EmailAddress")]
        [object[]]$Address
    )
    process {
        foreach ($A in $Address) {
            try {
                $EmailAddress = [System.Net.Mail.MailAddress]$A
                if ($null -eq $EmailAddress.DisplayName -or $EmailAddress.DisplayName -eq [string]::Empty) {
                    @{
                        email = $EmailAddress.Address
                    }
                }
                else {
                    @{
                        email = $EmailAddress.Address
                        name  = $EmailAddress.DisplayName
                    }
                }
            }
            catch {
                Write-Error "Invalid email address: $A" -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Private\ConvertTo-SendGridAddress.ps1' 33
#Region '.\Private\ConvertTo-TitleCase.ps1' 0
function ConvertTo-TitleCase {
    <#
    .SYNOPSIS
    The ConvertTo-TitleCase function converts a specified string to title case.
 
    .DESCRIPTION
    The ConvertTo-TitleCase function converts a specified string to title case using the Method in (Get-Culture).TextInfo
    All input strings will be converted to lowercase, because uppercase are considered to be acronyms.
 
    .EXAMPLE
    ConvertTo-TitleCase -InputObject 'roger johnsson'
    Roger Johnsson
 
    This example returns the string 'Roger Johnsson' which has capitalized the R and J chars.
 
    .EXAMPLE
    'roger johnsson', 'JOHN ROGERSSON' | ConvertTo-TitleCase
    Roger Johnsson
    John Rogersson
 
    This example returns the strings 'Roger Johnsson' and 'John Rogersson' which has capitalized the R and J chars.
 
    .LINK
        https://github.com/Omnicit/Omnicit/blob/master/docs/en-US/ConvertTo-TitleCase.md
    #>

    [Alias('ConvertTo-NameTitle')]
    [CmdletBinding(
        PositionalBinding,
        SupportsShouldProcess
    )]
    param (
        # Specifies one or more objects to be convert to title case strings.
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            Position = 0
        )]
        [AllowEmptyString()]
        [string[]]$InputObject
    )
    begin {
        $TextInfo = (Get-Culture).TextInfo
    }
    process {
        foreach ($String in $InputObject) {
            if ($PSCmdlet.ShouldProcess($String)) {
                $TextInfo.ToTitleCase($String.ToLower())
            }
        }
    }
}
#EndRegion '.\Private\ConvertTo-TitleCase.ps1' 52
#Region '.\Private\Invoke-SendGrid.ps1' 0
<#
.SYNOPSIS
This function is used to interact with the SendGrid API.
 
.DESCRIPTION
Invoke-SendGrid is a custom function designed to interact with the SendGrid API. It requires an active SendGridSession, which should be established via the Connect-SendGrid cmdlet before calling this function.
 
.PARAMETER Method
The web request method to use (GET, POST, PUT, DELETE etc).
 
.PARAMETER Namespace
The endpoint of the SendGrid API to interact with.
 
.PARAMETER ContentBody
(Optional) A hashtable containing the request body to send with the request.
 
.EXAMPLE
Invoke-SendGrid -Method GET -Namespace "mail/send"
 
#>

function Invoke-SendGrid {
    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # The web request method.
        [Parameter(
            Mandatory,
            HelpMessage = 'The web request method.',
            Position = 0
        )]
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestMethod]$Method,

        # The endpoint to use.
        [Parameter(
            Mandatory,
            HelpMessage = 'The SendGrid endpoint to use.',
            Position = 1
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Namespace,

        # The content body to send with the request.
        [Parameter(
            HelpMessage = 'The content body to send with the request.',
            Position = 2
        )]
        [ValidateNotNullOrEmpty()]
        [hashtable]$ContentBody,

        # The username of the subuser to send the query on behalf of.
        [Parameter(
            HelpMessage = 'The username of the subuser to send the query on behalf of.',
            Position = 3
        )]
        [ValidateNotNullOrEmpty()]
        [string]$OnBehalfOf
    )
    begin {
        # Function to get unique properties of an object.
        function Get-UniqueProperties {
            param (
                [Parameter(
                    Mandatory
                )]
                [object[]]$InputObject
            )
            # Get the properties of each object in the input array.
            $Members = foreach ($Object in $InputObject) {
                $Object | Get-Member -MemberType NoteProperty
            }
            # Return the unique properties.
            if ($null -ne $Members) {
                ($Members | Sort-Object -Property Name -Unique).Name | ConvertTo-TitleCase
            }
        }
    }
    process {
        if ($PSCmdlet.ShouldProcess("$Method : $Namespace")) {
            Write-Verbose "Starting process with method: $Method and namespace: $Namespace"
            # Check if session is not a SendGridSession.
            if ($script:Session -isnot [SendGridSession]) {
                throw 'You must call the Connect-SendGrid cmdlet before calling any other cmdlets.'
            }
            try {
                Write-Verbose 'Attempting to invoke query'
                # Invoke the query based on provided parameters.
                if ($PSBoundParameters.ContainsKey('ContentBody') -and $PSBoundParameters.ContainsKey('OnBehalfOf')) {
                    $Query = $script:Session.InvokeQuery($Method, $Namespace, $ContentBody, $OnBehalfOf)
                }
                elseif ($PSBoundParameters.ContainsKey('ContentBody')) {
                    $Query = $script:Session.InvokeQuery($Method, $Namespace, $ContentBody)
                }
                elseif ($PSBoundParameters.ContainsKey('OnBehalfOf')) {
                    $Query = $script:Session.InvokeQuery($Method, $Namespace, $OnBehalfOf)
                }
                else {
                    $Query = $script:Session.InvokeQuery($Method, $Namespace)
                }
            }
            catch {
                throw $_.Exception.Message
            }

            # Get unique properties.
            $Properties = Get-UniqueProperties -InputObject $Query
            
            # Handle PSObject array with a single 'Result' property.
            if ($Query -is [System.Management.Automation.PSObject[]] -and $Properties -eq 'Result' -and $Properties.Count -eq 1) {
                $Query = $Query.result
                $Properties = Get-UniqueProperties -InputObject $Query
            }

            # Process each object in the query.
            foreach ($Object in $Query) {
                # Create a new custom object.
                [PSCustomObject]$PSObject = [PSCustomObject]::new()

                # Process each property in the properties array.
                foreach ($Property in $Properties) {
                    # Check for inline properties.
                    if ($Object.$Property -is [System.Management.Automation.PSCustomObject]) {
                        $InlineProperties = Get-UniqueProperties -InputObject $Object.$Property

                        # Process each inline property.
                        foreach ($InlineProperty in $InlineProperties) {
                            $PSObject | Add-Member -MemberType NoteProperty -Name ('{0}{1}' -f ($Property -replace '[\s_-]+'), $($InlineProperty -replace '[\s_-]+'))  -Value $Object.$Property.$InlineProperty
                        }
                    }

                    # Switch based on the property type.
                    switch ($Object.$Property) {
                        { $_ -is [int64] -and $Property -match 'valid' } {
                            $PSObject | Add-Member -MemberType NoteProperty -Name ($Property -replace '[\s_-]+')  -Value ((Get-Date -Date '01-01-1970') + ([System.TimeSpan]::FromSeconds(($_))))
                            break
                        }
                        Default {
                            $PSObject | Add-Member -MemberType NoteProperty -Name ($Property -replace '[\s_-]+') -Value $Object.$Property -Force
                            break
                        }
                    }
                }
                if ($PSObject | Get-Member -Name 'Errors' -MemberType 'NoteProperty') {
                    throw $PSObject.Errors.Message
                }
                else {
                    $PSObject
                }
            }
        }
    }
}
#EndRegion '.\Private\Invoke-SendGrid.ps1' 154
#Region '.\Private\Remove-EmptyHashtable.ps1' 0
function Remove-EmptyHashtable {
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [alias('Splat', 'IDictionary')]
        [System.Collections.IDictionary[]]$Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun,
        [switch] $DoNotRemoveNull,
        [switch] $DoNotRemoveEmpty,
        [switch] $DoNotRemoveEmptyArray,
        [switch] $DoNotRemoveEmptyDictionary
    )
    foreach ($Hash in $Hashtable) {
        foreach ($Key in [object[]]$Hash.Keys) {
            if ($Key -notin $ExcludeParameter) {
                if ($Recursive) {
                    if ($Hash[$Key] -is [System.Collections.IDictionary]) {
                        if ($Hash[$Key].Count -eq 0) {
                            if (-not $DoNotRemoveEmptyDictionary) {
                                $Hash.Remove($Key)
                            }
                        }
                        else {
                            Remove-EmptyHashtable -Hashtable $Hash[$Key] -Recursive:$Recursive
                        }
                    }
                    else {
                        if (-not $DoNotRemoveNull -and $null -eq $Hash[$Key]) {
                            Write-Verbose -Message "Removing $Key from hashtable, because it is null."
                            $Hash.Remove($Key)
                        }
                        elseif (-not $DoNotRemoveEmpty -and $Hash[$Key] -is [string] -and $Hash[$Key] -eq '') {
                            Write-Verbose -Message "Removing $Key from hashtable, because it is empty."
                            $Hash.Remove($Key)
                        }
                        elseif (-not $DoNotRemoveEmptyArray -and $Hash[$Key] -is [System.Collections.IList] -and $Hash[$Key].Count -eq 0) {
                            Write-Verbose -Message "Removing $Key from hashtable, because it is an empty array."
                            $Hash.Remove($Key)
                        }
                    }
                }
                else {
                    if (-not $DoNotRemoveNull -and $null -eq $Hash[$Key]) {
                        Write-Verbose -Message "Removing $Key from hashtable, because it is null."
                        $Hash.Remove($Key)
                    }
                    elseif (-not $DoNotRemoveEmpty -and $Hash[$Key] -is [string] -and $Hash[$Key] -eq '') {
                        Write-Verbose -Message "Removing $Key from hashtable, because it is empty."
                        $Hash.Remove($Key)
                    }
                    elseif (-not $DoNotRemoveEmptyArray -and $Hash[$Key] -is [System.Collections.IList] -and $Hash[$Key].Count -eq 0) {
                        Write-Verbose -Message "Removing $Key from hashtable, because it is an empty array."
                        $Hash.Remove($Key)
                    }
                }
            }
        }
    }
    if ($Rerun) {
        for ($i = 0; $i -lt $Rerun; $i++) {
            Remove-EmptyHashtable -Hashtable $Hash -Recursive:$Recursive
        }
    }
}
#EndRegion '.\Private\Remove-EmptyHashtable.ps1' 71
#Region '.\Public\Confirm-SGAuthenticatedDomain.ps1' 0
function Confirm-SGAuthenticatedDomain {
    <#
    .SYNOPSIS
        Validates the authenticated domains within the current SendGrid instance.
         
    .DESCRIPTION
        Confirm-SGAuthenticatedDomain is used to validate the authenticated domains within the current SendGrid instance.
        An authenticated domain allows you to remove the "via" or "sent on behalf of" message that your recipients see when they read your emails.
        Authenticating a domain allows you to replace sendgrid.net with your personal sending domain.
        You will be required to create a subdomain so that SendGrid can generate the DNS records which you must give to your host provider.
         
        This function should be executed after the external DNS records have been applied.
 
    .PARAMETER UniqueId
        Specifies the unique ID of the branded link to validate. This parameter is mandatory.
 
    .EXAMPLE
        PS C:\> Confirm-SGAuthenticatedDomain -UniqueId '1234567'
         
        This command validates the authenticated domain with the unique ID '1234567' in the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Get-SGAuthenticatedDomain | Confirm-SGAuthenticatedDomain
         
        This command validates all authenticated domains in the current SendGrid instance.
     
    .NOTES
        This function requires an active SendGrid instance to work properly. Make sure to check the validity of the UniqueId parameter.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies the unique ID of the branded link to validate. This parameter is mandatory.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ValueFromPipeline
        )]
        [string[]]$UniqueId,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )    
    process {
        foreach ($Id in $UniqueId) { 
            $InvokeSplat = @{
                Method      = 'Post'
                Namespace   = "whitelabel/domains/$Id/validate"
                ErrorAction = 'Stop'
            }
            $GetSplat = @{
                UniqueId    = $Id
                ErrorAction = 'Stop'
            }
            if ($PSBoundParameters.OnBehalfOf) {
                $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
                $GetSplat.Add('OnBehalfOf', $OnBehalfOf)
            }
            $SGAuthenticatedDomain = Get-SGAuthenticatedDomain @GetSplat
            $SGAuthenticatedDomain

            if ($PSCmdlet.ShouldProcess(('{0}.{1}' -f $SGAuthenticatedDomain.Subdomain, $SGAuthenticatedDomain.Domain))) {

                if ($SGAuthenticatedDomain.Valid -eq $true) {
                    Write-Verbose -Message ('Authenticated Domain already validated!') -Verbose
                }
                else {
                    try {
                        Invoke-SendGrid @InvokeSplat
                    }
                    catch {
                        Write-Error ('Failed to validate SendGrid Authenticated Domain. {0}' -f $_.Exception.Message) -ErrorAction Stop
                    }
                }
            }
        }
    }
}
#EndRegion '.\Public\Confirm-SGAuthenticatedDomain.ps1' 82
#Region '.\Public\Confirm-SGBrandedDomainLink.ps1' 0
function Confirm-SGBrandedDomainLink {
    <#
    .SYNOPSIS
        Validates the branded domain links within the current SendGrid instance.
         
    .DESCRIPTION
        Confirm-SGBrandedDomainLink is used to validate the branded domain links within the current SendGrid instance.
        An authenticated domain allows you to remove the "via”" or "sent on behalf of" message that your recipients see when they read your emails.
        Authenticating a domain allows you to replace sendgrid.net with your personal sending domain.
        You will be required to create a subdomain so that SendGrid can generate the DNS records which you must give to your host provider.
 
        This function should be executed after the external DNS records have been applied.
 
    .PARAMETER UniqueId
        Specifies the unique ID of the branded link to validate. This parameter is mandatory.
 
    .EXAMPLE
        PS C:\> Confirm-SGBrandedDomainLink -UniqueId '1234567'
         
        This command validates the branded domain link with the unique ID '1234567' in the current SendGrid instance.
     
    .NOTES
        This function requires an active SendGrid instance to work properly. Make sure to check the validity of the UniqueId parameter.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # Specifies the unique ID of the branded link to validate. This parameter is mandatory.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName,
            ValueFromPipeline
        )]
        [string]$UniqueId,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )   
    process {
        foreach ($Id in $UniqueId) {
            $InvokeSplat = @{
                Method      = 'Post'
                Namespace   = "whitelabel/links/$Id/validate"
                ErrorAction = 'Stop'
            }
            $GetSplat = @{
                UniqueId    = $Id
                ErrorAction = 'Stop'
            }
            if ($PSBoundParameters.OnBehalfOf) {
                $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
                $GetSplat.Add('OnBehalfOf', $OnBehalfOf)
            }
            $SGBrandedDomainLink = Get-SGBrandedDomainLink @GetSplat
            $SGBrandedDomainLink

            if ($PSCmdlet.ShouldProcess(('{0}.{1}' -f $SGBrandedDomainLink.Subdomain, $SGBrandedDomainLink.Domain))) {

                if ($SGBrandedDomainLink.Valid -eq $true) {
                    Write-Verbose -Message ('Branded Link Domain already validated!') -Verbose
                }
                else {
                    try {
                        Invoke-SendGrid @InvokeSplat
                    }
                    catch {
                        Write-Error ('Failed to validate SendGrid Branded Domain Link. {0}' -f $_.Exception.Message) -ErrorAction Stop
                    }
                }
            }
        }
    }
}
#EndRegion '.\Public\Confirm-SGBrandedDomainLink.ps1' 76
#Region '.\Public\Connect-SendGrid.ps1' 0
function Connect-SendGrid {
    <#
    .SYNOPSIS
        Establishes a connection with a SendGrid instance.
         
    .DESCRIPTION
        Connect-SendGrid, or its alias Connect-SG, initiates a connection with a SendGrid instance using an API key as the credential.
        If a connection already exists, it ensures that the connection is active. If not, it creates a new connection.
 
    .PARAMETER Credential
        Specifies the API key to use when connecting to the SendGrid instance. The 'Username' field of the PSCredential object does not matter,
        and can be set to any string. The 'Password' field of the PSCredential object should be set to the SendGrid API key.
 
    .PARAMETER Force
        Indicates that this cmdlet forces a new connection, even if a connection already exists.
 
    .EXAMPLE
        PS C:\> Connect-SendGrid -Credential $myCred
 
        This command attempts to establish a connection to SendGrid using the API key stored in $myCred.
 
    .EXAMPLE
        PS C:\> Connect-SendGrid
 
        This command attempts to establish a connection to SendGrid. It will prompt for the API key since no credential was supplied.
 
    .EXAMPLE
        PS C:\> Connect-SendGrid -Force
 
        This command attempts to forcefully establish a new connection to SendGrid. It will prompt for the API key.
 
    .EXAMPLE
        PS C:\> Connect-SendGrid -Credential $myCred -Force
 
        This command attempts to forcefully establish a new connection to SendGrid using the API key stored in $myCred.
        Even if a connection already exists, it will create a new one.
 
    .NOTES
        A SendGrid API key is required to make a successful connection. Ensure your API key has adequate permissions for the tasks you intend to perform.
        The API key should be provided as the 'Password' field of the PSCredential object.
 
        The provided API key (credential) is stored in a script-scoped variable within the module. This means it's only accessible by functions
        within the same module and not accessible externally by other scripts or modules. This provides a degree of isolation and security.
 
        PowerShell does not store script or private variables in plain text in memory, but rather as secure strings, which means the actual API key
        is not easily retrievable through memory inspection tools. However, please note that this doesn't provide complete security. In environments
        where highly sensitive information is handled, it's recommended to use more secure methods of storing and using credentials, such as Azure Key Vault.
 
        The API key stored in the session will persist only as long as the PowerShell session remains active.
        Once the PowerShell session is closed, the variable storing the API key is discarded.
 
        In addition, the SendGridSession class has a built-in mechanism to limit session lifetime. It tracks the time when the session was last created
        or refreshed, and if the last successful connection attempt was more than 12 hours ago, the class automatically disconnects the session.
        This also removes the stored credential (API key) from memory. If you attempt to interact with the SendGrid API after the session has expired,
        you'll need to reconnect using your credentials. This is done to help ensure the security of your API key.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    [Alias('Connect-SG')]
    param (
        # Username does not matter. Use 'apikey' if unsure when connecting to SendGrid using API.
        [Parameter(
            Position = 0
        )]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]$Credential,

        [Parameter(
            Position = 1
        )]
        [switch]$Force
    )
    
    process {
        try {
            if ($PSCmdlet.ShouldProcess('SendGrid Session', 'Connect')) {
                if ($Force -or -not $script:Session -or -not ($script:Session -is [SendGridSession])) {
                    $script:Session = [SendGridSession]::new()
                    if ($Credential) {
                        $script:Session.Connect($Credential)
                    }
                    else {
                        $script:Session.Connect((Get-Credential -Message 'Enter your ApiKey' -UserName 'apikey' -Title 'Connect-SendGrid'))
                    }
                    Write-Verbose -Message 'Connection to SendGrid established.' -Verbose
                }
                else {
                    $script:Session.Connect()
                    Write-Verbose -Message 'Existing connection to SendGrid refreshed.' -Verbose
                }
            }
        }
        catch {
            if ($script:Session) {
                Write-Verbose -Message 'Encountered an error while connecting to SendGrid. Cleaning up...'
            }
            Remove-Variable -Name Session -Scope Script
            Write-Error -Message ('Unable to connect to SendGrid. Error detail: {0}' -f $_.Exception.Message) -ErrorAction Stop
        }
    }
}
#EndRegion '.\Public\Connect-SendGrid.ps1' 103
#Region '.\Public\Disable-SGSubuser.ps1' 0
function Disable-SGSubuser {
    <#
    .SYNOPSIS
        Disables a Subuser within the current SendGrid instance.
 
    .DESCRIPTION
        Disable-SGSubuser disables a Subuser within the current SendGrid instance.
        The Subuser is disabled with the provided username.
 
    .PARAMETER Username
        Specifies the ID of a specific Subuser to enable. This parameter is mandatory.
 
    .EXAMPLE
        PS C:\> Disable-SGSubuser -Username <username>
 
        This command disables a Subuser with the specified username within the current SendGrid instance.
     
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
 
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies the username for the Subuser to create.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string]$Username
    )
    process {
        $InvokeSplat = @{
            Method      = 'Patch'
            Namespace   = "subusers/$Username"
            ErrorAction = 'Stop'
            ContentBody = @{
                disabled = $true
            }
        } 

        if ($PSCmdlet.ShouldProcess($Username)) {
            try {
                Invoke-SendGrid @InvokeSplat
            }
            catch {
                Write-Error ('Failed to enable SendGrid Subuser. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Public\Disable-SGSubuser.ps1' 55
#Region '.\Public\Disconnect-SendGrid.ps1' 0
function Disconnect-SendGrid {
    <#
    .SYNOPSIS
        Disconnects from the current established SendGrid instance.
         
    .DESCRIPTION
        Disconnect-SendGrid, or its alias Disconnect-SG, disconnects the current session with a SendGrid instance.
        The function checks if a session exists and, if so, disconnects it.
 
    .EXAMPLE
        PS C:\> Disconnect-SendGrid
         
        This command attempts to disconnect an active connection to SendGrid.
 
    .NOTES
        To use this function, you must first be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    [Alias('Disconnect-SG')]
    param ()
    
    process {
        if ($script:Session -is [SendGridSession]) {
            if ($PSCmdlet.ShouldProcess('SendGrid Session', 'Disconnect')) {
                try {
                    $script:Session.Disconnect()
                    Remove-Variable -Name Session -Scope Script
                }
                catch {
                    Write-Error -Message ('Unable to disconnect from SendGrid. {0}' -f { $_.Exception.Message })
                }
            }
        }
        else {
            Write-Warning -Message 'No active SendGrid session was found.'
        }
    }
}
#EndRegion '.\Public\Disconnect-SendGrid.ps1' 41
#Region '.\Public\Enable-SGSubuser.ps1' 0
function Enable-SGSubuser {
    <#
    .SYNOPSIS
        Enables a Subuser within the current SendGrid instance.
 
    .DESCRIPTION
        Enable-SGSubuser enables a Subuser within the current SendGrid instance.
        The Subuser is enabled with the provided username.
 
    .PARAMETER Username
        Specifies the ID of a specific Subuser to enable. This parameter is mandatory.
 
    .EXAMPLE
        PS C:\> Enable-SGSubuser -Username <username>
         
        This command enables a Subuser with the specified username within the current SendGrid instance.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
 
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies the username for the Subuser to create.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string]$Username
    )
    process {
        $InvokeSplat = @{
            Method      = 'Patch'
            Namespace   = "subusers/$Username"
            ErrorAction = 'Stop'
            ContentBody = @{
                disabled = $false
            }
        } 

        if ($PSCmdlet.ShouldProcess($Username)) {
            try {
                Invoke-SendGrid @InvokeSplat
            }
            catch {
                Write-Error ('Failed to enable SendGrid Subuser. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Public\Enable-SGSubuser.ps1' 54
#Region '.\Public\Get-SGApiKey.ps1' 0
function Get-SGApiKey {
    <#
    .SYNOPSIS
        Retrieves all or a specific API Key within the current SendGrid instance.
 
    .DESCRIPTION
        Get-SGApiKey retrieves all API Keys or a specific API Key based on its ID
        within the current SendGrid instance. If a specific API Key ID is provided,
        the cmdlet also returns the scopes added to the key.
 
    .PARAMETER ApiKeyId
        Specifies the ID of a specific API Key to retrieve. If this parameter is not provided, all API Keys are retrieved.
        When a specific API Key ID is provided, the associated scopes of the key are also retrieved.
 
    .PARAMETER OnBehalfOf
        Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
 
    .EXAMPLE
        PS C:\> Get-SGApiKey
         
        This command retrieves all API Keys within the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Get-SGApiKey -ApiKeyId <apiKeyId>
         
        This command retrieves the API Key with the specified ID within the current SendGrid instance and returns
        the scopes added to the key.
     
    .EXAMPLE
        PS C:\> Get-SGApiKey -OnBehalfOf 'Subuser'
         
        This command retrieves all API Keys within the current SendGrid instance on behalf of the specified subuser.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies the ID of a specific API Key to retrieve. If this parameter is not provided, all API Keys are retrieved.
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
            
        )]
        [string[]]$ApiKeyId,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )

    process {
        $InvokeSplat = @{
            Method      = 'Get'
            Namespace   = 'api_keys'
            ErrorAction = 'Stop'
        }
        if ($PSBoundParameters.OnBehalfOf) {
            $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
        }
        if ($PSBoundParameters.ApiKeyId) {
            foreach ($Id in $ApiKeyID) {
                if ($PSCmdlet.ShouldProcess(('{0}' -f $Id))) {
                    $InvokeSplat['Namespace'] = "api_keys/$Id"
                    try {
                        Invoke-SendGrid @InvokeSplat
                    }
                    catch {
                        Write-Error ('Failed to retrieve SendGrid API Key. {0}' -f $_.Exception.Message) -ErrorAction Stop
                    }
                }
            }
        }
        else {
            if ($PSCmdlet.ShouldProcess(('{0}' -f 'All API Keys'))) {
                try {
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to retrieve SendGrid API Key. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }
}
#EndRegion '.\Public\Get-SGApiKey.ps1' 86
#Region '.\Public\Get-SGAuthenticatedDomain.ps1' 0
function Get-SGAuthenticatedDomain {
    <#
    .SYNOPSIS
        Retrieves all or specific Authenticated Domains within the current SendGrid instance.
         
    .DESCRIPTION
        Get-SGAuthenticatedDomain retrieves all Authenticated Domains or a specific Authenticated Domain based on its unique ID
        within the current SendGrid instance. An authenticated domain allows you to replace sendgrid.net with your personal sending domain,
        thereby removing the "via" or "sent on behalf of" message that recipients see when they read your emails.
 
    .PARAMETER UniqueId
        Specifies the UniqueId of a specific Authenticated Domain to retrieve. If this parameter is not provided, all Authenticated Domains are retrieved.
 
    .PARAMETER OnBehalfOf
        Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
 
    .EXAMPLE
        PS C:\> Get-SMSGAuthenticatedDomain
         
        Domain : sending.example.com
        Subdomain : em4963
        User : Top Account
        Valid : True
        AutomaticSecurity : True
        Default : False
        ValidationAttempt : 2022-03-04 15:34:34
        DNS : @{MailCNAME=; DKIM1=; DKIM2=}
        IPAddresses : {}
        UniqueId : 13508031
        UserId : 8262273
 
        Domain : email.example.com
        Subdomain : em200
        User : Top Account
        Valid : True
        AutomaticSecurity : True
        Default : False
        ValidationAttempt : 2021-11-12 07:38:27
        DNS : @{MailCNAME=; DKIM1=; DKIM2=}
        IPAddresses : {}
        UniqueId : 12589712
        UserId : 8262273
        ...
 
        This command retrieves all Authenticated Domains within the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Get-SMSGAuthenticatedDomain -UniqueId 12589712
         
        Domain : email.example.com
        Subdomain : em200
        User : Top Account
        Valid : True
        AutomaticSecurity : True
        Default : False
        ValidationAttempt : 2021-11-12 07:38:27
        DNS : @{MailCNAME=; DKIM1=; DKIM2=}
        IPAddresses : {}
        UniqueId : 12589712
        UserId : 8262273
        Username : Top Account
 
        This command retrieves the Authenticated Domain with the UniqueId '12589712' within the current SendGrid instance.
 
        .EXAMPLE
        PS C:\> Get-SMSGAuthenticatedDomain -UniqueId 12589712 -OnBehalfOf 'Subuser'
 
        Domain : email.example.com
        Subdomain : em200
        User : Top Account
        Valid : True
        AutomaticSecurity : True
        Default : False
        ValidationAttempt : 2021-11-12 07:38:27
        DNS : @{MailCNAME=; DKIM1=; DKIM2=}
        IPAddresses : {}
        UniqueId : 12589712
        UserId : 8262273
        Username : Subuser
 
        This command retrieves the Authenticated Domain with the UniqueId '12589712' within for the Subuser 'Subuser' within the current SendGrid instance.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # Specifies the UniqueId of a specific Authenticated Domain to retrieve. If this parameter is not provided, all Authenticated Domains are retrieved.
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string[]]$UniqueId,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )
    process {
        $InvokeSplat = @{
            Method      = 'Get'
            Namespace   = 'whitelabel/domains'
            ErrorAction = 'Stop'
        }
        if ($PSBoundParameters.OnBehalfOf) {
            $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
        }
        if ($PSBoundParameters.UniqueId) {
            foreach ($Id in $UniqueId) {
                if ($PSCmdlet.ShouldProcess(('{0}' -f $Id))) {
                    $InvokeSplat['Namespace'] = "whitelabel/domains/$Id"
                    try {
                        $InvokeResult = Invoke-SendGrid @InvokeSplat
                        if ($InvokeResult | Get-Member -Name 'Errors' -MemberType 'NoteProperty') {
                            throw $InvokeResult.Errors.Message
                        }
                        else {
                            $InvokeResult
                        }
                    }
                    catch {
                        Write-Error ('Failed to retrieve SendGrid Authenticated Domain. {0}' -f $_.Exception.Message) -ErrorAction Stop
                    }
                }
            }
        }
        else {
            if ($PSCmdlet.ShouldProcess(('All Authenticated Domains'))) {
                try {
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to retrieve SendGrid Authenticated Domain. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }   
}
#EndRegion '.\Public\Get-SGAuthenticatedDomain.ps1' 138
#Region '.\Public\Get-SGBrandedDomainLink.ps1' 0
function Get-SGBrandedDomainLink {
    <#
    .SYNOPSIS
        Retrieves all or specific Branded Domain Links within the current SendGrid instance.
         
    .DESCRIPTION
        Get-SGBrandedDomainLink retrieves all Branded Domain Links or a specific Branded Domain Link based on its unique ID
        within the current SendGrid instance. Branded Domain Links allow all of the click-tracked links, opens, and images in your
        emails to be served from your domain rather than sendgrid.net, which aids in spam filter and recipient server assessments
        of email trustworthiness.
 
    .PARAMETER UniqueId
        Specifies the UniqueId of a specific Branded Domain Link to retrieve. If this parameter is not provided, all Branded Domain Links are retrieved.
 
    .EXAMPLE
        PS C:\> Get-SGBrandedDomainLink
         
        Domain : sending.example.com
        Subdomain : sg
        User : Top Account
        Valid : True
        Default : False
        ValidationAttempt : 2022-03-04 15:34:34
        DNS : @{DomainCNAME=; OwnerCNAME=}
        UniqueId : 13508031
        UserId : 8262273
 
        Domain : email.example.com
        Subdomain : url6142
        User : Top Account
        Valid : True
        Default : False
        ValidationAttempt : 2021-11-12 07:38:12
        DNS : @{DomainCNAME=; OwnerCNAME=}
        UniqueId : 12589712
        UserId : 8262273
        ...
 
        This command retrieves all Branded Domain Links within the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Get-SGBrandedDomainLink -UniqueId '12589712'
 
        Domain : email.example.com
        Subdomain : url6142
        User : Top Account
        Valid : True
        Default : False
        ValidationAttempt : 2021-11-12 07:38:12
        DNS : @{DomainCNAME=; OwnerCNAME=}
        UniqueId : 12589712
        UserId : 8262273
 
        This command retrieves the Branded Domain Link with the UniqueId '12589712' within the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Get-SGBrandedDomainLink -OnBehalfOf 'Subuser'
 
        Domain : email.example.com
        Subdomain : url6142
        User : Top Account
        Valid : True
        Default : False
        ValidationAttempt : 2021-11-12 07:38:12
        DNS : @{DomainCNAME=; OwnerCNAME=}
        UniqueId : 12589712
        UserId : 8262273
 
        This command retrieves all Branded Domain Links within the current SendGrid instance on behalf of the specified subuser.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies a UniqueId to retrieve
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string]$UniqueId,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )
    process {
        $InvokeSplat = @{
            Method      = 'Get'
            Namespace   = 'whitelabel/links'
            ErrorAction = 'Stop'
        }
        
        if ($PSBoundParameters.OnBehalfOf) {
            $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
        }
        if ($PSBoundParameters.UniqueId) {
            foreach ($Id in $UniqueId) {
                if ($PSCmdlet.ShouldProcess(('{0}' -f $Id))) {
                    $InvokeSplat['Namespace'] = "whitelabel/links/$Id"
                    try {
                        $InvokeResult = Invoke-SendGrid @InvokeSplat
                        if ($InvokeResult | Get-Member -Name 'Errors' -MemberType 'NoteProperty') {
                            throw $InvokeResult.Errors.Message
                        }
                        else {
                            $InvokeResult
                        }
                    }
                    catch {
                        Write-Error ('Failed to retrieve SendGrid SendGrid Branded Domain Link. {0}' -f $_.Exception.Message) -ErrorAction Stop
                    }
                }
            }
        }
        else {
            if ($PSCmdlet.ShouldProcess(('{0}' -f 'All Branded Domain Links'))) {
                try {
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to retrieve SendGrid SendGrid Branded Domain Link. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }
}
#EndRegion '.\Public\Get-SGBrandedDomainLink.ps1' 131
#Region '.\Public\Get-SGIPAddress.ps1' 0
function Get-SGIPAddress {
    <#
    .SYNOPSIS
        Retrieves the IP addresses associated with the current SendGrid instance.
 
    .DESCRIPTION
        Get-SGIPAddress retrieves the IP addresses associated with the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Get-SGIPAddress
 
        This command retrieves the IP addresses associated with the current SendGrid instance.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
 
    #>

    [CmdletBinding()]
    param ()

    process {
        $InvokeSplat = @{
            Method      = 'Get'
            Namespace   = 'ips'
            ErrorAction = 'Stop'
        } 
        
        try {
            Invoke-SendGrid @InvokeSplat
        }
        catch {
            Write-Error ('Failed to retrieve SendGrid IPs. {0}' -f $_.Exception.Message) -ErrorAction Stop
        }
    }
}
#EndRegion '.\Public\Get-SGIPAddress.ps1' 36
#Region '.\Public\Get-SGPermissionScopes.ps1' 0
function Get-SGPermissionScopes {
    <#
    .SYNOPSIS
        Retrieves all permission scopes that the current SendGrid session (apikey) has permission to.
         
    .DESCRIPTION
        Get-SGPermissionScopes queries SendGrid for all permission scopes that the current session, identified by the API key, has access to.
        Permission scopes define the specific actions that are permitted in a session. This information can be useful for diagnosing
        authorization issues or for configuring new sessions with the appropriate permissions.
 
    .EXAMPLE
        PS C:\> Get-SGPermissionScopes
 
        This command retrieves all permission scopes that the current session (apikey) has permission to.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
    #>

    [CmdletBinding()]
    param ()
    process {
        $InvokeSplat = @{
            Method      = 'Get'
            Namespace   = 'scopes'
            ErrorAction = 'Stop'
        }
        try {
            Invoke-SendGrid @InvokeSplat
        }
        catch {
            Write-Error ('Failed to retrieve permission scopes in SendGrid. {0}' -f $_.Exception.Message) -ErrorAction Stop
        }
        
    }
}
#EndRegion '.\Public\Get-SGPermissionScopes.ps1' 36
#Region '.\Public\Get-SGSubuser.ps1' 0
function Get-SGSubuser {
    <#
    .SYNOPSIS
        Retrieves all or a specific Subuser within the current SendGrid instance.
 
    .DESCRIPTION
        Get-SGSubuser retrieves all Subusers or a specific Subuser based on its username
        within the current SendGrid instance. Due to limitations in the Sendgrid API, when retriev ing all users it wont display disabled users.
 
    .PARAMETER Username
        Specifies the ID of a specific Subuser to retrieve. If this parameter is not provided, all Subusers are retrieved.
 
    .PARAMETER Limit
        The number of results you would like to get in each request.
        Default: none
 
    .PARAMETER Offset
        The number of Subusers to skip.
        Default: none
 
    .EXAMPLE
        PS C:\> Get-SGSubuser
         
        This command retrieves all users within the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Get-SGSubuser -Username <username>
         
        This command retrieves the user with the specified username within the current SendGrid instance.
     
    .EXAMPLE
        PS C:\> Get-SGSubuser -Limit 2
         
        This command retrieves the first two Subusers within the current SendGrid instance.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies the ID of a specific Subuser to retrieve. If this parameter is not provided, all Subusers are retrieved.
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string]$Username,

        # Specifies the number of results you would like to get in each request.
        [Parameter()]
        [int]$Limit,

        [Parameter()]
        [int]$Offset
    )

    process {
        $InvokeSplat = @{
            Method      = 'Get'
            Namespace   = 'subusers'
            ErrorAction = 'Stop'
        } 
        
        #Generic List
        [System.Collections.Generic.List[string]]$QueryParameters = [System.Collections.Generic.List[string]]::new()

        if ($PSBoundParameters.Username) {
            $InvokeSplat['Namespace'] += "/$username"
        }
        if ($PSBoundParameters.Limit) {
            $QueryParameters.Add("limit=$limit")
        }
        if ($PSBoundParameters.Offset) {
            $QueryParameters.Add("offset=$offset")
        }

        if ($QueryParameters.Count -gt 0) {
            $InvokeSplat['Namespace'] += '?' + ($QueryParameters -join '&')
        }

        if ($PSCmdlet.ShouldProcess(('{0}' -f 'Subusers'))) {
            try {
                Invoke-SendGrid @InvokeSplat
            }
            catch {
                Write-Error ('Failed to retrieve SendGrid Subuser. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Public\Get-SGSubuser.ps1' 90
#Region '.\Public\New-SGApiKey.ps1' 0
function New-SGApiKey {
    <#
    .SYNOPSIS
        Creates a new API Key for the current SendGrid instance.
 
    .DESCRIPTION
        New-SGApiKey creates a new API key for the SendGrid instance. The API key can be given full access, restricted access, or billing access.
        The created API key can then be used to authenticate access to SendGrid services.
 
    .PARAMETER Name
        Specifies the name to describe this API Key.
 
    .PARAMETER Scopes
        Specifies the individual permissions that you are giving to this API Key.
 
    .PARAMETER FullAccessKey
        Specifies to create a full access API Key. This will nullify the Scopes parameter.
 
    .EXAMPLE
        PS C:\> New-SGApiKey -Name 'MyAPIKey' -Scopes @('mail.send', 'alerts.create', 'alerts.read')
 
        Creates a new API key with the name 'MyAPIKey' and assigns 'mail.send', 'alerts.create', 'alerts.read' scopes to the key.
 
    .EXAMPLE
        PS C:\> New-SGApiKey -Name 'MyFullAccessKey' -FullAccessKey
 
        Creates a new full access API key with the name 'MyFullAccessKey'. This will prompt for confirmation.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
        There is a limit of 100 API Keys on your account. Omitting the Scopes field from your request will create a key with "Full Access" permissions by default.
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        DefaultParameterSetName = 'Scopes')]
    param (
        [Parameter(
            ParameterSetName = 'Scopes',
            Mandatory
        )]
        [Parameter(
            ParameterSetName = 'FullAccess',
            Mandatory
        )]
        [string]$Name,

        [Parameter(
            ParameterSetName = 'Scopes'
        )]
        [ValidateSet([SendGridScopes])]
        [string[]]$Scopes,

        [Parameter(
            ParameterSetName = 'FullAccess',
            Mandatory
        )]
        [switch]$FullAccessKey,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )
    begin {
        [hashtable]$ContentBody = @{
            name = $Name
        }
        if ($PSCmdlet.ParameterSetName -eq 'Scopes') {
            $ContentBody.Add('scopes', $Scopes)
        }
    }
    process {
        $InvokeSplat = @{
            Method      = 'Post'
            Namespace   = 'api_keys'
            ErrorAction = 'Stop'
        }
        if ($PSBoundParameters.OnBehalfOf) {
            $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
        }
        $InvokeSplat.Add('ContentBody', $ContentBody)
        if ($PSCmdlet.ParameterSetName -eq 'FullAccess') {
            if ($PSCmdlet.ShouldContinue("You are about to create an API key ($Name) with Full Access. Do you want to continue?", $MyInvocation.MyCommand.Name)) {
                try {
                    $InvokeResult = Invoke-SendGrid @InvokeSplat
                    if ($InvokeResult | Get-Member -Name 'Errors' -MemberType 'NoteProperty') {
                        throw $InvokeResult.Errors.Message
                    }
                    else {
                        $InvokeResult
                    }
                }
                catch {
                    Write-Error ('Failed to create a FullAccess SendGrid API key. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
        else {
            if ($PSCmdlet.ShouldProcess($Name)) {
                try {
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to create SendGrid API key. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }
}
#EndRegion '.\Public\New-SGApiKey.ps1' 109
#Region '.\Public\New-SGAuthenticatedDomain.ps1' 0
function New-SGAuthenticatedDomain {
    <#
    .SYNOPSIS
        Adds a new Authenticated Domain to the current Sendgrid instance.
 
    .DESCRIPTION
        New-SGAuthenticatedDomain allows you to add a new Authenticated Domain to the current SendGrid instance. An authenticated domain allows
        you to remove the "via" or "sent on behalf of" message that your recipients see when they read your emails. Authenticating a domain allows
        you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that SendGrid can generate
        the DNS records which you must give to your host provider.
 
        This function uses a dynamic parameter, `CustomSPF`, which is available only when `DisableAutomaticSecurity` switch is set. The "CustomSPF"
        parameter allows to specify whether to use a custom SPF or allow SendGrid to manage your SPF. This option is only available to authenticated
        domains set up for manual security.
 
    .PARAMETER Domain
        Specifies a domain. It's not recommended to provide a full domain including a subdomain, for instance email.example.com.
 
    .PARAMETER Subdomain
        Specifies a subdomain to be used, in most cases it's "email".
 
    .PARAMETER DisableAutomaticSecurity
        Specify whether to not allow SendGrid to manage your SPF records, DKIM keys, and DKIM key rotation. Default is that SendGrid manages
        those records.
 
    .PARAMETER CustomDkimSelector
        Add a custom DKIM selector. Accepts three letters or numbers. Defaults to 'sg'.
 
    .PARAMETER Force
        Specifies if the current domain (parameter Domain) should be created despite it contains a subdomain (email.example.com).
 
    .PARAMETER CustomSPF
        This is a dynamic parameter and only becomes available when the 'DisableAutomaticSecurity' switch is set.
        Specifies whether to use a custom SPF or allow SendGrid to manage your SPF. This option is only available to authenticated domains set up for manual security.
 
    .EXAMPLE
        PS C:\> New-SGAuthenticatedDomain -Domain 'example.com' -Subdomain 'email'
 
        Adds a new authenticated domain 'example.com' with the subdomain 'email' using the specified API key.
 
    .EXAMPLE
        PS C:\> New-SGAuthenticatedDomain -Domain 'example.com' -Subdomain 'email' -DisableAutomaticSecurity -CustomDkimSelector 'exm'
 
        Adds a new authenticated domain 'example.com' with the subdomain 'email', disables automatic security, and uses a custom DKIM selector 'exm' with the specified API key.
 
    .EXAMPLE
        PS C:\> New-SGAuthenticatedDomain -Domain 'sub.example.com' -Subdomain 'email' -Force
 
        Adds a new authenticated domain 'email.sub.example.com' with the subdomain 'email' and forces the creation despite the domain containing a subdomain.
 
    .EXAMPLE
        PS C:\> New-SGAuthenticatedDomain -Domain 'example.com' -Subdomain 'email' -DisableAutomaticSecurity -CustomSPF
 
        Adds a new authenticated domain 'example.com' with the subdomain 'email', disables automatic security, and uses a custom SPF record with the specified API key.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (

        # Specifies a domain. It's not recommended to provide a full domain including a subdomain, for instance email.example.com.
        [Parameter(Mandatory)]
        [ValidatePattern('^([a-zA-Z0-9]([-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?\.)?([a-zA-Z0-9]{1,2}([-a-zA-Z0-9]{0,252}[a-zA-Z0-9])?)\.([a-zA-Z]{2,63})$')]
        [string]$Domain,

        # Specifies a subdomain to be used, in most cases it's "email".
        [Parameter()]
        [string]$Subdomain,

        # Specifies a subuser to be used, this is optional.
        [Parameter()]
        [string]$SubUser,

        # Specify whether to not allow SendGrid to manage your SPF records, DKIM keys, and DKIM key rotation. Default is that SendGrid manages those records.
        [Parameter()]
        [switch]$DisableAutomaticSecurity,

        # Add a custom DKIM selector. Accepts three letters or numbers. Defaults to 'sg'.
        [Parameter()]
        [ValidatePattern('^[a-zA-Z\d]{3}$')]
        $CustomDkimSelector = 'sg',

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf,

        # Specifies if the current domain (parameter Domain) should be created despite it contains a subdomain (email.example.com).
        [Parameter()]
        [switch]$Force
    )
    DynamicParam {
        if ($DisableAutomaticSecurity) {
            # Specify whether to use a custom SPF or allow SendGrid to manage your SPF. This option is only available to authenticated domains set up for manual security.
            $CustomSPFParamAttribute = [System.Management.Automation.ParameterAttribute]::new()
            $CustomSPFParamAttribute.Position = 4

            # Add the parameter attributes to an attribute collection
            $AttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
            $AttributeCollection.Add($CustomSPFParamAttribute)

            # Create the actual CustomSPF parameter
            $CustomSPFParam = [System.Management.Automation.RuntimeDefinedParameter]::new('CustomSPF', [switch], $AttributeCollection)

            # Push the parameter(s) into a parameter dictionary
            $ParamDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
            $ParamDictionary.Add('CustomSPF', $CustomSPFParam)

            # Return the dictionary
            return $ParamDictionary
        }
    }
    begin {
        [hashtable]$ContentBody = [hashtable]::new()
        $ContentBody.Add('domain', $Domain)
        if ($Domain -match '.*\..*\..*' -and -not $PSBoundParameters.ContainsKey('Subdomain')) {
            Write-Verbose -Message ("Sendgrid will automatically generate a custom subdomain for you. Example:em1234.$Subdomain.$Domain") -Verbose
            $ProcessMessage = $Domain
        }
        elseif ($Domain -match '.*\..*\..*' -and $PSBoundParameters.ContainsKey('Subdomain') -and -not $Force.IsPresent) {
            Write-Warning -Message "It's not recommended to use a double custom subdomain. If you know what you are doing, re-run with -Force. Terminating function..."
            break
        }
        elseif ($Domain -match '.*\..*\..*' -and $PSBoundParameters.ContainsKey('Subdomain') -and $Force.IsPresent) {
            Write-Verbose -Message "Running with force using double subdomain. Sendgrid will automatically generate a subdomain for you. Example:em1234.$Subdomain.$Domain" -Verbose
            $ProcessMessage = "$Subdomain.$Domain"
        }
        else {
            Write-Verbose -Message ("Sendgrid will automatically generate a custom subdomain for you. Example:em1234.$Subdomain.$Domain") -Verbose
            $ContentBody.Add('subdomain', $Subdomain)
            $ProcessMessage = "$Domain"
            
        }
        $ContentBody.Add('custom_dkim_selector', $CustomDkimSelector)
        $ContentBody.Add('default', $false)

        if ($PSBoundParameters.ContainsKey('SubUser')) {
            $ContentBody.Add('username', $SubUser)
        }
        if ($PSBoundParameters.ContainsKey('DisableAutomaticSecurity')) {
            $ContentBody.Add('automatic_security', $false)
        }
        else {
            $ContentBody.Add('automatic_security', $true)
        }
        if ($PSBoundParameters.ContainsKey('CustomSPF')) {
            $ContentBody.Add('custom_spf', $true)
        }
        else {
            $ContentBody.Add('custom_spf', $false)
        }
        $InvokeSplat = @{
            Method      = 'Post'
            Namespace   = 'whitelabel/domains'
            ErrorAction = 'Stop'
        }
        if ($PSBoundParameters.OnBehalfOf) {
            $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
        }
        $InvokeSplat.Add('ContentBody', $ContentBody)
    }    
    process {
        if ($PSCmdlet.ShouldProcess($ProcessMessage)) {
            try {
                Invoke-SendGrid @InvokeSplat
            }
            catch {
                Write-Error ('Failed to add SendGrid Authenticated Domain. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Public\New-SGAuthenticatedDomain.ps1' 173
#Region '.\Public\New-SGBrandedDomainLink.ps1' 0
function New-SGBrandedDomainLink {
    <#
    .SYNOPSIS
        Adds a new Branded Domain Link within the current Sendgrid instance.
 
    .DESCRIPTION
        New-SGBrandedDomainLink adds a new Branded Domain Link within the current SendGrid instance. Email link branding (formerly "Link Whitelabel")
        allows all of the click-tracked links, opens, and images in your emails to be served from your domain rather than sendgrid.net. Spam filters
        and recipient servers look at the links within emails to determine whether the email looks trustworthy. They use the reputation of the root
        domain to determine whether the links can be trusted.
 
    .PARAMETER Domain
        Specifies a domain. Do not provide a full domain including a subdomain here, for instance email.example.com.
 
    .PARAMETER Subdomain
        Specifies a subdomain to be used, in most cases it's "link".
 
    .PARAMETER Force
        Specifies if the current domain (parameter Domain) should be created despite it contains a subdomain (email.example.com).
 
    .EXAMPLE
        PS C:\> New-SGBrandedDomainLink -Domain 'example.com' -Subdomain 'link'
 
        Adds a new branded domain link 'sub.example.com' with the subdomain 'link' using the specified API key.
 
    .EXAMPLE
        PS C:\> New-SGBrandedDomainLink -Domain 'sub.example.com' -Force
 
        Adds a new branded domain link 'url123.sub.example.com' and forces the creation despite the domain containing a subdomain.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (

        # Specifies a domain. Do not provide a full domain including a subdomain here, for instance email.example.com.
        [Parameter(Mandatory)]
        [ValidatePattern('^([a-zA-Z0-9]([-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?\.)?([a-zA-Z0-9]{1,2}([-a-zA-Z0-9]{0,252}[a-zA-Z0-9])?)\.([a-zA-Z]{2,63})$')]
        [string]$Domain,

        # Specifies a subdomain to be used, in most cases it's "link".
        [Parameter()]
        [string]$Subdomain = 'link',

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf,

        # Specifies if the current domain (parameter Domain) should be created despite it contains a subdomain (email.example.com).
        [Parameter()]
        [switch]$Force
    )
    
    begin {
        [hashtable]$ContentBody = [hashtable]::new()
        $ContentBody.Add('domain', $Domain)
        if ($Domain -match '.*\..*\..*' -and -not $Force.IsPresent) {
            Write-Warning -Message "It's not recommended to use a double custom subdomain. Sendgrid will automatically generate a subdomain for you. If you know what you are doing, re-run with -Force. Terminating function..."
            break
        }
        elseif ($Force.IsPresent) {
            Write-Verbose -Message ('Sendgrid will automatically generate a custom subdomain for you.') -Verbose
            $ProcessMessage = $Domain
        }
        else {
            $ContentBody.Add('subdomain', $Subdomain)
            $ProcessMessage = "$Subdomain.$Domain"
        }
        $ContentBody.Add('default', $false)
    }    
    process {
        $InvokeSplat = @{
            Method      = 'Post'
            Namespace   = 'whitelabel/links'
            ErrorAction = 'Stop'
        }
        if ($PSBoundParameters.OnBehalfOf) {
            $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
        }
        $InvokeSplat.Add('ContentBody', $ContentBody)
        if ($PSCmdlet.ShouldProcess($ProcessMessage)) {
            try {
                Invoke-SendGrid @InvokeSplat
            }
            catch {
                Write-Error ('Failed to create SendGrid Branded Domain Link. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Public\New-SGBrandedDomainLink.ps1' 92
#Region '.\Public\New-SGSubuser.ps1' 0
function New-SGSubuser {
    <#
    .SYNOPSIS
        Creates a new Subuser within the current SendGrid instance.
 
    .DESCRIPTION
        New-SGSubuser creates a new Subuser within the current SendGrid instance.
        The Subuser is created with the provided username and email address to contact this subuser.
 
    .PARAMETER Username
        Specifies the ID of a specific Subuser to retrieve. If this parameter is not provided, all Subusers are retrieved.
 
    .PARAMETER Email
        Specifies the email address to contact this subuser.
    .PARAMETER Password
        Specifies the password for the Subuser to create.
 
    .PARAMETER Ips
        Specifies the IP addresses that you would like to allow this Subuser to access.
 
    .EXAMPLE
        PS C:\> New-SGSubuser -Username <username> -Email <email> -Password <securestring> -Ips <ipaddress>
         
        This command creates a new Subuser with the specified username, email address, password, and IP address within the current SendGrid instance.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
 
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies the username for the Subuser to create.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string]$Username,

        # Specifies the email address to contact this subuser.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [MailAddress]$Email,

        # Specifies the password for the Subuser to create.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [SecureString]$Password,

        # Specifies the IP addresses that you would like to allow this Subuser to access.
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [IPAddress[]]$Ips

    )

    begin {
        [hashtable]$ContentBody = @{
            username = $Username
            email    = $Email
            password = (ConvertFrom-SecureString -SecureString $Password -AsPlainText)
            ips      = @($Ips)
        }
    }
    process {
        $InvokeSplat = @{
            Method      = 'Post'
            Namespace   = 'subusers'
            ErrorAction = 'Stop'
        } 

        $InvokeSplat.Add('ContentBody', $ContentBody)
        if ($PSCmdlet.ShouldProcess($Username)) {
            try {
                #Invoke-SendGrid @InvokeSplat
                $InvokeSplat
            }
            catch {
                Write-Error ('Failed to create SendGrid Subuser. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}

#{ 'ips':["167.89.80.214"], 'username':"kalletest_omnicit", 'email':"philip@gonjer.com", 'password':"Gonjer.com123!"#","passwordConfirm":"Gonjer.com123!\"#" }
#EndRegion '.\Public\New-SGSubuser.ps1' 94
#Region '.\Public\Remove-SGApiKey.ps1' 0
function Remove-SGApiKey {
    <#
    .SYNOPSIS
        Deletes a given API key.
 
    .DESCRIPTION
        Remove-SGApiKey deletes the API key with the given ID.
 
    .PARAMETER ApiKeyID
        Specifies the ID of the API Key to be deleted.
 
    .EXAMPLE
        PS C:\> Remove-SGApiKey -ApiKeyId 'R2l2W3kZSQukQv4lCkG3zW'
 
        This command deletes the API Key with the specified ID within the current SendGrid instance.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        # Specifies the ID of the API Key to be deleted.
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string[]]$ApiKeyId,
        
        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )
    
    process {
        foreach ($Id in $ApiKeyID) {
            $InvokeSplat = @{
                Method      = 'Delete'
                Namespace   = "api_keys/$Id"
                ErrorAction = 'Stop'
            }
            $GetSplat = @{
                ApiKeyID    = $Id
                ErrorAction = 'Stop'
            }

            if ($PSBoundParameters.OnBehalfOf) {
                $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
                $GetSplat.Add('OnBehalfOf', $OnBehalfOf)
            }
            $SGApiKey = Get-SGApiKey @GetSplat
            if ($PSCmdlet.ShouldProcess('ApiKey: {0}' -f $SGApiKey.Name)) {
                try {
                    # Deletes the API Key
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to delete SendGrid API Key. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }
}
#EndRegion '.\Public\Remove-SGApiKey.ps1' 61
#Region '.\Public\Remove-SGAuthenticatedDomain.ps1' 0
function Remove-SGAuthenticatedDomain {
    <#
    .SYNOPSIS
        Removes an Authenticated Domain within the current Sendgrid instance.
 
    .DESCRIPTION
        Remove-SGAuthenticatedDomain removes an authenticated domain from the current SendGrid instance. An authenticated domain allows you
        to remove the "via" or "sent on behalf of" message that your recipients see when they read your emails. Authenticating a domain allows
        you to replace sendgrid.net with your personal sending domain. You must provide the unique identifier of the domain to be removed.
        Please note that you might need to remove the DNS records manually after removing the domain.
 
    .PARAMETER UniqueId
        Specifies the unique identifier for the authenticated domain to remove.
 
    .EXAMPLE
        PS C:\> Remove-SGAuthenticatedDomain -UniqueId '1234567'
 
        Removes the authenticated domain with the unique identifier '1234567'.
 
    .EXAMPLE
        PS C:\> Get-SGAuthenticatedDomain | Where-Object { $_.Domain -eq 'example.com' } | Remove-SGAuthenticatedDomain
 
        Removes the authenticated domain 'example.com' using its unique identifier obtained from the Get-SGAuthenticatedDomain cmdlet.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High')]
    param (

        # Specifies the unique identifier for the authenticated domain to remove.
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string[]]$UniqueId,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )   
    process {
        foreach ($Id in $UniqueId) { 
            $InvokeSplat = @{
                Method      = 'Delete'
                Namespace   = "whitelabel/domains/$Id"
                ErrorAction = 'Stop'
            }
            $GetSplat = @{
                UniqueId    = $Id
                ErrorAction = 'Stop'
            }
            if ($PSBoundParameters.OnBehalfOf) {
                $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
                $GetSplat.Add('OnBehalfOf', $OnBehalfOf)
            }
            $SGAuthenticatedDomain = Get-SGAuthenticatedDomain @GetSplat
            
            Write-Verbose -Message ("Don't forget to remove DNS records:") -Verbose
            $SGAuthenticatedDomain

            if ($PSCmdlet.ShouldProcess(('{0}.{1}' -f $SGAuthenticatedDomain.Subdomain, $SGAuthenticatedDomain.Domain))) {
                try {
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to remove SendGrid Authenticated Domain. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }
}
#EndRegion '.\Public\Remove-SGAuthenticatedDomain.ps1' 76
#Region '.\Public\Remove-SMSGBrandedDomainLink.ps1' 0
function Remove-SGBrandedDomainLink {
    <#
    .SYNOPSIS
        Removes a Branded Domain Link from the current Sendgrid instance.
 
    .DESCRIPTION
        Remove-SGBrandedDomainLink removes a branded domain link from the current SendGrid instance. Branded Domain Links allows all of the click-tracked links,
        opens, and images in your emails to be served from your domain rather than sendgrid.net. It improves the trustworthiness of your emails. You must provide
        the unique identifier of the branded link to be removed. Please note that you might need to remove the DNS records manually after removing the branded link.
 
    .PARAMETER UniqueId
        Specifies the unique identifier for the branded link to remove.
 
    .EXAMPLE
        PS C:\> Remove-SGBrandedDomainLink -UniqueId '1234567'
 
        Removes the branded domain link with the unique identifier '1234567'.
 
    .EXAMPLE
        PS C:\> Get-SGBrandedDomainLink | Where-Object { $_.Domain -eq 'example.com' } | Remove-SGBrandedDomainLink
 
        Removes the branded domain link 'example.com' using its unique identifier obtained from the Get-SGBrandedDomainLink cmdlet.
 
    .NOTES
        To use this function, you must be connected to a SendGrid instance. Use the Connect-SendGrid function to establish a connection.
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High')]
    param (

        # Specifies a the UniqueId for the branded link to remove.
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string]$UniqueId,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )
    process {
        foreach ($Id in $UniqueId) { 
            $InvokeSplat = @{
                Method      = 'Delete'
                Namespace   = "whitelabel/links/$Id"
                ErrorAction = 'Stop'
            }
            $GetSplat = @{
                UniqueId    = $Id
                ErrorAction = 'Stop'
            }
            if ($PSBoundParameters.OnBehalfOf) {
                $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
                $GetSplat.Add('OnBehalfOf', $OnBehalfOf)
            }
            $SGBrandedDomainLink = Get-SGBrandedDomainLink @GetSplat
            Write-Verbose -Message ("Don't forget to remove DNS records:") -Verbose
            $SGBrandedDomainLink

            if ($PSCmdlet.ShouldProcess(('{0}.{1}' -f $SGBrandedDomainLink.Subdomain, $SGBrandedDomainLink.Domain))) {
                try {
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to remove SendGrid Branded Domain Link. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }
}
#EndRegion '.\Public\Remove-SMSGBrandedDomainLink.ps1' 74
#Region '.\Public\Send-SGMailMessage.ps1' 0
function Send-SGMailMessage {
    <#
    .SYNOPSIS
        Sends an email via the SendGrid API.
 
    .DESCRIPTION
        The Send-SGMailMessage function sends an email using the SendGrid API. It allows you to specify the sender, recipient, subject, and body of the email, as well as any attachments.
 
    .PARAMETER From
        Specifies the sender's email address.
 
    .PARAMETER To
        One or an array of recipients who will receive your email.
        Each object in this array must contain the recipient's email address.
        Each object in the array may optionally contain the recipient's name. 'Name<Name@example.com>'
        'Name name@example.com'
        'Firstname Lastname firstname.lastname@example.com'
        'Firstname Lastname <firstname.lastname@example.com>'
        'Firstname.Lastname firstname.lastname@example.com'
        '"Firstname Lastname" firstname.lastname@example.com'
        '"Firstname Lastname" <firstname.lastname@example.com>'
 
    .PARAMETER Subject
        Specifies the email subject.
 
    .PARAMETER Body
        Specifies the email body.
 
    .PARAMETER CC
        Specifies the CC email address. CC addresses are not visible to the recipients of the email.
        One or an array of CC who will receive your email.
        Each object in this array must contain the recipient's email address.
        Each object in the array may optionally contain the recipient's name.
        'Name<Name@example.com>'
        'Name name@example.com'
        'Firstname Lastname firstname.lastname@example.com'
        'Firstname Lastname <firstname.lastname@example.com>'
        'Firstname.Lastname firstname.lastname@example.com'
        '"Firstname Lastname" firstname.lastname@example.com'
        '"Firstname Lastname" <firstname.lastname@example.com>'
 
    .PARAMETER BCC
        Specifies the BCC email address. BCC addresses are not visible to the recipients of the email.
        One or an array of BCC who will receive your email.
        Each object in this array must contain the recipient's email address.
        Each object in the array may optionally contain the recipient's name.
        'Name<Name@example.com>'
        'Name name@example.com'
        'Firstname Lastname firstname.lastname@example.com'
        'Firstname Lastname <firstname.lastname@example.com>'
        'Firstname.Lastname firstname.lastname@example.com'
        '"Firstname Lastname" firstname.lastname@example.com'
        '"Firstname Lastname" <firstname.lastname@example.com>'
 
    .PARAMETER BodyAsHtml
        Specifies if the email body is formatted as HTML. Default is False, i.e. plain text.
 
    .PARAMETER Attachment
        Specifies the attachment(s) to be included in the email.
 
    .PARAMETER Priority
        Specifies the email priority. Valid values are 'Low', 'Normal', and 'High'.
 
    .PARAMETER SeparateTo
        Specifies if Separate To should be used.
 
    .PARAMETER ReplyTo
        Specifies if a separate ReplyTo address should be used. If not specified, the sender's email address (From) will be used.
     
    .PARAMETER SendAt
        Specifies the date and time to send the email. If not specified, the email will be sent immediately. Both datetime and unix timestamp formats are accepted.
        A unix timestamp allowing you to specify when your email should be delivered. Scheduling delivery more than 72 hours in advance is forbidden.
 
    .PARAMETER TemplateId
        Specifies the template ID to use. An email template ID. A template that contains a subject and content — either text or html — will override any subject and content values specified at the personalizations or message level.
 
    .PARAMETER Categories
        Specifies an array of category names for this message. Each category name may not exceed 255 characters.
     
    .PARAMETER BatchId
        Specifies an ID representing a batch of emails to be sent at the same time. Including a batch_id in your request allows you include this email in that batch. It also enables you to cancel or pause the delivery of that batch. For more information, see the Cancel Scheduled Sends API.
 
    .EXAMPLE
        PS C:\> Send-SGMailMessage -From 'sender@example.com' -To 'recipient@example.com' -Subject 'Test Email' -Body 'Hello, this is a test email.'
         
        This command sends an email with the specified parameters via the SendGrid API.
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        DefaultParameterSetName = 'Default'
    )]
    [Alias('Send-SGMessage', 'Send-SGMail')]
    param (
        # Specifies the sender's email address.
        [Parameter(
            Position = 0,
            Mandatory = $true,
            ParameterSetName = 'Default'
        )]
        [Parameter(
            Position = 0,
            Mandatory = $true,
            ParameterSetName = 'SeparateTo'
        )]
        [MailAddress]$From,

        # One or an array of recipients who will receive your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name. 'Name<Name@example.com>'
        [Parameter(
            Position = 1,
            Mandatory = $true,
            ParameterSetName = 'Default'
        )]
        [Parameter(
            Position = 1,
            Mandatory = $true,
            ParameterSetName = 'SeparateTo'
        )]
        [Alias('Recipient')]
        [MailAddress[]]$To,

        # An array of recipients who will receive a copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name. 'Name<Name@example.com>'
        [Parameter(
            Position = 2,
            ParameterSetName = 'Default'
        )]
        [MailAddress[]]$CC,

        # An array of recipients who will receive a blind copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name. 'Name<Name@example.com>'
        [Parameter(
            Position = 3,
            ParameterSetName = 'Default'
        )]
        [MailAddress[]]$BCC,

        # Specifies the email subject.
        [Parameter(
            Position = 4,
            Mandatory = $true,
            ParameterSetName = 'Default'
        )]
        [Parameter(
            Position = 4,
            Mandatory = $true,
            ParameterSetName = 'SeparateTo'
        )]
        [ValidateLength(1, 998)]
        [string]$Subject,

        # Specifies the email body.
        [Parameter(
            Position = 5,
            Mandatory = $true,
            ParameterSetName = 'Default'
        )]
        [Parameter(
            Position = 5,
            Mandatory = $true,
            ParameterSetName = 'SeparateTo'
        )]
        [string]$Body,

        # Specifies if the email body is formatted as HTML.
        [Parameter(
            Position = 6,
            ParameterSetName = 'Default'
        )]
        [Parameter(
            Position = 6,
            ParameterSetName = 'SeparateTo'
        )]
        [switch]$BodyAsHtml,

        # Specifies the attachment(s) to be included in the email.
        [Parameter(
            Position = 7,
            ParameterSetName = 'Default'
        )]
        [Parameter(
            Position = 7, 
            ParameterSetName = 'SeparateTo'
        )]
        [Alias('Attachments')]
        [System.Net.Mail.Attachment[]]$Attachment,

        # Specifies the email priority. Valid values are 'Low', 'Normal', and 'High'.
        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'SeparateTo')]
        [Alias('Importance')]
        [ValidateSet('Low', 'Normal', 'High')]
        [string]$Priority,

        # Specifies if Separate To should be used.
        [Parameter(ParameterSetName = 'SeparateTo')]
        [switch]$SeparateTo,

        # Specifies if a separate ReplyTo address should be used.
        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'SeparateTo')]
        [MailAddress]$ReplyTo,

        # Specifies the date and time to send the email. If not specified, the email will be sent immediately. Both datetime and unix timestamp formats are accepted.
        # Scheduling delivery more than 72 hours in advance is forbidden.
        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'SeparateTo')]
        [UnixTime]$SendAt,

        # Specifies the template ID to use. An email template ID. A template that contains a subject and content — either text or html — will override any subject and content values specified at the personalizations or message level.
        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'SeparateTo')]
        [string]$TemplateId,

        # Specifies an array of category names for this message. Each category name may not exceed 255 characters.
        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'SeparateTo')]
        [ValidateLength(1, 10)]
        [string[]]$Categories,

        # Specifies an ID representing a batch of emails to be sent at the same time. Including a batch_id in your request allows you include this email in that batch. It also enables you to cancel or pause the delivery of that batch. For more information, see the Cancel Scheduled Sends API.
        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'SeparateTo')]
        [string]$BatchId,

        <#
            Specifies an object of type [SGASM] that allows you to specify how to handle unsubscribes.
 
            The [SGASM] class represents a set of options for handling unsubscribes. It has the following properties:
 
            - GroupId: An integer that represents the group ID for unsubscribes. Default value is 0.
            - GroupsToDisplay: An array of integers that represents the groups to display for unsubscribes. Default value is an empty array.
 
            You can create an instance of [SGASM] using the following constructors:
            - SGASM(): Creates an instance with default values for GroupId and GroupsToDisplay.
            - SGASM([int]$GroupId): Creates an instance with the specified GroupId and default value for GroupsToDisplay.
            - SGASM([int]$GroupId, [int[]]$GroupsToDisplay): Creates an instance with the specified GroupId and GroupsToDisplay.
 
            The ToString() method returns a string representation of the GroupId.
 
            Example usage:
            $unsubscribe = [SGASM]::new(123, @(1, 2, 3))
            Send-SGMailMessage -Unsubscribe $unsubscribe
        #>

        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'SeparateTo')]
        [ValidateNotNullOrEmpty()]
        [SGASM]$Unsubscribe


    )
    begin {
        $InvokeSplat = @{
                Method      = 'Post'
                Namespace   = 'mail/send'
                ErrorAction = 'Stop'
            }
        [hashtable]$ContentBody = @{
            personalizations = [System.Collections.Generic.List[hashtable]]::new()
        }
        $RecipientCount = $To.Count + $CC.Count + $BCC.Count
    }
    process {
        if ($PSCmdlet.ShouldProcess(('{0} to "{1}" recipients with subject: "{2}"' -f $From.Address, $RecipientCount, $Subject))) {
        
            # Convert the MailAddress objects to SendGrid API compatible objects.
            $FromList = ConvertTo-SendGridAddress -Address $From
            $ToList = ConvertTo-SendGridAddress -Address $To
            $CCList = ConvertTo-SendGridAddress -Address $CC
            $BCCList = ConvertTo-SendGridAddress -Address $BCC
            $ReplyToList = ConvertTo-SendGridAddress -Address $ReplyTo
            <#
            Personalizations
            An array of messages and their metadata. Each object within personalizations can be thought of as an envelope - it defines who should receive an individual message and how that message should be handled. See our Personalizations documentation for examples.
            maxItems: 1000
            Personalizations can contain the following fields:
                from
                to
                cc = An array of recipients who will receive a copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name.
                bcc = An array of recipients who will receive a blind carbon copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name.
                subject = The subject of your email. See character length requirements according to RFC 2822.
                headers = A collection of JSON key/value pairs allowing you to specify handling instructions for your email. You may not overwrite the following headers: x-sg-id, x-sg-eid, received, dkim-signature, Content-Type, Content-Transfer-Encoding, To, From, Subject, Reply-To, CC, BCC
                substitutions = Substitutions allow you to insert data without using Dynamic Transactional Templates. This field should not be used in combination with a Dynamic Transactional Template, which can be identified by a template_id starting with d-. This field is a collection of key/value pairs following the pattern "substitution_tag":"value to substitute". The key/value pairs must be strings. These substitutions will apply to the text and html content of the body of your email, in addition to the subject and reply-to parameters. The total collective size of your substitutions may not exceed 10,000 bytes per personalization object.
                dynamic_template_data = Dynamic template data is available using Handlebars syntax in Dynamic Transactional Templates. This field should be used in combination with a Dynamic Transactional Template, which can be identified by a template_id starting with d-. This field is a collection of key/value pairs following the pattern "variable_name":"value to insert".
                custom_args = Values that are specific to this personalization that will be carried along with the email and its activity data. Substitutions will not be made on custom arguments, so any string that is entered into this parameter will be assumed to be the custom argument that you would like to be used. This field may not exceed 10,000 bytes.
                send_at = A unix timestamp allowing you to specify when your email should be delivered. Scheduling delivery more than 72 hours in advance is forbidden.
            #>

            if ($SeparateTo) {
                foreach ($T in $To) {
                    $Personalizations = @{
                        subject = $Subject
                        to      = @(
                            ConvertTo-SendGridAddress -Address $T
                        )
                    }
                    $ContentBody.personalizations.Add($Personalizations)
                }
            }
            else {
                $ContentBody.personalizations.Add(@{
                        subject = $Subject
                        to      = @($ToList)
                        cc      = @($CCList)
                        bcc     = @($BCCList)
                    })
            }
            $ContentBody.personalizations | Remove-EmptyHashtable -Recursive
            $ContentBody.Add('from', $FromList)
            if ($PSBoundParameters.ContainsKey('ReplyTo')) {
                $ContentBody.Add('reply_to', $ReplyToList)
            }
            $ContentBody.Add('content', @(
                    @{
                        type  = if ($BodyAsHtml) { 'text/html' } else { 'text/plain' }
                        value = $Body
                    }
                ))
            if ($PSBoundParameters.ContainsKey('Attachment')) {
                $ContentBody.Add('attachments', @(
                        foreach ($A in $Attachment) {
                            @{
                                content     = [Convert]::ToBase64String([IO.File]::ReadAllBytes($A.ContentStream.Name))
                                filename    = $A.Name
                                type        = $A.ContentType.MediaType
                                disposition = 'attachment'
                            }
                        }
                    ))
            }
            if ($PSBoundParameters.ContainsKey('Priority')) {
                $ContentBody.Add('priority', $Priority)
            }
            if ($PSBoundParameters.ContainsKey('SendAt')) {
                $ContentBody.Add('send_at', $SendAt.ToUnixTime())
            }
            if ($PSBoundParameters.ContainsKey('TemplateId')) {
                $ContentBody.Add('template_id', $TemplateId)
            }
            if ($PSBoundParameters.ContainsKey('Categories')) {
                $ContentBody.Add('categories', $Categories)
            }

            try {
                Remove-EmptyHashtable -Hashtable $ContentBody -Recursive
                $InvokeSplat.Add('ContentBody', $ContentBody)
                Invoke-SendGrid @InvokeSplat
            }
            catch {
                Write-Error ('Failed to send email via SendGrid API. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Public\Send-SGMailMessage.ps1' 351
#Region '.\Public\Set-SGApiKey.ps1' 0
function Set-SGApiKey {
    <#
    .SYNOPSIS
        Update the name and scopes of a given API key.
 
    .DESCRIPTION
        Set-SGApiKey updates the name and scopes of a given API key.
 
    .PARAMETER ApiKeyID
        Specifies the ID of the API Key to be updated.
 
    .PARAMETER Scopes
        Specifies the new scopes of the API Key.
 
    .PARAMETER NewName
        Specifies the new name of the API Key. This parameter is not mandatory.
 
    .EXAMPLE
        PS C:\> Set-SGApiKey -ApiKeyID 'R2l2W3kZSQukQv4lCkG3zW' -Scopes 'access_settings.activity.read', 'alerts.create', 'alerts.read'
 
        This command updates the scopes of the API Key with the specified ID within the current SendGrid instance.
 
    .EXAMPLE
        PS C:\> Set-SGApiKey -ApiKeyID 'R2l2W3kZSQukQv4lCkG3zW' -Scopes 'access_settings.activity.read', 'alerts.create', 'alerts.read' -NewName 'MyUpdatedKey'
 
        This command updates both the name and scopes of the API Key with the specified ID within the current SendGrid instance.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # Specifies the ID of the API Key to be updated.
        [Parameter(
            Mandatory = $true
        )]
        [string[]]$ApiKeyID,

        # Specifies the new scopes of the API Key.
        [Parameter()]
        [ValidateSet([SendGridScopes])]
        [string[]]$Scopes,

        # Specifies the new name of the API Key. This parameter is not mandatory.
        [Parameter()]
        [string]$NewName,

        # Specifies a On Behalf Of header to allow you to make API calls from a parent account on behalf of the parent's Subusers or customer accounts.
        [Parameter()]
        [string]$OnBehalfOf
    )
    
    process {
        if ($ApiKeyID.Count -gt 1) {
            Write-Warning ('Only one API Key can be updated at a time. Only scopes will be updated for {0} API Keys.' -f ($ApiKeyID.Count - 1))
            $PSBoundParameters.Remove('NewName')
        }
        foreach ($Id in $ApiKeyID) {
            $InvokeSplat = @{
                Method      = 'Put'
                Namespace   = "api_keys/$Id"
                ErrorAction = 'Stop'
            }
            $GetSplat = @{
                ApiKeyID    = $Id
                ErrorAction = 'Stop'
            }

            if ($PSBoundParameters.OnBehalfOf) {
                $InvokeSplat.Add('OnBehalfOf', $OnBehalfOf)
                $GetSplat.Add('OnBehalfOf', $OnBehalfOf)
            }
            $CurrentKey = Get-SGApiKey @GetSplat
            if ($PSCmdlet.ShouldProcess($Id)) {
                Write-Verbose -Message ('Updating key {0}' -f $SGApiKey.Name)

                if ($PSBoundParameters.ContainsKey('Scopes')) {
                    [hashtable]$ContentBody = @{
                        scopes = $Scopes
                    }
                }
                else {
                    [hashtable]$ContentBody = @{
                        scopes = $CurrentKey.Scopes
                    }
                }
                
                if ($PSBoundParameters.ContainsKey('NewName')) {
                    $ContentBody.Add('name', $NewName)
                    
                }
                else {
                    $ContentBody.Add('name', $CurrentKey.Name)
                }
                $InvokeSplat.Add('ContentBody', $ContentBody)
                try {
                    Invoke-SendGrid @InvokeSplat
                }
                catch {
                    Write-Error ('Failed to update SendGrid API Key. {0}' -f $_.Exception.Message) -ErrorAction Stop
                }
            }
        }
    }
}
#EndRegion '.\Public\Set-SGApiKey.ps1' 105
#Region '.\Public\Set-SGAuthenticatedDomainToSubUser.ps1' 0
function Set-SGAuthenticatedDomainToSubUser {
    <#
    .SYNOPSIS
        Assign a specified Authenticated Domain to a subuser.
 
    .DESCRIPTION
        Authenticated domains can be associated with (i.e. assigned to) subusers from a parent account.
        This functionality allows subusers to send mail using their parent's domain authentication.
        To associate an authenticated domain with a subuser, the parent account must first authenticate and validate the domain.
        The parent may then associate the authenticated domain via the subuser management tools.
 
    .PARAMETER UnqieId
        Specifies the ID of the authenticated domain to assign to the subuser. This parameter is mandatory.
 
        .PARAMETER UserName
        Specifies username of the subuser to assign the authenticated domain to. This parameter is mandatory.
 
    .EXAMPLE
        PS C:\> Set-SGAuthenticatedDomainToSubUser -UniqueId '1234567' -UserName 'subuser'
 
        This command assigns the authenticated domain with the unique ID '1234567' to the subuser 'subuser'.
 
    .EXAMPLE
        PS C:\> Get-SGAuthenticatedDomain | Where-Object { $_.Domain -eq 'example.com' } | Set-SGAuthenticatedDomainToSubUser -UserName 'subuser'
         
        This command assigns the authenticated domain 'example.com' to the subuser 'subuser' using its unique ID obtained from the Get-SGAuthenticatedDomain cmdlet.
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (

        # Specifies the ID of a specific API Key to retrieve. If this parameter is not provided, all API Keys are retrieved.
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string]$UniqueId,

        # Specifies username of the subuser to assign the authenticated domain to.
        [Parameter(
            Mandatory
        )]
        [string]$UserName
    )
    process {
        $InvokeSplat = @{
            Method      = 'Post'
            Namespace   = "whitelabel/domains/$UniqueId/subuser"
            ErrorAction = 'Stop'
        }
        $GetSplat = @{
            UniqueId    = $UniqueId
            ErrorAction = 'Stop'
        }
        $SGAuthenticatedDomain = Get-SGAuthenticatedDomain @GetSplat
        if ($PSCmdlet.ShouldProcess(('{0}.{1}' -f $SGAuthenticatedDomain.Subdomain, $SGAuthenticatedDomain.Domain))) {
            try {
                [hashtable]$ContentBody = @{
                    username = $UserName
                }
                $InvokeSplat.Add('ContentBody', $ContentBody)
                
                $InvokeResult = Invoke-SendGrid @InvokeSplat
                if ($InvokeResult | Get-Member -Name 'Errors' -MemberType 'NoteProperty') {
                    throw $InvokeResult.Errors.Message
                }
                else {
                    $InvokeResult
                }
            }
            catch {
                Write-Error ('Failed to assign authenticated domain. {0}' -f $_.Exception.Message) -ErrorAction Stop
            }
        }
    }
}
#EndRegion '.\Public\Set-SGAuthenticatedDomainToSubUser.ps1' 79