PSMimecast.psm1

<#
.SYNOPSIS
    This function will get information of a Mimecast account.
.DESCRIPTION
    This function will get information of a Mimecast account including if the account is locked or disabled.
    This is equivalent to going to Administration -> Directories -> Internal Directories -> <domain> -> and picking a user in the web UI.
.PARAMETER EmailAddress
    This parameter specifies the email address of the user you want get information about.
.EXAMPLE
    PS C:\> Get-MimecastAccount -EmailAddress syrius.cleveland@example.com
 
    Name EmailAddress accountLocked AccountDisabled AllowSmtp AllowPop PasswordNeverExpires ForcePasswordChange
    ---- ------------ ------------- --------------- --------- -------- -------------------- -------------------
    Syrius Cleveland syrius.cleveland@example.com False False False False False False
 
    In this example we get the account information for the user syrius.cleveland@example.com. We can see his account is not locked and not disabled.
.INPUTS
    string
        EmailAddress
.OUTPUTS
    spz.Mimecast.Account
.NOTES
    This function actually calls the Update API call wihtout any changes to retrieve this information.
#>

function Get-MimecastAccount{
    [cmdletbinding()]
    [Alias("Get-mcAccount")]
    param(
        [Parameter(Mandatory,ValueFromPipelineByPropertyName,ValueFromPipeline)]
        [Alias("User","Email")]
        [string]$EmailAddress
    )

    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/user/update-user"
        $url = $baseUrl + $apiCall
    }

    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        $postBody = @{
            data = @(@{
                emailAddress = $EmailAddress
            })
        }
        $postBodyJson = $postBody | ConvertTo-Json

        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBodyJson -Uri $url
        
        #Print the respons
        if ($response.data){
            $response.data | ForEach-Object {
                $_ | Add-Member -TypeName "PSMimecast.Account"
                $_
            }
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    }
}
<#
.SYNOPSIS
    This function retrieves the app info used to communicate with Mimecast's API that was previously stored using Set-MimecastAppInfo.
.DESCRIPTION
    This function retrieves the app info used to communicate with Mimecast's API that was previously stored using Set-MimecastAppInfo.
    This information is stored in the following path, "$ENV:APPDATA\PSMimecast\AppInfo.xml".
.EXAMPLE
    PS C:\> Get-MimecastAppInfo
 
    AppId AppKey
    ----- ------
    XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 
    This example show the only way to use this function. The app info was successfully retrieved. Actual values have been redacted for this example.
.INPUTS
    None
.OUTPUTS
    PSCustomObject
.NOTES
    App info must have been previously set using Set-MimecastAppInfo.
#>

function Get-MimecastAppInfo {
    [CmdletBinding()]
    [Alias("Get-mcAppInfo")]
    $Path = "$ENV:APPDATA\PSMimecast\AppInfo.xml"
    if (Test-Path -Path $Path){
        $AppInfo = Import-Clixml -Path $Path
        [PSCustomObject]@{
            AppId = $Appinfo.AppId
            AppKey = [pscredential]::new("API",(ConvertTo-SecureString -String $Appinfo.AppKey)).GetNetworkCredential().Password
        }
    }
    else{
        Write-Error "No App Info has been set, use Set-MimecastAppInfo function to set this data" -ErrorAction Stop
    }
}
function Get-MimecastDateTime{
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing

    $form = New-Object Windows.Forms.Form -Property @{
        StartPosition = [Windows.Forms.FormStartPosition]::CenterScreen
        Size          = [drawing.size]::new(225,275)
        Text          = 'Select a Date'
        Topmost       = $true
    }

    $calendar = New-Object Windows.Forms.MonthCalendar -Property @{
        Location = [drawing.size]::new(15,15)
        ShowTodayCircle   = $false
        MaxSelectionCount = 1
    }
    $form.Controls.Add($calendar)

    $TimePicker = New-Object System.Windows.Forms.DateTimePicker
    $TimePicker.Size = [Drawing.Size]::new(125,25)
    $TimePicker.Location = [Drawing.Size]::new(40,175)
    $TimePicker.ShowUpDown = $true
    $TimePicker.Format = [System.Windows.Forms.DateTimePickerFormat]::Time
    $form.Controls.Add($TimePicker)

    $okButton = New-Object Windows.Forms.Button -Property @{
        Location     = [drawing.size]::new(30,200)
        Size         = [drawing.size]::new(75,23)
        Text         = 'OK'
        DialogResult = [Windows.Forms.DialogResult]::OK
    }
    $form.AcceptButton = $okButton
    $form.Controls.Add($okButton)

    $cancelButton = New-Object Windows.Forms.Button -Property @{
        Location     = [drawing.size]::new(105,200)
        Size         = New-Object Drawing.Size 75, 23
        Text         = 'Cancel'
        DialogResult = [Windows.Forms.DialogResult]::Cancel
    }
    $form.CancelButton = $cancelButton
    $form.Controls.Add($cancelButton)

    $result = $form.ShowDialog()

    if ($result -eq [Windows.Forms.DialogResult]::OK) {
        $date = $calendar.SelectionStart
        $time = $TimePicker.Value
        $SelectionDate = ([DateTime]::new($date.Year,$date.Month,$date.Day,$time.Hour,$time.Minute,0)).ToUniversalTime()
        $SelectionDate.ToString("yyyy-MM-ddTHH:mm:ss+0000")
    }
}
<#
.SYNOPSIS
    This function returns the directory connections of Mimecast.
.DESCRIPTION
    This function returns the directory connections of Mimecast.
    This function is equivalent to going to Administration -> Services -> Directory Synchronization in the web UI.
.EXAMPLE
    PS C:\> Get-MimecastDirectoryConnection | Select-Object -First 1
 
    enabled : True
    id : MTOKEN:XXXXXXXXXXXXXXXXXXXXXXX...
    description : description
    info :
    serverType : active_directory
    ldapSettings : @{hostname=example.example...
    status : ok
    lastSync : 12/29/2021 7:43:30 AM
    syncRunning : False
    domains :
    acknowledgeDisabledAccounts : True
 
    This example gets the directory connectors and grabs only the first one that is returned. Here we can see whether the connection sync is running and the last time it ran.
.INPUTS
    None
.OUTPUTS
    PSCustomObject
.NOTES
    No notes to add.
#>

function Get-MimecastDirectoryConnection{
    [CmdletBinding()]
    [Alias("Get-mcDirectoryConnection")]
    param()
    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/directory/get-connection"
        $url = $baseUrl + $apiCall
    }

    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        #Create post body
        $postBody = @{data = @()} | ConvertTo-Json

        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url

        #Print the response
        if ($response.data){
            $response.data | ForEach-Object {
                $_.lastSync = [datetime]$_.lastSync
                $_
            }
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    }
}
<#
.SYNOPSIS
    This function will get messages that are under moderatoin review to be rejected or released.
.DESCRIPTION
    This function will get messages that are under moderatoin review to be rejected or released.
    This function is equivalent of going to Administration -> Message Center -> and selecting held reason category in the web UI.
.PARAMETER reasonCode
    This parameter should return only messages that match the reasonCode supplied, however I think their API does not support this.
.PARAMETER subject
    This parameter will return all held messages that have the subject field matching the string provided.
.PARAMETER sender1
    This parameter will return all held messages that have the sender field matching the string provided. The parameter uses sender1 since the variable sender is an automatic function and should not be used.
.PARAMETER recipient
    This parameter will return all held messages that have the recipient field matching the string provided.
.PARAMETER all
    This parameter will return all held messages that contain the provided string in any of the message's properties (subject, sender, recipeint, reasonCode).
.PARAMETER admin
    This parameter determines the level of results to return. If false, only results for the currenlty authenticated user will be returned. If true, held messages for all recipients will be returned. The default value is false.
    The default value is True.
.PARAMETER start
    This parameter will return all held messages that were receivied after the date and time provided. Use tab to launch the GUI to selecte a date and time.
.PARAMETER end
    This parameter will return all held messages that were receivied before the date and time provided. Use tab to launch the GUI to selecte a date and time.
.PARAMETER PageSize
    This parameter specifies how many held messages are return by each query. The default value is 50 and max value is 500.
.EXAMPLE
    PS C:\> Get-mcHeldMessage -recipient syrius.cleveland
 
    From To subject route Held Reason HasAttachments DateReceived
    ---- -- ------- ----- ----------- -------------- ------------
    Spiceworks Syrius Cleveland Need a new hobby for 2022? Check out ... INBOUND Agressive Spam Detection False 12/28/2021 7:57:50 PM
 
    This example gets all the held messages for receipient syrius.cleveland for the last 24 hours.
.INPUTS
    None
.OUTPUTS
    PSMimecast.HeldMessage
.NOTES
    I do not believe the reasonCode parameter works, but I included since it is the documenation provided by Mimecast.
#>

function Get-MimecastHeldMessage {
    [cmdletbinding()]
    [Alias("Get-mcHeldMessage")]
    Param(
        [string]$reasonCode,
        [string]$subject,
        [Alias("Sender")]
        [string]$sender1,
        [string]$recipient,
        [string]$all,
        [bool]$admin = $true,
        [ArgumentCompleter({
            param ($commandName,$parameterName,$wordToComplete,$commandAst,$fakeBoundParameters)
            Get-MimecastDateTime
        })]
        [string]$start = (Get-Date).AddDays(-1).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss+0000"),
        [ArgumentCompleter({
            param ($commandName,$parameterName,$wordToComplete,$commandAst,$fakeBoundParameters)
            Get-MimecastDateTime
        })]
        [string]$end = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss+0000"),

        [ValidateRange(1,500)]
        [int]$PageSize = 50
    )

    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/gateway/get-hold-message-list"
        $url = $baseUrl + $apiCall

        $searchBy = @{}
        $data = @{}
        $meta = @{
            pagination = @{
                pageSize = $PageSize
             }
        }

        $CommonParameters = [System.Management.Automation.Internal.CommonParameters].DeclaredProperties.Name
        $Parameters = $MyInvocation.MyCommand.Parameters.Keys | where {$_ -notin $CommonParameters}

        $SearchParam = @("all","subject","sender1","recipient","reason_code")
        $dataParam = @("admin","start","end")
        foreach ($param in $Parameters){
            $value = (Get-Variable -Name $param).Value
            if (![String]::IsNullOrEmpty($value)){
                if ($param -in $SearchParam){
                    $searchBy["fieldName"] = $param.TrimEnd('1')
                    $searchBy["value"] = $value.ToString()
                }
                elseif ($param -in $dataParam){
                    $data[$param] = $value.ToString()
                }
            }
        }
        
        if ($searchBy.Keys -ne $null){
            $data["searchBy"] = $searchBy
        }
    } #Begin

    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        #Create post body
        $postBody = @{
            meta = $meta
            data = @($data)
        } | ConvertTo-Json -Depth 3
        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url
        #Print the response
        if ($response.fail){
            Write-Error "$($response.fail.errors.message)"
        }
        else{
            $message = $response.data
            foreach ($message in $response.data){
                $message | Add-Member -Name ReleaseMessage -MemberType ScriptMethod -Value {New-HeldMessageReleaseAction -Id $this.Id}
                $message.dateReceived = [datetime]::Parse($message.dateReceived)
                $message | Add-Member -TypeName "PSMimecast.HeldMessage"
                $message
            }
        }
    } #Process
}
<#
.SYNOPSIS
    This function will get the held message summary of all held messages broken up by Reason (or policy Info).
.DESCRIPTION
    This function will get the held message summary of all held messages broken up by Reason (or policy Info).
    This function is equivalent of going to Administration -> Message Center in the web UI.
.EXAMPLE
    PS C:\> Get-MimecastHeldMessageSummary
 
    policyInfo numberOfItems
    ---------- -------------
    Agressive Spam Detection 7030
 
    In this example we can see that there is 7,030 held messages under the Agressive Spam Detection policy and no held messages under any other policies.
.INPUTS
    None
.OUTPUTS
    PSCustomObject
.NOTES
    No notes to add.
#>

function Get-MimecastHeldMessageSummary {
    [CmdletBinding()]
    [Alias("Get-mcHeldMessageSummary")]
    param()
    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/gateway/get-hold-summary-list"
        $url = $baseUrl + $apiCall
    }
    
    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        #Create post body
        $postBody = @{data = @()} | ConvertTo-Json

        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url

        #Print the response
        if ($response.data){
            $response.data
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    }
}
<#
.SYNOPSIS
    This function will return the profile of a specified Mimecast user.
.DESCRIPTION
    This function will return the profile of a specified Mimecast user.
    This function is equivalent to going to Administration -> Interal Directories -> <Domain> -> and selecting a user in the web UI.
    Some fields are missing like whether the account is locked or disabled, and Get-MimecastAccount should be used if these properties are needed.
.PARAMETER EmailAddress
    This parameter specifies the email address of the user profile to return.
.EXAMPLE
    PS C:\> Get-MimecastProfile -EmailAddress syrius.cleveland@example.com
 
    name : Syrius Cleveland
    emailAddress : syrius.cleveland@example.com
    links : {@{name=avatar; method=GET; uri=http://us-api.mimecast.com/...
    attributes : @{phoneNumber=}
    localPassword : True
    changePassword : True
    external : False
    editable : False
    role : Full Administrator
    companyName : Example Company
    serverTimeZone : America/New_York
    id : syrius.cleveland@example.com
    accountCode : XXXXXXX
    admin : True
 
    This example returns the profile of the user with the email address syrius.cleveland@example.com.
.INPUTS
    string
        EmailAddress
.OUTPUTS
    PSCustomObject
.NOTES
    Some fields are missing like whether the account is locked or disabled, and Get-MimecastAccount should be used if these properties are needed.
#>

function Get-MimecastProfile {
    [cmdletbinding()]
    [Alias("Get-mcProfile")]
    Param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [string]$EmailAddress
    )

    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/user/get-profile"
        $url = $baseUrl + $apiCall
    }
    
    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        #Create post body
        $postBody = @{
            data = @(@{
                emailAddress = $emailAddress
                showAvatar = "False"
            })
        }
        $postBody = $postBody | ConvertTo-Json
        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url
        #Print the response
        if ($response.data){
            $response.data
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    }
}
function Get-MimecastRegion {
    [Alias("Get-mcRegion")]
    [CmdletBinding()]
    param()

    $Path = "$ENV:APPDATA\PSMimecast\Region.xml"
    if (Test-Path -Path $Path){
        Import-Clixml -Path $Path
    }
    else{
        throw "Region not set! Use New-MimecastAPIKeys or Set-MimecastRegion to set a region to use for API calls!"
    }
}
<#
.SYNOPSIS
    This function will lock a Mimecast user account.
.DESCRIPTION
    This function will lock a Mimecast user account.
.PARAMETER EmailAddress
    This parameter specifies the emaill address of the account you want to lock.
.EXAMPLE
    PS C:\> Lock-MimecastAccount -EmailAddress syrius.cleveland@example.com
 
    emailAddress : syrius.cleveland@example.com
    name : Syrius Cleveland
    forcePasswordChange : False
    passwordNeverExpires : False
    accountLocked : True
    accountDisabled : False
    allowSmtp : False
    allowPop : False
 
    This example locks the account with the email address syrius.cleveland@example.com.
.INPUTS
    None
.OUTPUTS
    PSCustomObject
.NOTES
    This function uses the update-user API call to lock the user account.
#>

function Lock-MimecastAccount{
    [cmdletbinding()]
    [Alias("Lock-mcAccount")]
    param(
        [Parameter(Mandatory)]
        $EmailAddress
    )

    Begin{
        $baseUrl = Get-mcBaseURL
        $uri = "/api/user/update-user"
        $url = $baseUrl + $uri
    }

    Process{
        $headers = New-MimecastHeader -Uri $Uri

        $postBody = "{
            ""data"": [
                {
                    ""accountLocked"": True,
                    ""emailAddress"": ""$EmailAddress""
                }
            ]
        }"


        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url
        #Print the respons
        if ($response.data){
            $response.data
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    }
}
<#
.SYNOPSIS
    This function is used to reject a currently held message.
.DESCRIPTION
    This function is used to reject a currently held message.
    This function is equivalent to navigating to Administration -> Held Message -> Selecting a message and clicking the "reject" button in the web UI.
.PARAMETER MessageId
    This parameter specifies the Id of the message that will be rejected.
.PARAMETER message
    This parameter provides a message to be returned to the sender.
.PARAMETER reasonType
    This parameter specifies the reason code for the message being rejected.
    Possible values are: MESSAGE CONTAINS UNDESIRABLE CONTENT, MESSAGE CONTAINS CONFIDENTIAL INFORMATION,
    REVIEWER DISAPPROVES OF CONTENT, INAPPROPRIATE COMMUNICATION, MESSAGE GOES AGAINST EMAIL POLICIES
.PARAMETER notify
    This parameter indicates whether or not to Deliever a rejection notification to the sender. Default value is false.
.EXAMPLE
    PS C:\> Get-MimecastHeldMessage -recipient syrius.cleveland | where policyinfo -eq "Agressive Spam Detection" |
     New-HeldMessageRejectAction -reasonType 'REVIEWER DISAPPROVES OF CONTENT' -nofify $true -message "Message was rejected due to spam."
 
    id reject
    -- ------
    XXXXXXXXXXXXXXXXX.... True
 
    This example gets all held messages for syrius.cleveland and filters for only messages with the PolicyInfo equal to "Agressive Spam Detection".
    These held messages are then piped to New-HeldMessageRejectAction function to be rejected with a reason give and message for more information.
    An object is rertun to confirm this rejection was successful and provides the Id of the held message that was rejected.
.INPUTS
    string
        MessageId
.OUTPUTS
    PSCustomObject
.NOTES
    You can provide an array of Ids to be rejected.
#>

function New-HeldMessageRejectAction{
    [cmdletbinding()]
    [Alias("Invoke-mcRejectMessage")]
    Param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("Id")]
        [string[]]$MessageId,
        [string]$message,
        [ValidateSet("MESSAGE CONTAINS UNDESIRABLE CONTENT", "MESSAGE CONTAINS CONFIDENTIAL INFORMATION", "REVIEWER DISAPPROVES OF CONTENT",
         "INAPPROPRIATE COMMUNICATION", "MESSAGE GOES AGAINST EMAIL POLICIES")]
        [string]$reasonType,
        [bool]$nofify

    )

    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/gateway/hold-reject"
        $url = $baseUrl + $apiCall
        $SkipParamerters = @("MessageId")
    }

    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        $data = @{ids = $MessageId}
        $PSBoundParameters.Keys | where {$_ -notin $SkipParamerters} | foreach{
            $data[$_] = $PSBoundParameters[$_]
        }

        $postBody = @{data = @($data)} | ConvertTo-Json -Depth 5

        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url

        #Print the response
        if ($response.fail){
            Write-Error $response.fail.errors.message
        }
        else{
            $response.data
        }
    } #Process
}
<#
.SYNOPSIS
    This function can be used to release a currently held messages.
.DESCRIPTION
    This function can be used to release a currently held messages.
    This function is equivalent to navigating to Administration -> Held Message -> Selecting a message and clicking the "release" button in the web UI.
.PARAMETER MessageId
    This parameter specifies the Id of the message that will be released.
.EXAMPLE
    PS C:\> Get-MimecastHeldMessage -recipient syrius.cleveland -start 2021-12-25T22:47:00+0000 | where {$_.from.displayableName -like "*example*"} | New-HeldMessageReleaseAction
 
    Id release
    -- -------
    eNpVzF0Lgj7Y... True
 
    In tis example we get all held messages for syrius.cleveland strating 12/25/2021 and filter for only messages that came from example.
    These held messages are then piped to New-HeldMessageReleaseAction to be release. A return object confirms that the release was successful.
.INPUTS
    string
        MessageId
.OUTPUTS
    PSMimecast.ReleaseMessage
.NOTES
    No notes to add.
#>

function New-HeldMessageReleaseAction{
    [cmdletbinding()]
    [Alias("Invoke-mcReleaseMessage")]
    Param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("Id")]
        [string]$MessageId
    )

    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/gateway/hold-release"
        $url = $baseUrl + $apiCall
    }

    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        #Create post body
        $postBody = @{data = @(@{id = $MessageId})} | ConvertTo-Json
        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url

        #Print the response
        if ($response.fail){
            Write-Error $response.fail.errors.message
        }
        else{
            $message = $response.data
            $message | Add-Member -TypeName "PSMimecast.ReleaseMessage"
            $message
        }
    }
}
<#
.SYNOPSIS
    This function will create a new file (or overwrite an existing one) and store the API key that will used by other functions in this module to authenticate with the Mimecast API.
.DESCRIPTION
    This function will create a new file (or overwrite an existing one) and store the API key that will used by other functions in this module to authenticate with the Mimecast API.
.PARAMETER AppId
    This is the AppId string of the custome app integration you setup in the Mimecast web UI.
.PARAMETER Credentials
    This parameter will use the credentials passed to it to authenticate with Mimecast.
.PARAMETER AuthType
    This parameter specifies the authentication method to use with the provided credentials. If they are credentials to your AD account, then use "Basic-AD", otherwise setup a cloud password and use "Basic-Cloud".
.EXAMPLE
    PS C:\> New-MimecastAPIKeys -AppId XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -AuthType Basic-Cloud -Credentials (Get-Credential)
 
    This examples creates a new API key and stores it in file to be used by the rest of the module. Nothing is returned.
.INPUTS
    None
.OUTPUTS
    None
.NOTES
    This function can be used to overwrite your existing API keys. You cannot use more than one API key with this module.
#>

function New-MimecastAPIKeys {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory)]
        $AppId,
        [Parameter(Mandatory)]
        [pscredential]$Credentials,
        [ValidateSet("Basic-Cloud","Basic-Ad")]
        $AuthType = "Basic-Cloud",

        [Parameter(Mandatory)]
        [ValidateSet("eu","de","us","ca","za","au","je")]
        [string]$Region
    )

    Begin{
        Set-MimecastRegion -Region $Region
        
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/login/login"
        $url = $baseUrl + $apiCall
        #Generate request header values
        $hdrDate = (Get-Date).ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss UTC") 
        $requestId = [guid]::NewGuid().guid
    }

    Process{    
        $EmailAddress = $Credentials.UserName
        $password = $Credentials.GetNetworkCredential().Password
        $headers = @{
            "Authorization" = $authType + " " + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($emailAddress + ":" + $password));
            "x-mc-date" = $hdrDate;
            "x-mc-app-id" = $AppId;
            "x-mc-req-id" = $requestId;
            "Content-Type" = "application/json"
        }
        #Create post body
        $postBody = @{
            data = @(@{
                userName = $emailAddress
            })
        }
        $postBody = $postBody | ConvertTo-Json
        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url
        #Print the response
        if ($response.data.accesskey){
            Set-MimecastAPIKeys -AccessKey $response.data.accesskey -SecretKey $response.data.secretKey -Email $EmailAddress -AuthType $AuthType
        }
        else{
            Write-Error -Message "Unable to create keys: $($response.fail.errors.message)"
        }
    } #Process
}
<#
.SYNOPSIS
    This function is used to initially setup the module by storing the appid and the appkey in an encrypted file to be used by other function.
.DESCRIPTION
    This function is used to initially setup the module by storing the appid and the appkey in an encrypted file to be used by other function.
    The path to the file that is created is the following, "$ENV:APPDATA\PSMimecast\Keys.xml".
    You can find the values needed for the two paramters of this function by navigating to Administration -> Services -> API and Platform Integrations -> Your Application Integrations -> and selecting your app in the web UI.
.PARAMETER AppId
    This parameter specifies the AppId of the custom app integration you created in the Mimecast portal.
.PARAMETER AppKey
    This parameter specifies the AppKey of the custom app integration you created in the Mimecast portal.
.EXAMPLE
    PS C:\> Set-MimecastAppInfo -AppId "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" -AppKey "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
     
    This example sets the Mimecast App Info needed to use the rest of the functions in the module with redacted Id and Key.
.INPUTS
    None
.OUTPUTS
    None
.NOTES
    You can find the values needed for the two paramters of this function by navigating to Administration -> Services -> API and Platform Integrations -> Your Application Integrations -> and selecting your app in the web UI.
#>

function Set-MimecastAppInfo {
    [cmdletbinding()]
    [Alias("Set-mcAppInfo")]
    Param(
        [Parameter(Mandatory)]
        [string]$AppId,
        [Parameter(Mandatory)]
        [string]$AppKey
    )

    $Path = "$ENV:APPDATA\PSMimecast"
    if (!(Test-Path -Path $Path)){
        New-Item -Path $Path -ItemType Directory -Force | Out-Null
    }

    $AppInfo = [PSCustomObject]@{
        AppId = $AppId
        AppKey = (ConvertTo-SecureString $AppKey -AsPlainText -Force | ConvertFrom-SecureString)
    }
    $AppInfo | Export-Clixml -Path "$Path\AppInfo.xml" -Force
}
function Set-MimecastRegion {
    [Alias("Set-mcRegion")]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet("eu","de","us","ca","za","au","je")]
        [string]$Region
    )

    try{
        $Path = "$ENV:APPDATA\PSMimecast"
        if (!(Test-Path -Path $Path)){
            New-Item -Path $Path -ItemType Directory -Force | Out-Null
        }

        $RegionObject = @{
            Region = $Region
        }
        $RegionObject | Export-Clixml -Path "$Path\Region.xml" -Force
    }
    catch{
        $PSCmdlet.WriteError($_)
    }
}
<#
.SYNOPSIS
    This function will initiate directory synchronization of all connected directories.
.DESCRIPTION
    This function will initiate directory synchronization of all connected directories.
    This function is equivalent to navigating to Administration -> Services - Directory Synchronization -> and click "Sync Directory Data" in the web UI.
.EXAMPLE
    PS C:\>Start-MimecastDirectorySync
 
    type syncStatus
    ---- ----------
    ACTIVE_DIRECTORY started
 
    This example starts the directory synchronization process and the return object confirms that execution was successful.
.INPUTS
    None
.OUTPUTS
    PSCustomObject
.NOTES
    This function may take some time to process, be patient when you call this function.
#>

function Start-MimecastDirectorySync{
    [CmdletBinding()]
    [Alias("Start-mcDirectorySync")]
    param()
    Begin{       
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/directory/execute-sync"
        $url = $baseUrl + $apiCall
    }

    Process{
        $headers = New-MimecastHeader -Uri $apiCall
        #Create post body
        $postBody = @{data = @()} | ConvertTo-Json
        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url

        #Print the respons
        if ($response.data){
            $response.data
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    } #Process
}
<#
.SYNOPSIS
    This function unlocks a Mimecast account that is currently locked.
.DESCRIPTION
    This function unlocks a Mimecast account that is currently locked. It does this by setting the property "accountLocked" to False.
.PARAMETER EmailAddress
    This parameter specifies the email address of the Mimecast account you want to unlock.
.EXAMPLE
    PS C:\> Unlock-MimecastAccount -EmailAddress syrius.cleveland@example.com
 
    emailAddress : syrius.cleveland@example.com
    name : Syrius Cleveland
    forcePasswordChange : False
    passwordNeverExpires : False
    accountLocked : False
    accountDisabled : False
    allowSmtp : False
    allowPop : False
 
    This example unlocks the Mimecast account with the email address of syrius.cleveland@example.com.
    The object of this account is returned and we can confirm the account is now unlocked by looking at the accountLocked property on this object.
.INPUTS
    string
        EmailAddress
.OUTPUTS
    PSCustomObject
.NOTES
    General notes
#>

function Unlock-MimecastAccount{
    [cmdletbinding()]
    [Alias("Unlock-mcAccount")]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        $EmailAddress
    )

    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/user/update-user"
        $url = $baseUrl + $apiCall
    }

    Process{
        $headers = New-MimecastHeader -Uri $apiCall

        $postBody = @{
            data = @(@{
                accountLocked = "False"
                emailAddress = $EmailAddress
            })
        }
        $postBody = $postBody | ConvertTo-Json

        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url
        #Print the response
        if ($response.data){
            $response.data
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    } #Process
}
<#
.SYNOPSIS
    This function will refresh an API keys access token if it has expired.
.DESCRIPTION
    This function will refresh an API keys access token if it has expired.
    This function will automatically trigger if another function is called and the Mimecasat server returns a 418 error indicating the current access token has expired.
.PARAMETER Password
    This parameter specifies the the password to use for the authentication process. It should be the same one initially used when creating the API keys.
.EXAMPLE
    PS C:\> Update-MimecastExpiredAccessKey
     
    This example runs the command without the passowrd parameter. The user will be prompted to input a password since that parameter is mandatory.
    Once the password has been entered the function will try to create a new access token to use and store it in a secure file.
.INPUTS
    None
.OUTPUTS
    None
.NOTES
    This function will automatically trigger if a function is called, but gets a 418 error indicating the current access token has expired.
 
    This re-authentication process was developed using the documenation in the link below.
    https://www.mimecast.com/developer/documentation/authentication-ui-apps/
#>

function Update-MimecastExpiredAccessKey {
    param(
        [Parameter(Mandatory)]
        [SecureString]$Password
    )
    Begin{
        $baseUrl = Get-mcBaseURL
        $apiCall = "/api/login/login"
        $url = $baseUrl + $apiCall

        $Keys = Get-MimecastAPIKeys
        $Appinfo = Get-MimecastAppInfo
        $accessKey = $Keys.AccessKey
        $appId = $Appinfo.AppId
        $emailAddress = $keys.EmailAddress
        $authType = $Keys.AuthType
    }

    Process{     
        $hdrDate = (Get-Date).ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss UTC")
        $requestId = [guid]::NewGuid().guid
        $cred = [pscredential]::new("Test",$Password)
        $pass = $cred.GetNetworkCredential().Password
        $headers = @{
            "Authorization" = $authType + " " + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($emailAddress + ":" + $pass))
            "x-mc-date" = $hdrDate
            "x-mc-app-id" = $appId
            "x-mc-req-id" = $requestId
            "Content-Type" = "application/json"
        }

        #Create post body
        $postBody = @{
            data = @(@{
                userName = $emailAddress
                accessKey = $accessKey
            })
        }
        $postBody = $postBody | ConvertTo-Json

        #Send Request
        $response = Invoke-MimecastAPI -Method Post -Headers $headers -Body $postBody -Uri $url
        
        #Print the response
        if ($response.data){
            $response.data
        }
        else{
            Write-Error "$($response.fail.errors.message)"
        }
    }
}
function Get-mcBaseURL {
    [CmdletBinding()]
    param()

    try{
        $region = (Get-MimecastRegion).Region
        return "https://$region-api.mimecast.com"
    }
    catch{
        $PSCmdlet.WriteError($_)
    }
}
function Get-MimecastAPIKeys {
    $Path = "$ENV:APPDATA\PSMimecast\Keys.xml"
    if (Test-Path -Path $Path){
        $SecretObject = Import-Clixml -Path $Path
        [PSCustomObject]@{
            AccessKey = [pscredential]::new("API",(ConvertTo-SecureString -String $SecretObject.AccessKey)).GetNetworkCredential().Password
            SecretKey = [pscredential]::new("API",(ConvertTo-SecureString -String $SecretObject.SecretKey)).GetNetworkCredential().Password
            EmailAddress = $SecretObject.EmailAddress
            AuthType = $SecretObject.AuthType
        }
    }
    else{
        Write-Error "Keys have not been set, use New-MimecastAPIKeys to set the keys" -ErrorAction Stop
    }
}
function Get-MimecastBaseURL {
    [cmdletbinding()]
    Param(
        $UserPrincipalName,
        $AppId
    )

    Begin{
        $baseUrl = "https://api.mimecast.com"
        $uri = "/api/login/discover-authentication"
        $url = $baseUrl + $uri
    }

    Process{    
        #Generate request header values
        $hdrDate = (Get-Date).ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss UTC")
        $requestId = [guid]::NewGuid().guid

        #Create Headers
        $headers = @{
            "x-mc-date" = $hdrDate; 
            "x-mc-app-id" = $Appid;
            "x-mc-req-id" = $requestId;
            "Content-Type" = "application/json"
        }

        #Create post body
        $postBody = @{
            data = @(@{
                emailAddress = $UserPrincipalName
            })
        }
        $postBody = $postBody | ConvertTo-Json
        #Send Request
        $response = Invoke-RestMethod -Method Post -Headers $headers -Body $postBody -Uri $url
        #Print the response
        $response
    }
}
function Invoke-MimecastAPI{
    [cmdletbinding()]
    param(
        $Method,
        $Uri,
        $headers,
        $Body
    )

    try{
        Write-Information "Sending API Request to $uri"
        $response = Invoke-RestMethod @PSBoundParameters
    }
    catch [System.Net.WebException]{
        Write-Information "A System.Net.WebException has occurred"
        if ($_.Exception.Message -like "*418*"){
            Write-Warning "Your API Access has expired. Use Update-MimecastExpiredAccessKey to update your expired AccessKey"
            if ((Read-Host "Would you like to Update your API Access key (Y/N)?") -like "Y*"){
                Update-MimecastExpiredAccessKey | Out-Null
                if ($?){
                    $response = Invoke-RestMethod @PSBoundParameters
                }
            }
        }
        elseif ($_.Exception.Message -like "*401*"){
            $errorresponse = $_.ErrorDetails.message | ConvertFrom-Json
            $email = $errorresponse.fail.key.username
            $message = $errorresponse.fail.errors.message
            $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
                        [System.Security.Authentication.InvalidCredentialException]::new($message),
                        'InvalidCredentialException',
                        [System.Management.Automation.ErrorCategory]::AuthenticationError,
                        $email
                    )
            throw $ErrorRecord
        }
    }
    catch{
        Write-Information "An unknown error has occurred"
        $PSCmdlet.WriteError($_)
    }

    return $response
}
function New-MimecastHeader{
    param(
        [string]$Uri
    )
    $Keys = Get-MimecastAPIKeys
    $Appinfo = Get-MimecastAppInfo
    $accessKey = $Keys.AccessKey
    $secretKey = $Keys.SecretKey
    $appId = $Appinfo.AppId
    $appKey = $Appinfo.AppKey

    #Generate request header values
    $hdrDate = (Get-Date).ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss UTC")
    $requestId = [guid]::NewGuid().guid

    #Create the HMAC SHA1 of the Base64 decoded secret key for the Authorization header
    $sha = New-Object System.Security.Cryptography.HMACSHA1
    $sha.key = [Convert]::FromBase64String($secretKey)
    $sig = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($hdrDate + ":" + $requestId + ":" + $Uri + ":" + $appKey))
    $sig = [Convert]::ToBase64String($sig)
        
    #Create Headers
    $headers = @{
        "Authorization" = "MC $($accessKey): $sig"
        "x-mc-date" = $hdrDate
        "x-mc-app-id" = $appId
        "x-mc-req-id" = $requestId
        "Content-Type" = "application/json"
    }
    
    $headers
}
function Set-MimecastAPIKeys{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory)]
        $AccessKey,
        [Parameter(Mandatory)]
        $SecretKey,
        $EmailAddress,
        $AuthType
    )

    Begin{
        $Path = "$ENV:APPDATA\PSMimecast"
        if (!(Test-Path -Path $Path)){
            New-Item -Path $Path -ItemType Directory -Force | Out-Null
        }
    } #Begin

    Process{
        $SecureAccessKey = ConvertTo-SecureString $AccessKey -AsPlainText -Force
        $SecureSecretKey = ConvertTo-SecureString $SecretKey -AsPlainText -Force

        $EncryptedAccessKey = ConvertFrom-SecureString -SecureString $SecureAccessKey
        $EncryptedSecureKey = ConvertFrom-SecureString -SecureString $SecureSecretKey

        $SecretObject = [PSCustomObject]@{
            AccessKey = $EncryptedAccessKey
            SecretKey = $EncryptedSecureKey
            EmailAddress = $EmailAddress
            AuthType = $AuthType
        }

        $SecretObject | Export-Clixml -Path "$Path\Keys.xml" -Force
    } #Process
}
Export-ModuleMember -function Get-MimecastAccount, Get-MimecastAppInfo, Get-MimecastDateTime, Get-MimecastDirectoryConnection, Get-MimecastHeldMessage, Get-MimecastHeldMessageSummary, Get-MimecastProfile, Get-MimecastRegion, Lock-MimecastAccount, New-HeldMessageRejectAction, New-HeldMessageReleaseAction, New-MimecastAPIKeys, Set-MimecastAppInfo, Set-MimecastRegion, Start-MimecastDirectorySync, Unlock-MimecastAccount, Update-MimecastExpiredAccessKey