Exch-Rest.psm1

## A PowerShell module for the Office 365 and Exchange 2016 REST API
## for documenation please refer to https://github.com/gscales/Exch-Rest
##
## The MIT License (MIT)
##
## Copyright (c) 2017 Glen Scales
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:

## The above copyright notice and this permission notice shall be included in all
## copies or substantial portions of the Software.

## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
function Get-AppSettings(){
        param( 
        
        )  
     Begin
         {
            $configObj = "" |  select ResourceURL,ClientId,redirectUrl,ClientSecret,x5t,TenantId,ValidateForMinutes
            $configObj.ResourceURL = "outlook.office.com"
            $configObj.ClientId = "" # 1bdbfb41-f690-4f93-b0bb-002004bbca79
            $configObj.redirectUrl = "" # http://localhost:8000/authorize
            $configObj.TenantId = "" # 1c3a18bf-da31-4f6c-a404-2c06c9cf5ae4
            $configObj.ClientSecret = ""
            $configObj.x5t = "" # VS/H6cNa/3gc9FrSxGs9jOOZP3o=
            $configObj.ValidateForMinutes = 60
            return $configObj            
         }    
}

function Get-HTTPClient{ 
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName
    )  
     Begin
         {
            Add-Type -AssemblyName System.Net.Http
            $handler = New-Object  System.Net.Http.HttpClientHandler
            $handler.CookieContainer = New-Object System.Net.CookieContainer
            $handler.AllowAutoRedirect = $true;
            $HttpClient = New-Object System.Net.Http.HttpClient($handler);
            #$HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "");
            $Header = New-Object System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")
            $HttpClient.DefaultRequestHeaders.Accept.Add($Header);
            $HttpClient.Timeout = New-Object System.TimeSpan(0, 0, 90);
            $HttpClient.DefaultRequestHeaders.TransferEncodingChunked = $false
            if (!$HttpClient.DefaultRequestHeaders.Contains("X-AnchorMailbox")){
                $HttpClient.DefaultRequestHeaders.Add("X-AnchorMailbox", $MailboxName);
            }
            $Header = New-Object System.Net.Http.Headers.ProductInfoHeaderValue("RestClient", "1.1")
            $HttpClient.DefaultRequestHeaders.UserAgent.Add($Header);
            return $HttpClient
         }
}

function Convert-FromBase64StringWithNoPadding([string]$data)
{
    $data = $data.Replace('-', '+').Replace('_', '/')
    switch ($data.Length % 4)
    {
        0 { break }
        2 { $data += '==' }
        3 { $data += '=' }
        default { throw New-Object ArgumentException('data') }
    }
    return [System.Convert]::FromBase64String($data)
}

function Invoke-DecodeToken { 
        param( 
        [Parameter(Position=1, Mandatory=$true)] [String]$Token
    )  
    ## Start Code Attribution
    ## Decode-Token function is based on work of the following Authors and should remain with the function if copied into other scripts
    ## https://gallery.technet.microsoft.com/JWT-Token-Decode-637cf001
    ## End Code Attribution
    Begin
    {
        $parts = $Token.Split('.');
        $headers = [System.Text.Encoding]::UTF8.GetString((Convert-FromBase64StringWithNoPadding $parts[0]))
        $claims = [System.Text.Encoding]::UTF8.GetString((Convert-FromBase64StringWithNoPadding $parts[1]))
        $signature = (Convert-FromBase64StringWithNoPadding $parts[2])

        $customObject = [PSCustomObject]@{
            headers = ($headers | ConvertFrom-Json)
            claims = ($claims | ConvertFrom-Json)
            signature = $signature
        }
        return $customObject
    }
}

function New-JWTToken{
        param( 
        [Parameter(Position=1, Mandatory=$true)] [string]$CertFileName,
        [Parameter(Position=2, Mandatory=$true)] [string]$TenantId,
        [Parameter(Position=3, Mandatory=$true)] [string]$ClientId,        
        [Parameter(Position=4, Mandatory=$true)] [Int32]$ValidateForMinutes,
        [Parameter(Mandatory=$True)][Security.SecureString]$password        
    )  
     Begin
         {
           
            $date1 = Get-Date -Date "01/01/1970"
            $date2 = (Get-Date).ToUniversalTime().AddMinutes($ValidateForMinutes)           
            $date3 = (Get-Date).ToUniversalTime().AddMinutes(-5)      
            $exp = [Math]::Round((New-TimeSpan -Start $date1 -End $date2).TotalSeconds,0) 
            $nbf = [Math]::Round((New-TimeSpan -Start $date1 -End $date3).TotalSeconds,0) 
            $exVal = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
            $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $CertFileName,$password,$exVal
            $x5t = [System.Convert]::ToBase64String($cert.GetCertHash())
            $jti = [System.Guid]::NewGuid().ToString()
            $Headerassertaion =  "{" 
            $Headerassertaion += " `"alg`": `"RS256`"," 
            $Headerassertaion += " `"x5t`": `""+ $x5t + "`""
            $Headerassertaion += "}"
            $PayLoadassertaion += "{"
            $PayLoadassertaion += " `"aud`": `"https://login.windows.net/" + $TenantId +"/oauth2/token`"," 
            $PayLoadassertaion += " `"exp`": $exp,"            
            $PayLoadassertaion += " `"iss`": `""+ $ClientId + "`"," 
            $PayLoadassertaion += " `"jti`": `"" + $jti + "`","
            $PayLoadassertaion += " `"nbf`": $nbf,"       
            $PayLoadassertaion += " `"sub`": `"" + $ClientId + "`""              
            $PayLoadassertaion += "} " 
            $encodedHeader = [System.Convert]::ToBase64String([System.Text.UTF8Encoding]::UTF8.GetBytes($Headerassertaion)).Replace('=','').Replace('+', '-').Replace('/', '_')
            $encodedPayLoadassertaion = [System.Convert]::ToBase64String([System.Text.UTF8Encoding]::UTF8.GetBytes($PayLoadassertaion)).Replace('=','').Replace('+', '-').Replace('/', '_')
            $JWTOutput = $encodedHeader + "." + $encodedPayLoadassertaion
            $SigBytes = [System.Text.UTF8Encoding]::UTF8.GetBytes($JWTOutput)            
            $rsa = $cert.PrivateKey;
            $sha256 = [System.Security.Cryptography.SHA256]::Create()
            $hash = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($encodedHeader + '.' + $encodedPayLoadassertaion));
            $sigform = New-Object System.Security.Cryptography.RSAPKCS1SignatureFormatter($rsa);
            $sigform.SetHashAlgorithm("SHA256");
            $sig = [System.Convert]::ToBase64String($sigform.CreateSignature($hash)).Replace('=','').Replace('+', '-').Replace('/', '_')
            $JWTOutput = $encodedHeader + '.' + $encodedPayLoadassertaion + '.' + $sig
            Write-Output ($JWTOutput)

         }
}

function Invoke-CreateSelfSignedCert{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$CertName,
        [Parameter(Position=1, Mandatory=$true)] [string]$CertFileName,
        [Parameter(Position=2, Mandatory=$true)] [string]$KeyFileName
    )  
     Begin
         {
             $Cert = New-SelfSignedCertificate -certstorelocation cert:\currentuser\my -dnsname $CertName -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider' 
             $SecurePassword = Read-Host -Prompt "Enter password" -AsSecureString
             $CertPath = "cert:\currentuser\my\" + $Cert.Thumbprint.ToString()
             Export-PfxCertificate -cert $CertPath -FilePath $CertFileName -Password $SecurePassword 
             $bin = $cert.RawData
             $base64Value = [System.Convert]::ToBase64String($bin)
             $bin = $cert.GetCertHash()
             $base64Thumbprint = [System.Convert]::ToBase64String($bin)
             $keyid = [System.Guid]::NewGuid().ToString()
             $jsonObj = @{customKeyIdentifier=$base64Thumbprint;keyId=$keyid;type="AsymmetricX509Cert";usage="Verify";value=$base64Value}
             $keyCredentials=ConvertTo-Json @($jsonObj) | Out-File $KeyFileName
             Remove-Item $CertPath
             Write-Host ("Key written to " + $KeyFileName)
             
         }
    
}

Function Show-OAuthWindow
{
    param(
        [System.Uri]$Url
    )
    ## Start Code Attribution
    ## Show-AuthWindow function is the work of the following Authors and should remain with the function if copied into other scripts
    ## https://foxdeploy.com/2015/11/02/using-powershell-and-oauth/
    ## https://blogs.technet.microsoft.com/ronba/2016/05/09/using-powershell-and-the-office-365-rest-api-with-oauth/
    ## End Code Attribution
    Add-Type -AssemblyName System.Web
    Add-Type -AssemblyName System.Windows.Forms
 
    $form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width=440;Height=640}
    $web  = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width=420;Height=600;Url=($url ) }
    $DocComp  = {
        $Global:uri = $web.Url.AbsoluteUri
        if ($Global:Uri -match "error=[^&]*|code=[^&]*") {$form.Close() }
    }
    $web.ScriptErrorsSuppressed = $true
    $web.Add_DocumentCompleted($DocComp)
    $form.Controls.Add($web)
    $form.Add_Shown({$form.Activate()})
    $form.ShowDialog() | Out-Null
    $queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query)
    $output = @{}
    foreach($key in $queryOutput.Keys){
        $output["$key"] = $queryOutput[$key]
    }
    return $output 
}
function Get-AccessToken{ 
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [string]$ClientId,
        [Parameter(Position=2, Mandatory=$false)] [string]$redirectUrl,
        [Parameter(Position=3, Mandatory=$false)] [string]$ClientSecret,
        [Parameter(Position=4, Mandatory=$false)] [string]$ResourceURL,
        [Parameter(Position=5, Mandatory=$false)] [switch]$Beta
    )  
     Begin
         {
            Add-Type -AssemblyName System.Web
            $HttpClient =  Get-HTTPClient($MailboxName)
            $AppSetting = Get-AppSettings 
           if($ClientId -eq $null){
                 $ClientId = $AppSetting.ClientId
            }
            if($ClientSecret -eq $null){
                 $ClientSecret =  $AppSetting.ClientSecret
            }           
            if($redirectUrl -eq $null){
                $redirectUrl = [System.Web.HttpUtility]::UrlEncode($AppSetting.redirectUrl)
            }
            else{
                $redirectUrl = [System.Web.HttpUtility]::UrlEncode($redirectUrl)
            }
            if([String]::IsNullOrEmpty($ResourceURL)){
                $ResourceURL = $AppSetting.ResourceURL
            }      
            $Phase1auth = Show-OAuthWindow -Url "https://login.microsoftonline.com/common/oauth2/authorize?resource=https%3A%2F%2F$ResourceURL&client_id=$ClientId&response_type=code&redirect_uri=$redirectUrl&prompt=login"
            $code = $Phase1auth["code"]
            $AuthorizationPostRequest = "resource=https%3A%2F%2F$ResourceURL&client_id=$ClientId&grant_type=authorization_code&code=$code&redirect_uri=$redirectUrl"
            if(![String]::IsNullOrEmpty($ClientSecret)){
                $AuthorizationPostRequest = "resource=https%3A%2F%2F$ResourceURL&client_id=$ClientId&client_secret=$ClientSecret&grant_type=authorization_code&code=$code&redirect_uri=$redirectUrl"
            }
            $content = New-Object System.Net.Http.StringContent($AuthorizationPostRequest, [System.Text.Encoding]::UTF8, "application/x-www-form-urlencoded")
            $ClientReesult = $HttpClient.PostAsync([Uri]("https://login.windows.net/common/oauth2/token"),$content)
            $JsonObject = ConvertFrom-Json -InputObject  $ClientReesult.Result.Content.ReadAsStringAsync().Result
            if([bool]($JsonObject.PSobject.Properties.name -match "refresh_token")){
                $JsonObject.refresh_token =  (Get-ProtectedToken -PlainToken $JsonObject.refresh_token) 
            }
            if([bool]($JsonObject.PSobject.Properties.name -match "access_token")){
                $JsonObject.access_token =  (Get-ProtectedToken -PlainToken $JsonObject.access_token) 
            }
            Add-Member -InputObject $JsonObject -NotePropertyName clientid -NotePropertyValue $ClientId
            Add-Member -InputObject $JsonObject -NotePropertyName redirectUrl -NotePropertyValue $redirectUrl
            if($Beta.IsPresent){
                Add-Member -InputObject $JsonObject -NotePropertyName Beta -NotePropertyValue True
            }
            return $JsonObject
         }
}

function Get-AccessTokenUserAndPass{ 
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [string]$ClientId,
        [Parameter(Position=2, Mandatory=$false)] [string]$ResourceURL,
        [Parameter(Position=3, Mandatory=$true)] [System.Management.Automation.PSCredential]$Credentials,
        [Parameter(Position=4, Mandatory=$false)] [switch]$Beta
    )  
     Begin
         {
            Add-Type -AssemblyName System.Web
            $HttpClient =  Get-HTTPClient($MailboxName)
            $AppSetting = Get-AppSettings 
           if($ClientId -eq $null){
                 $ClientId = $AppSetting.ClientId
            }      

            if([String]::IsNullOrEmpty($ResourceURL)){
                $ResourceURL = $AppSetting.ResourceURL
            }  
            $UserName = $Credentials.UserName.ToString()
            $password = $Credentials.GetNetworkCredential().password.ToString()    
            $AuthorizationPostRequest = "resource=https%3A%2F%2F$ResourceURL&client_id=$ClientId&grant_type=password&username=$username&password=$password"
            $content = New-Object System.Net.Http.StringContent($AuthorizationPostRequest, [System.Text.Encoding]::UTF8, "application/x-www-form-urlencoded")
            $ClientReesult = $HttpClient.PostAsync([Uri]("https://login.windows.net/common/oauth2/token"),$content)
            $JsonObject = ConvertFrom-Json -InputObject  $ClientReesult.Result.Content.ReadAsStringAsync().Result
            Add-Member -InputObject $JsonObject -NotePropertyName clientid -NotePropertyValue $ClientId
            if([bool]($JsonObject.PSobject.Properties.name -match "refresh_token")){
                $JsonObject.refresh_token =  (Get-ProtectedToken -PlainToken $JsonObject.refresh_token) 
            }
            if([bool]($JsonObject.PSobject.Properties.name -match "access_token")){
                $JsonObject.access_token =  (Get-ProtectedToken -PlainToken $JsonObject.access_token) 
            }
            if($Beta.IsPresent){
                Add-Member -InputObject $JsonObject -NotePropertyName Beta -NotePropertyValue True
            }
            #Add-Member -InputObject $JsonObject -NotePropertyName redirectUrl -NotePropertyValue $redirectUrl
            return $JsonObject
         }
}



function Get-AppOnlyToken{ 
    param( 
       
        [Parameter(Position=1, Mandatory=$true)] [string]$CertFileName,
        [Parameter(Position=2, Mandatory=$false)] [string]$TenantId,
        [Parameter(Position=3, Mandatory=$false)] [string]$ClientId,
        [Parameter(Position=4, Mandatory=$false)] [string]$redirectUrl,     
        [Parameter(Position=6, Mandatory=$false)] [Int32]$ValidateForMinutes,
        [Parameter(Position=7, Mandatory=$false)] [string]$ResourceURL,
        [Parameter(Position=8, Mandatory=$false)] [switch]$Beta,
        [Parameter(Mandatory=$true)] [Security.SecureString]$password
        
    )  
     Begin
         {
             $AppSetting = Get-AppSettings 
            if($TenantId -eq $null){
                $AppSetting.TenantId
            }
            if($ClientId -eq $null){
                 $ClientId = $AppSetting.ClientId
            }
            if($redirectUrl -eq $null){
                $redirectUrl = $AppSetting.redirectUrl
            }
            if($ValidateForMinutes -eq 0){
                $ValidateForMinutes = $AppSetting.ValidateForMinutes               
            }
            if([String]::IsNullOrEmpty($ResourceURL)){
                $ResourceURL = $AppSetting.ResourceURL
            }        
            $JWTToken = New-JWTToken -CertFileName $CertFileName -password $password -TenantId $TenantId -ClientId $ClientId  -ValidateForMinutes $ValidateForMinutes
            Add-Type -AssemblyName System.Web
            $HttpClient =  Get-HTTPClient(" ")          
            $AuthorizationPostRequest = "resource=https%3A%2F%2F$ResourceURL&client_id=$ClientId&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=$JWTToken&grant_type=client_credentials&redirect_uri=$redirectUrl"
            $content = New-Object System.Net.Http.StringContent($AuthorizationPostRequest, [System.Text.Encoding]::UTF8, "application/x-www-form-urlencoded")
            $ClientReesult = $HttpClient.PostAsync([Uri]("https://login.windows.net/" + $TenantId + "/oauth2/token"),$content)
            $JsonObject = ConvertFrom-Json -InputObject  $ClientReesult.Result.Content.ReadAsStringAsync().Result
            if([bool]($JsonObject.PSobject.Properties.name -match "refresh_token")){
                $JsonObject.refresh_token =  (Get-ProtectedToken -PlainToken $JsonObject.refresh_token) 
            }
            if([bool]($JsonObject.PSobject.Properties.name -match "access_token")){
                $JsonObject.access_token =  (Get-ProtectedToken -PlainToken $JsonObject.access_token) 
            }
            Add-Member -InputObject $JsonObject -NotePropertyName tenantid -NotePropertyValue $TenantId
            Add-Member -InputObject $JsonObject -NotePropertyName clientid -NotePropertyValue $ClientId
            Add-Member -InputObject $JsonObject -NotePropertyName redirectUrl -NotePropertyValue $redirectUrl
            if($Beta.IsPresent){
                Add-Member -InputObject $JsonObject -NotePropertyName Beta -NotePropertyValue True
            }
            return $JsonObject
         }
}



function Invoke-RefreshAccessToken{ 
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$true)] [psobject]$AccessToken
    )  
     Begin
         {
            Add-Type -AssemblyName System.Web
            $HttpClient =  Get-HTTPClient($MailboxName)
            $ClientId = $AccessToken.clientid            
           # $redirectUrl = [System.Web.HttpUtility]::UrlEncode($AccessToken.redirectUrl)
            $redirectUrl = $AccessToken.redirectUrl
            $RefreshToken = (Get-TokenFromSecureString -SecureToken $AccessToken.refresh_token)
            $AuthorizationPostRequest = "client_id=$ClientId&refresh_token=$RefreshToken&grant_type=refresh_token&redirect_uri=$redirectUrl"
            $content = New-Object System.Net.Http.StringContent($AuthorizationPostRequest, [System.Text.Encoding]::UTF8, "application/x-www-form-urlencoded")
            $ClientResult = $HttpClient.PostAsync([Uri]("https://login.windows.net/common/oauth2/token"),$content)             
             if (!$ClientResult.Result.IsSuccessStatusCode)
             {                    
                     Write-Output ("Error making REST POST " + $ClientResult.Result.StatusCode + " : " + $ClientResult.Result.ReasonPhrase)
                     Write-Output $ClientResult.Result
                     if($ClientResult.Content -ne $null){
                         Write-Output ($ClientResult.Content.ReadAsStringAsync().Result);   
                     }                     
             }
            else
             {
               $JsonObject = ConvertFrom-Json -InputObject  $ClientResult.Result.Content.ReadAsStringAsync().Result
               Add-Member -InputObject $JsonObject -NotePropertyName clientid -NotePropertyValue $AccessToken.clientid
               Add-Member -InputObject $JsonObject -NotePropertyName redirectUrl -NotePropertyValue $AccessToken.redirectUrl
               if([bool]($AccessToken.PSobject.Properties.name -match "Beta")){
                    Add-Member -InputObject $JsonObject -NotePropertyName Beta -NotePropertyValue True
               }
               if([bool]($JsonObject.PSobject.Properties.name -match "refresh_token")){
                   $JsonObject.refresh_token =  (Get-ProtectedToken -PlainToken $JsonObject.refresh_token) 
               }
               if([bool]($JsonObject.PSobject.Properties.name -match "access_token")){
                   $JsonObject.access_token =  (Get-ProtectedToken -PlainToken $JsonObject.access_token) 
               }
               return $JsonObject
             }

         }
}

function Get-TokenFromSecureString{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [System.Security.SecureString]$SecureToken
    )
    begin{
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureToken)
        $Token = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
        return,$Token
    }
}

function Get-ProtectedToken{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [String]$PlainToken
    )
    begin{
          $SecureString = New-Object System.Security.SecureString
          for ($i = 0; $i -lt $PlainToken.length; $i++) 
          {
              $SecureString.AppendChar($PlainToken[$i]) 
          }
          $EncryptedToken = ConvertFrom-SecureString -SecureString $SecureString 
          $SecureEncryptedToken = ConvertTo-SecureString -String $EncryptedToken
          return,$SecureEncryptedToken
    }
}
## Start Code Attribution
## ExpandPayload function is the work of the following Authors and should remain with the function if copied into other scripts
## https://www.powershellgallery.com/profiles/chriswahl/
## End Code Attribution
function ExpandPayload($response)
{
  [void][System.Reflection.Assembly]::LoadWithPartialName('System.Web.Extensions')
  return ParseItem -jsonItem ((New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property @{
        MaxJsonLength = [Int32]::MaxValue
  }).DeserializeObject($response))
}
function ParseItem($jsonItem) 
{
  if($jsonItem.PSObject.TypeNames -match 'Array') 
  {
    return ParseJsonArray -jsonArray ($jsonItem)
  }
  elseif($jsonItem.PSObject.TypeNames -match 'Dictionary') 
  {
    return ParseJsonObject -jsonObj ([HashTable]$jsonItem)
  }
  else 
  {
    return $jsonItem
  }
}
## Start Code Attribution
## ParseJsonObject function is the work of the following Authors and should remain with the function if copied into other scripts
## https://www.powershellgallery.com/profiles/chriswahl/
## End Code Attribution
function ParseJsonObject($jsonObj) 
{
  $result = New-Object -TypeName PSCustomObject
  foreach ($key in $jsonObj.Keys) 
  {
    $item = $jsonObj[$key]
    if ($item) 
    {
      $parsedItem = ParseItem -jsonItem $item
    }
    else 
    {
      $parsedItem = $null
    }
    $result | Add-Member -MemberType NoteProperty -Name $key -Value $parsedItem
  }
  return $result
}
## Start Code Attribution
## ParseJsonArray function is the work of the following Authors and should remain with the function if copied into other scripts
## https://www.powershellgallery.com/profiles/chriswahl/
## End Code Attribution
function ParseJsonArray($jsonArray) 
{
  $result = @()
  $jsonArray | ForEach-Object -Process {
    $result += , (ParseItem -jsonItem $_)
  }
  return $result
}
## Start Code Attribution
## ParseJsonString function is the work of the following Authors and should remain with the function if copied into other scripts
## https://www.powershellgallery.com/profiles/chriswahl/
## End Code Attribution
function ParseJsonString($json) 
{
  $config = $javaScriptSerializer.DeserializeObject($json)
  return ParseJsonObject -jsonObj ($config)
}
####
function Invoke-RestGet
{
        param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$RequestURL,
        [Parameter(Position=1, Mandatory=$true)] [String]$MailboxName,
        [Parameter(Position=2, Mandatory=$true)] [System.Net.Http.HttpClient]$HttpClient,
        [Parameter(Position=3, Mandatory=$true)] [psobject]$AccessToken,
        [Parameter(Position=4, Mandatory=$false)] [switch]$NoJSON
    )  
     Begin
         {
             #Check for expired Token
             $minTime = new-object DateTime(1970, 1, 1, 0, 0, 0, 0,[System.DateTimeKind]::Utc);
             $expiry =  $minTime.AddSeconds($AccessToken.expires_on)
             if($expiry -le [DateTime]::Now.ToUniversalTime()){
                if([bool]($AccessToken.PSobject.Properties.name -match "refresh_token")){
                    write-host "Refresh Token"
                    $AccessToken = Invoke-RefreshAccessToken -MailboxName $MailboxName -AccessToken $AccessToken          
                    Set-Variable -Name "AccessToken" -Value $AccessToken -Scope Script -Visibility Public
                }
                else{
                    throw "App Token has expired"
                }

             }
             $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", (Get-TokenFromSecureString -SecureToken $AccessToken.access_token));
             $HttpClient.DefaultRequestHeaders.Add("Prefer", ("outlook.timezone=`"" + [TimeZoneInfo]::Local.Id + "`"")) 
             $ClientResult = $HttpClient.GetAsync($RequestURL)
             if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::OK){
                 if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::Created){
                     write-Host ($ClientResult.Result)
                 }
             }             
             if (!$ClientResult.Result.IsSuccessStatusCode)
             {
                     Write-Output ("Error making REST Get " + $ClientResult.Result.StatusCode + " : " + $ClientResult.Result.ReasonPhrase)
                     Write-Output $ClientResult.Result
                     if($ClientResult.Content -ne $null){
                         Write-Output ($ClientResult.Content.ReadAsStringAsync().Result);   
                     }                     
             }
            else
             {
               if($NoJSON){
                    return  $ClientResult.Result.Content  
               }
               else{
                    $JsonObject = ExpandPayload($ClientResult.Result.Content.ReadAsStringAsync().Result) 
                    #$JsonObject = ConvertFrom-Json -InputObject $ClientResult.Result.Content.ReadAsStringAsync().Result
                   if([String]::IsNullOrEmpty($ClientResult)){
                        write-host "No Value returned"
                   }
                   else{
                       return $JsonObject
                   }

               }  

             }
  
         }    
}

function Invoke-RestPOST
{
        param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$RequestURL,
        [Parameter(Position=1, Mandatory=$true)] [String]$MailboxName,
        [Parameter(Position=2, Mandatory=$true)] [System.Net.Http.HttpClient]$HttpClient,
        [Parameter(Position=3, Mandatory=$true)] [psobject]$AccessToken,
        [Parameter(Position=4, Mandatory=$true)] [PSCustomObject]$Content
    )  
     Begin
         {
             #Check for expired Token
             $minTime = new-object DateTime(1970, 1, 1, 0, 0, 0, 0,[System.DateTimeKind]::Utc);
             $expiry =  $minTime.AddSeconds($AccessToken.expires_on)
             if($expiry -le [DateTime]::Now.ToUniversalTime()){
                if([bool]($AccessToken.PSobject.Properties.name -match "refresh_token")){
                    write-host "Refresh Token"
                    $AccessToken = Invoke-RefreshAccessToken -MailboxName $MailboxName -AccessToken $AccessToken                
                    Set-Variable -Name "AccessToken" -Value $AccessToken -Scope Script -Visibility Public
                }
                else{
                    throw "App Token has expired a new access token is required rerun get-apptoken"
                }
             }
             $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", (Get-TokenFromSecureString -SecureToken $AccessToken.access_token));
             $PostContent = New-Object System.Net.Http.StringContent($Content, [System.Text.Encoding]::UTF8, "application/json")
             $HttpClient.DefaultRequestHeaders.Add("Prefer", ("outlook.timezone=`"" + [TimeZoneInfo]::Local.Id + "`"")) 
             $ClientResult = $HttpClient.PostAsync([Uri]($RequestURL),$PostContent)
             if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::OK){
                 if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::Created){
                     write-Host ($ClientResult.Result)
                 }
             }   
             if (!$ClientResult.Result.IsSuccessStatusCode)
             {
                     Write-Output ("Error making REST POST " + $ClientResult.Result.StatusCode + " : " + $ClientResult.Result.ReasonPhrase)
                     Write-Output $ClientResult.Result
                     if($ClientResult.Content -ne $null){
                         Write-Output ($ClientResult.Content.ReadAsStringAsync().Result);   
                     }                     
             }
            else
             {
               $JsonObject = ExpandPayload($ClientResult.Result.Content.ReadAsStringAsync().Result)
               #$JsonObject = ConvertFrom-Json -InputObject $ClientResult.Result.Content.ReadAsStringAsync().Result
               if([String]::IsNullOrEmpty($JsonObject)){
                   Write-Output $ClientResult.Result
               }
               else{
                   return $JsonObject
               }
               
             }
  
         }    
}

function Invoke-RestPatch
{
        param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$RequestURL,
        [Parameter(Position=1, Mandatory=$true)] [String]$MailboxName,
        [Parameter(Position=2, Mandatory=$true)] [System.Net.Http.HttpClient]$HttpClient,
        [Parameter(Position=3, Mandatory=$true)] [psobject]$AccessToken,
        [Parameter(Position=4, Mandatory=$true)] [PSCustomObject]$Content
    )  
     Begin
         {
             #Check for expired Token
             $minTime = new-object DateTime(1970, 1, 1, 0, 0, 0, 0,[System.DateTimeKind]::Utc);
             $expiry =  $minTime.AddSeconds($AccessToken.expires_on)
             if($expiry -le [DateTime]::Now.ToUniversalTime()){
                write-host "Refresh Token"
                $AccessToken = Invoke-RefreshAccessToken -MailboxName $MailboxName -AccessToken $AccessToken            
                Set-Variable -Name "AccessToken" -Value $AccessToken -Scope Script -Visibility Public
             }
             $method =  New-Object System.Net.Http.HttpMethod("PATCH")
             $HttpRequestMessage =  New-Object System.Net.Http.HttpRequestMessage($method,[Uri]$RequestURL)
             $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", (Get-TokenFromSecureString -SecureToken $AccessToken.access_token));
             $HttpRequestMessage.Content = New-Object System.Net.Http.StringContent($Content, [System.Text.Encoding]::UTF8, "application/json")
             $ClientResult = $HttpClient.SendAsync($HttpRequestMessage)
             if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::OK){
                 if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::Created){
                     write-Host ($ClientResult.Result)
                 }
             }   
             if (!$ClientResult.Result.IsSuccessStatusCode)
             {
                     Write-Output ("Error making REST Patch " + $ClientResult.Result.StatusCode + " : " + $ClientResult.Result.ReasonPhrase)
                     Write-Output $ClientResult.Result
                     if($ClientResult.Content -ne $null){
                         Write-Output ($ClientResult.Content.ReadAsStringAsync().Result);   
                     }                     
             }
            else
             {
              # $JsonObject = ConvertFrom-Json -InputObject $ClientResult.Result.Content.ReadAsStringAsync().Result
               $JsonObject = ExpandPayload($ClientResult.Result.Content.ReadAsStringAsync().Result)
               if([String]::IsNullOrEmpty($JsonObject)){
                   Write-Output $ClientResult.Result
               }
               else{
                   return $JsonObject
               }
               
             }
  
         }    
}

function Invoke-RestPut
{
        param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$RequestURL,
        [Parameter(Position=1, Mandatory=$true)] [String]$MailboxName,
        [Parameter(Position=2, Mandatory=$true)] [System.Net.Http.HttpClient]$HttpClient,
        [Parameter(Position=3, Mandatory=$true)] [psobject]$AccessToken,
        [Parameter(Position=4, Mandatory=$true)] [String]$ContentHeader,
        [Parameter(Position=5, Mandatory=$true)] [PSCustomObject]$Content
        
    )  
     Begin
         {
             #Check for expired Token
             $minTime = new-object DateTime(1970, 1, 1, 0, 0, 0, 0,[System.DateTimeKind]::Utc);
             $expiry =  $minTime.AddSeconds($AccessToken.expires_on)
             if($expiry -le [DateTime]::Now.ToUniversalTime()){
                write-host "Refresh Token"
                $AccessToken = Invoke-RefreshAccessToken -MailboxName $MailboxName -AccessToken $AccessToken            
                Set-Variable -Name "AccessToken" -Value $AccessToken -Scope Script -Visibility Public
             }
             $method =  New-Object System.Net.Http.HttpMethod("PUT")
             $HttpRequestMessage =  New-Object System.Net.Http.HttpRequestMessage($method,[Uri]$RequestURL)
             $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", (Get-TokenFromSecureString -SecureToken $AccessToken.access_token));
             if(![String]::IsNullorEmpty($ContentHeader)){
                   $HttpRequestMessage.Content = New-Object System.Net.Http.ByteArrayContent -ArgumentList @(,$Content)  
             }
             else{
                   $HttpRequestMessage.Content = New-Object System.Net.Http.StringContent($Content, [System.Text.Encoding]::UTF8, "application/json")
             }
             $ClientResult = $HttpClient.SendAsync($HttpRequestMessage)
             if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::OK){
                 if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::Created){
                     write-Host ($ClientResult.Result)
                 }
             }   
             if (!$ClientResult.Result.IsSuccessStatusCode)
             {
                     Write-Output ("Error making REST PUT " + $ClientResult.Result.StatusCode + " : " + $ClientResult.Result.ReasonPhrase)
                     Write-Output $ClientResult.Result
                     if($ClientResult.Content -ne $null){
                         Write-Output ($ClientResult.Content.ReadAsStringAsync().Result);   
                     }                     
             }
            else
             {
              # $JsonObject = ConvertFrom-Json -InputObject $ClientResult.Result.Content.ReadAsStringAsync().Result
               $JsonObject = ExpandPayload($ClientResult.Result.Content.ReadAsStringAsync().Result)
               if([String]::IsNullOrEmpty($JsonObject)){
                   Write-Output $ClientResult.Result
               }
               else{
                   return $JsonObject
               }
               
             }
  
         }    
}
function Invoke-RestDELETE
{
        param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$RequestURL,
        [Parameter(Position=1, Mandatory=$true)] [String]$MailboxName,
        [Parameter(Position=2, Mandatory=$true)] [System.Net.Http.HttpClient]$HttpClient,
        [Parameter(Position=3, Mandatory=$true)] [psobject]$AccessToken

    )  
     Begin
         {
             #Check for expired Token
             $minTime = new-object DateTime(1970, 1, 1, 0, 0, 0, 0,[System.DateTimeKind]::Utc);
             $expiry =  $minTime.AddSeconds($AccessToken.expires_on)
             if($expiry -le [DateTime]::Now.ToUniversalTime()){
                write-host "Refresh Token"
                $AccessToken = Invoke-RefreshAccessToken -MailboxName $MailboxName -AccessToken $AccessToken          
                Set-Variable -Name "AccessToken" -Value $AccessToken -Scope Script -Visibility Public
             }
             $method =  New-Object System.Net.Http.HttpMethod("DELETE")
             $HttpRequestMessage =  New-Object System.Net.Http.HttpRequestMessage($method,[Uri]$RequestURL)
             $HttpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", (Get-TokenFromSecureString -SecureToken $AccessToken.access_token));
             $ClientResult = $HttpClient.SendAsync($HttpRequestMessage)
             if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::OK){
                 if($ClientResult.Result.StatusCode -ne [System.Net.HttpStatusCode]::NoContent){
                     write-Host ($ClientResult.Result)
                 }
             }   
             if (!$ClientResult.Result.IsSuccessStatusCode)
             {
                     Write-Output ("Error making REST Delete " + $ClientResult.Result.StatusCode + " : " + $ClientResult.Result.ReasonPhrase)
                     Write-Output $ClientResult.Result
                     if($ClientResult.Content -ne $null){
                         Write-Output ($ClientResult.Content.ReadAsStringAsync().Result);   
                     }                     
             }
            else
             {
               $JsonObject = ConvertFrom-Json -InputObject  $ClientResult.Result.Content.ReadAsStringAsync().Result
               if([String]::IsNullOrEmpty($JsonObject)){
                   Write-Output $ClientResult.Result
               }
               else{
                   return $JsonObject
               }
               
             }
  
         }    
}
function Get-MailboxSettings{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "('$MailboxName')/MailboxSettings"
        return Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
    }
}

function Get-AutomaticRepliesSettings{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName  
                    
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL = $EndPoint + "('$MailboxName')/MailboxSettings/AutomaticRepliesSetting"
       return Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
    }
}

function Get-MailboxTimeZone{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "('$MailboxName')/MailboxSettings/TimeZone"
        return Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
    }
}

function Get-FolderFromPath{
    param (
            [Parameter(Position=0, Mandatory=$true)] [string]$FolderPath,
            [Parameter(Position=1, Mandatory=$true)] [string]$MailboxName,
            [Parameter(Position=2, Mandatory=$false)] [psobject]$AccessToken
          )
    process{
        ## Find and Bind to Folder based on Path
        #Define the path to search should be seperated with \
        #Bind to the MSGFolder Root
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "('$MailboxName')/MailFolders/msgfolderroot/childfolders?"
      # $RootFolder = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        #Split the Search path into an array
        $tfTargetFolder = $RootFolder
        $fldArray = $FolderPath.Split("\") 
         #Loop through the Split Array and do a Search for each level of folder
        for ($lint = 1; $lint -lt $fldArray.Length; $lint++) { 
            #Perform search based on the displayname of each folder level
            $FolderName = $fldArray[$lint];
            $RequestURL = $RequestURL += "`$filter=DisplayName eq '$FolderName'" 
            $tfTargetFolder = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            if($tfTargetFolder.Value.displayname -match $FolderName){
                $folderId = $tfTargetFolder.value.Id.ToString()
                $RequestURL =  $EndPoint + "('$MailboxName')/MailFolders('$folderId')/childfolders?"
            }
            else{
                throw ("Folder Not found")
            }
        }  
        if($tfTargetFolder.Value -ne $null){
            $folderId = $tfTargetFolder.Value.Id.ToString()
            Add-Member -InputObject $tfTargetFolder.Value -NotePropertyName FolderRestURI -NotePropertyValue ($EndPoint + "('$MailboxName')/MailFolders('$folderId')")
            return ,$tfTargetFolder.Value
        }
        else{
            throw ("Folder Not found")
        }
    }
}

function Get-Inbox{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =   $EndPoint + "('$MailboxName')/MailFolders/Inbox"
        return Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
    }
}

function Get-InboxItems{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL = $EndPoint + "('$MailboxName')/MailFolders/Inbox/messages/?`$select=ReceivedDateTime,Sender,Subject,IsRead,InferenceClassification`&`$Top=1000"
        do{
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            foreach ($Message in $JSONOutput.Value) {
                Write-Output $Message
            }           
            $RequestURL = $JSONOutput.'@odata.nextLink'
        }while(![String]::IsNullOrEmpty($RequestURL))       

    }
}

function Get-FocusedInboxItems{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "('$MailboxName')/MailFolders/Inbox/messages/?`$select=ReceivedDateTime,Sender,Subject,IsRead,InferenceClassification`&`$Top=1000`&`$filter=InferenceClassification eq 'Focused'"
        do{
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            foreach ($Message in $JSONOutput.Value) {
                Write-Output $Message
            }           
            $RequestURL = $JSONOutput.'@odata.nextLink'
        }while(![String]::IsNullOrEmpty($RequestURL))       

    }
}

function Get-CalendarItems{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }   
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "('$MailboxName')/events/?`$select=Start,End,Subject,Organizer`&`$Top=1000"
        do{
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            foreach ($Message in $JSONOutput.Value) {
                Write-Output $Message
            }           
            $RequestURL = $JSONOutput.'@odata.nextLink'
        }while(![String]::IsNullOrEmpty($RequestURL))       

    }
}

function Get-FolderItems{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [string]$FolderPath,
        [Parameter(Position=3, Mandatory=$false)] [PSCustomObject]$Folder,
        [Parameter(Position=4, Mandatory=$false)] [switch]$ReturnSize,
        [Parameter(Position=5, Mandatory=$false)] [string]$SelectProperties,
        [Parameter(Position=6, Mandatory=$false)] [string]$Filter
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        if($FolderPath -ne $null)
        {
            $Folder = Get-FolderFromPath -FolderPath $FolderPath -AccessToken $AccessToken -MailboxName $MailboxName         
        }
        if(![String]::IsNullorEmpty($Filter)){
            $Filter = "`&`$filter=" + $Filter
        }        
        if([String]::IsNullorEmpty($SelectProperties)){
            $SelectProperties = "`$select=ReceivedDateTime,Sender,Subject,IsRead"
        }
        else{
            $SelectProperties = "`$select=" + $SelectProperties
        }
        if($Folder -ne $null)
        {
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $Folder.FolderRestURI + "/messages/?" +  $SelectProperties + "`&`$Top=1000" + $Filter
            write-host $RequestURL
            if($ReturnSize.IsPresent){
                $RequestURL =  $Folder.FolderRestURI + "/messages/?`$select=ReceivedDateTime,Sender,Subject,IsRead`&`$Top=1000`&`$expand=SingleValueExtendedProperties(`$filter=PropertyId%20eq%20'Integer%200x0E08')" + $Filter
            }
           
            do{
                $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
                foreach ($Message in $JSONOutput.Value) {
                    Add-Member -InputObject $Message -NotePropertyName ItemRESTURI -NotePropertyValue ($Folder.FolderRestURI + "/messages('" + $Message.Id + "')")
                    Write-Output $Message
                }           
                $RequestURL = $JSONOutput.'@odata.nextLink'
            }while(![String]::IsNullOrEmpty($RequestURL))     
       } 
   

    }
}

function Move-Message{
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$true)] [string]$ItemURI,
        [Parameter(Position=2, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=3, Mandatory=$false)] [string]$TargetFolderPath
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }    
        if($TargetFolderPath -ne $null)
        {
            $Folder = Get-FolderFromPath -FolderPath $TargetFolderPath -AccessToken $AccessToken -MailboxName $MailboxName         
        }
        if($Folder -ne $null){
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $ItemURI + "/move"
            $MoveItemPost = "{`"DestinationId`": `"" + $Folder.Id + "`"}"
            write-host $MoveItemPost
            return Invoke-RestPOST -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $MoveItemPost
        } 
    }
}
function Update-Message{
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$true)] [string]$ItemURI,
        [Parameter(Position=2, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=3, Mandatory=$false)] [String]$Subject,
        [Parameter(Position=4, Mandatory=$false)] [String]$Body,
        [Parameter(Position=5, Mandatory=$false)] [psobject]$Attachments,
        [Parameter(Position=6, Mandatory=$false)] [psobject]$ToRecipients,
        [Parameter(Position=7, Mandatory=$false)] [psobject]$StandardPropList,
        [Parameter(Position=8, Mandatory=$false)] [psobject]$ExPropList

    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }    
        $HttpClient =  Get-HTTPClient($MailboxName)
        $RequestURL =  $ItemURI
        $UpdateItemPatch =   Get-MessageJSONFormat -Subject $Subject -Body $Body -Attachments $Attachments -ExPropList $ExPropList -StandardPropList $StandardPropList  
        Write-host $UpdateItemPatch
        return Invoke-RestPatch -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $UpdateItemPatch
    }
}


function Get-Attachments{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$true)] [string]$ItemURI,
        [Parameter(Position=2, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=3, Mandatory=$false)] [switch]$MetaData,
        [Parameter(Position=4, Mandatory=$false)] [string]$SelectProperties
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }    
        if([String]::IsNullorEmpty($SelectProperties)){
            $SelectProperties = "`$select=Name,ContentType,Size,isInline,ContentType"
        }
        else{
            $SelectProperties = "`$select=" + $SelectProperties
        }
        $HttpClient =  Get-HTTPClient($MailboxName)
        $RequestURL =  $ItemURI + "/Attachments?" +  $SelectProperties   
        do{
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            foreach ($Message in $JSONOutput.Value) {
                  Add-Member -InputObject $Message -NotePropertyName AttachmentRESTURI -NotePropertyValue ($ItemURI + "/Attachments('" + $Message.Id + "')")
                  Write-Output $Message
             }           
             $RequestURL = $JSONOutput.'@odata.nextLink'
         }while(![String]::IsNullOrEmpty($RequestURL))     
   

    }
}

function Invoke-DownloadAttachment{
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$true)] [string]$AttachmentURI,
        [Parameter(Position=2, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        } 
        $HttpClient =  Get-HTTPClient($MailboxName)
        $AttachmentURI = $AttachmentURI + "?`$expand"
        $AttachmentObj = Invoke-RestGet -RequestURL $AttachmentURI -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName   
        return $AttachmentObj
    }
} 



function New-Folder{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$true)] [string]$ParentFolderPath,
        [Parameter(Position=3, Mandatory=$true)] [string]$DisplayName

    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $ParentFolder = Get-FolderFromPath -FolderPath $ParentFolderPath -AccessToken $AccessToken -MailboxName $MailboxName            
        if($ParentFolder  -ne $null)
        {
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $ParentFolder.FolderRestURI + "/childfolders"
            $NewFolderPost = "{`"DisplayName`": `"" + $DisplayName + "`"}"
            write-host $NewFolderPost
            return Invoke-RestPOST -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $NewFolderPost

       } 
   

    }
}
function New-ContactFolder{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=3, Mandatory=$true)] [string]$DisplayName

    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "('$MailboxName')/ContactFolders"
        $NewFolderPost = "{`"DisplayName`": `"" + $DisplayName + "`"}"
        write-host $NewFolderPost
        return Invoke-RestPOST -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $NewFolderPost
   

    }
}
function New-CalendarFolder{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=3, Mandatory=$true)] [string]$DisplayName

    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "('$MailboxName')/calendars"
        $NewFolderPost = "{`"Name`": `"" + $DisplayName + "`"}"
        write-host $NewFolderPost
        return Invoke-RestPOST -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $NewFolderPost
   

    }
}

function Rename-Folder{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$true)] [string]$FolderPath,
        [Parameter(Position=3, Mandatory=$true)] [string]$NewDisplayName

    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $Folder = Get-FolderFromPath -FolderPath $FolderPath -AccessToken $AccessToken -MailboxName $MailboxName            
        if($Folder  -ne $null)
        {
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $Folder.FolderRestURI
            $RenameFolderPost = "{`"DisplayName`": `"" + $NewDisplayName + "`"}"
            return Invoke-RestPatch -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $RenameFolderPost

       } 
   

    }
}
function Update-FolderClass{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$true)] [string]$FolderPath,
        [Parameter(Position=3, Mandatory=$true)] [string]$FolderClass

    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $Folder = Get-FolderFromPath -FolderPath $FolderPath -AccessToken $AccessToken -MailboxName $MailboxName            
        if($Folder  -ne $null)
        {
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $Folder.FolderRestURI
            $UpdateFolderPost = "{`"SingleValueExtendedProperties`": [{`"PropertyId`":`"String 0x3613`",`"Value`":`"" + $FolderClass + "`"}]}"
            return Invoke-RestPatch -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $UpdateFolderPost

       } 
   

    }
}
function Update-Folder{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$true)] [string]$FolderPath,
        [Parameter(Position=3, Mandatory=$true)] [string]$FolderPost

    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $Folder = Get-FolderFromPath -FolderPath $FolderPath -AccessToken $AccessToken -MailboxName $MailboxName            
        if($Folder  -ne $null)
        {
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $Folder.FolderRestURI
            $FolderPostValue = $FolderPost
            return Invoke-RestPatch -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $FolderPostValue

       } 
   

    }
}

function GetFolderRetentionTags(){
        #PR_POLICY_TAG 0x3019
    $PR_POLICY_TAG = Get-TaggedProperty -DataType "Binary" -Id "0x3019"  
    #PR_RETENTION_FLAGS 0x301D
    $PR_RETENTION_FLAGS =  Get-TaggedProperty -DataType "Integer" -Id "0x301D" 
    #PR_RETENTION_PERIOD 0x301A
    $PR_RETENTION_PERIOD = Get-TaggedProperty -DataType "Integer" -Id "0x301A"    
}
function Set-FolderRetentionTag {
        param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$true)] [string]$FolderPath,
          [Parameter(Position=3, Mandatory=$true)] [String]$PolicyTagValue,
        [Parameter(Position=4, Mandatory=$true)] [Int32]$RetentionFlagsValue,        
        [Parameter(Position=5, Mandatory=$true)] [Int32]$RetentionPeriodValue
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $Folder = Get-FolderFromPath -FolderPath $FolderPath -AccessToken $AccessToken -MailboxName $MailboxName            
        if($Folder  -ne $null)
        {    
 
            $retentionTagGUID = "{$($PolicyTagValue)}"
            $policyTagGUID = new-Object Guid($retentionTagGUID) 
            $PolicyTagBase64 = [System.Convert]::ToBase64String($PolicyTagGUID.ToByteArray()) 
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $Folder.FolderRestURI
            $FolderPostValue = "{`"SingleValueExtendedProperties`": [`r`n"
            $FolderPostValue += "`t{`"PropertyId`":`"Binary 0x3019`",`"Value`":`"" + $PolicyTagBase64 + "`"},`r`n"
            $FolderPostValue += "`t{`"PropertyId`":`"Integer 0x301D`",`"Value`":`"" + $RetentionFlagsValue + "`"},`r`n"
            $FolderPostValue += "`t{`"PropertyId`":`"Integer 0x301A`",`"Value`":`"" + $RetentionPeriodValue+ "`"}`r`n"
            $FolderPostValue += "]}"
            Write-Host $FolderPostValue
            return Invoke-RestPatch -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $FolderPostValue
       } 
    }
    
}

function Invoke-DeleteItem{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$true)] [string]$ItemURI
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $confirmation = Read-Host "Are you Sure You Want To proceed with deleting the Item"
        if ($confirmation -eq 'y') {
             $HttpClient =  Get-HTTPClient($MailboxName)
             $RequestURL =  $ItemURI
             return Invoke-RestDELETE -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        }
        else
        {
             Write-Host "skipped deletion"                
        }
   

    }
}

function Invoke-DeleteFolder{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$true)] [string]$FolderPath
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
        $Folder = Get-FolderFromPath -FolderPath $FolderPath -AccessToken $AccessToken -MailboxName $MailboxName            
        if($Folder  -ne $null)
        {
            $confirmation = Read-Host "Are you Sure You Want To proceed with deleting Folder"
            if ($confirmation -eq 'y') {
                $HttpClient =  Get-HTTPClient($MailboxName)
                $RequestURL =  $Folder.FolderRestURI
                return Invoke-RestDELETE -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            }
            else
            {
                Write-Host "skipped deletion"                
            }
       } 
   

    }
}

function Get-AllMailboxItems{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/MailFolders/AllItems/messages/?`$select=ReceivedDateTime,Sender,Subject,IsRead,ParentFolderId`&`$Top=1000"
            do{
                $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
                foreach ($Message in $JSONOutput.Value) {
                    Write-Output $Message
                }           
                $RequestURL = $JSONOutput.'@odata.nextLink'
            }while(![String]::IsNullOrEmpty($RequestURL))     
   

    }
}

function GetExtendedPropList{
        param( 
        [Parameter(Position=1, Mandatory=$false)] [PSCustomObject]$PropertyList
    )
    Begin{
        $rtString = "";
        foreach($Prop in $PropertyList){
            if($Prop.PropertyType -eq "Tagged"){
                if($rtString -eq ""){
                    $rtString = "(PropertyId%20eq%20'" + $Prop.DataType + "%20" + $Prop.Id + "')"                 
                }
                else{
                    $rtString += " or (PropertyId%20eq%20'" + $Prop.DataType + "%20" + $Prop.Id + "')"       
                }
            }
            else{
                if($Prop.Type -eq "String"){
                    if($rtString -eq ""){
                        $rtString = "(PropertyId%20eq%20'" + $Prop.DataType + "%20{" + $Prop.Guid + "}%20Name%20"+ $Prop.Id + "')"                 
                    }
                    else{
                        $rtString += " or (PropertyId%20eq%20'" + $Prop.DataType + "%20{" + $Prop.Guid + "}%20Name%20"+ $Prop.Id + "')"       
                    }   
                }
                else{
                    if($rtString -eq ""){
                        $rtString = "(PropertyId%20eq%20'" + $Prop.DataType + "%20{" + $Prop.Guid + "}%20Id%20"+ $Prop.Id + "')"                 
                    }
                    else{
                        $rtString += " or (PropertyId%20eq%20'" + $Prop.DataType + "%20{" + $Prop.Guid + "}%20Id%20"+ $Prop.Id + "')"       
                    }                    
                }
            }
            
        }
        return $rtString
        
    }
}

function Get-TaggedProperty{
     param( 
        [Parameter(Position=0, Mandatory=$true)] [String]$DataType,
        [Parameter(Position=1, Mandatory=$true)] [String]$Id,
        [Parameter(Position=2, Mandatory=$false)] [String]$Value
    )
    Begin{
        $Property = "" | Select Id,DataType,PropertyType,Value
        $Property.Id = $Id
        $Property.DataType = $DataType   
        $Property.PropertyType = "Tagged"
        if(![String]::IsNullOrEmpty($Value)){
             $Property.Value = $Value
        }       
        return ,$Property
    }
}

function Get-NamedProperty{
     param( 
        [Parameter(Position=0, Mandatory=$true)] [String]$DataType,
        [Parameter(Position=1, Mandatory=$true)] [String]$Id,
        [Parameter(Position=1, Mandatory=$true)] [String]$Guid,
        [Parameter(Position=1, Mandatory=$true)] [String]$Type,
        [Parameter(Position=2, Mandatory=$false)] [String]$Value
    )
    Begin{
        $Property = "" | Select Id,DataType,PropertyType,Type,Guid,Value
        $Property.Id = $Id
        $Property.DataType = $DataType   
        $Property.PropertyType = "Named"
        $Property.Guid = $Guid
        if($Type = "String"){
            $Property.Type = "String"
        }
        else{
             $Property.Type = "Id"
        }
         if(![String]::IsNullOrEmpty($Value)){
             $Property.Value = $Value
        }     
        return ,$Property
    }
}

function Get-FolderClass()
{
    $FolderClass = "" | Select Id,DataType,PropertyType
    $FolderClass.Id = "0x3613"
    $FolderClass.DataType = "String"  
    $FolderClass.PropertyType = "Tagged" 
    return ,$FolderClass
}
function Get-FolderPath()
{
    $FolderPath = "" | Select Id,DataType,PropertyType
    $FolderPath.Id = "0x66B5"
    $FolderPath.DataType = "String"
    $FolderPath.PropertyType = "Tagged"   
    return ,$FolderPath
}

function Get-AllMailFolders{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [PSCustomObject]$PropList
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/MailFolders/msgfolderroot/childfolders/?`$Top=1000"
            if($PropList -ne $null){
               $Props = GetExtendedPropList -PropertyList $PropList
               $RequestURL += "`&`$expand=SingleValueExtendedProperties(`$filter=" + $Props + ")"
               Write-Host $RequestURL
            }
            do{
                $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
                foreach ($Folder in $JSONOutput.Value) {
                    $Folder | Add-Member -NotePropertyName FolderPath -NotePropertyValue ("\\" + $Folder.DisplayName)
                    $folderId = $Folder.Id.ToString()
                    Add-Member -InputObject $Folder -NotePropertyName FolderRestURI -NotePropertyValue ($EndPoint + "('$MailboxName')/MailFolders('$folderId')")
                    Write-Output $Folder
                    if($Folder.ChildFolderCount -gt 0)
                    {
                        if($PropList -ne $null){
                            Get-AllChildFolders -Folder $Folder -AccessToken $AccessToken -PropList $PropList     
                        }
                        else{                            
                             Get-AllChildFolders -Folder $Folder -AccessToken $AccessToken     
                        }                                           
                    }
                }           
                $RequestURL = $JSONOutput.'@odata.nextLink'
            }while(![String]::IsNullOrEmpty($RequestURL))     
   

    }
}
function Get-AllChildFolders{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [PSCustomObject]$Folder,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [PSCustomObject]$PropList
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =   $Folder.FolderRestURI + "/childfolders/?`$Top=1000"
            if($PropList -ne $null){
               $Props = GetExtendedPropList -PropertyList $PropList
               $RequestURL += "`&`$expand=SingleValueExtendedProperties(`$filter=" + $Props + ")"
            }
            do{
                $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
                foreach ($ChildFolder in $JSONOutput.Value) {
                    $ChildFolder | Add-Member -NotePropertyName FolderPath -NotePropertyValue ($Folder.FolderPath + "\" + $ChildFolder.DisplayName)
                    $folderId = $ChildFolder.Id.ToString()
                    Add-Member -InputObject $ChildFolder -NotePropertyName FolderRestURI -NotePropertyValue ($EndPoint + "('$MailboxName')/MailFolders('$folderId')")
                    Write-Output $ChildFolder
                    if($ChildFolder.ChildFolderCount -gt 0)
                    {
                        if($PropList -ne $null){
                            Get-AllChildFolders -Folder $ChildFolder -AccessToken $AccessToken -PropList $PropList     
                        }
                        else{                            
                            Get-AllChildFolders -Folder $ChildFolder -AccessToken $AccessToken     
                        }                
                    }
                }           
                $RequestURL = $JSONOutput.'@odata.nextLink'
            }while(![String]::IsNullOrEmpty($RequestURL))     
   

    }
}

function Get-AllCalendarFolders{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [switch]$FolderClass
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            if($FolderClass.IsPresent){
                $RequestURL =  $EndPoint + "('$MailboxName')/Calendars/?`$Top=1000`&`$expand=SingleValueExtendedProperties(`$filter=PropertyId%20eq%20'String%200x66B5')"
                if($AccessToken.resource -eq "https://graph.microsoft.com"){
                    $RequestURL =  $EndPoint + "('$MailboxName')/Calendars/?`$Top=1000`&`$expand=SingleValueExtendedProperties(`$filter=Id%20eq%20'String%200x66B5')"
                }
                
            }
            else{
                 $RequestURL =  $EndPoint + "('$MailboxName')/Calendars/?`$Top=1000"
            }
            do{
                $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
                foreach ($Folder in $JSONOutput.Value) {
                    Write-Output $Folder
                }           
                $RequestURL = $JSONOutput.'@odata.nextLink'
            }while(![String]::IsNullOrEmpty($RequestURL))     
   

    }
}
function Get-AllContactFolders{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/contactfolders/?`$Top=1000"
            Write-Host  $RequestURL
            do{
                $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
                foreach ($Folder in $JSONOutput.Value) {
                    Write-Output $Folder
                }           
                $RequestURL = $JSONOutput.'@odata.nextLink'
            }while(![String]::IsNullOrEmpty($RequestURL))     
   

    }
}
function Get-AllTaskfolders{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/taskfolders/?`$Top=1000"
            do{
                $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
                foreach ($Folder in $JSONOutput.Value) {
                    Write-Output $Folder
                }           
                $RequestURL = $JSONOutput.'@odata.nextLink'
            }while(![String]::IsNullOrEmpty($RequestURL))     
   

    }
}



function Get-ArchiveFolder{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL = $EndPoint + "('$MailboxName')/MailboxSettings/ArchiveFolder"
        $JsonObject =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        $folderId = $JsonObject.value.ToString()
        $HttpClient =  Get-HTTPClient($MailboxName)
        $RequestURL = $EndPoint + "('$MailboxName')/MailFolders('$folderId')"
        return  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
    }
}


function Get-MailboxSettingsReport{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [psobject]$Mailboxes,
        [Parameter(Position=1, Mandatory=$true)] [string]$CertFileName,
        [Parameter(Mandatory=$True)][Security.SecureString]$password   
    )
    Begin{
        $rptCollection = @()
        $AccessToken = Get-AppOnlyToken -CertFileName $CertFileName -password $password 
        $HttpClient =  Get-HTTPClient($Mailboxes[0])
        foreach ($MailboxName in $Mailboxes) {
            $rptObj = "" | Select MailboxName,Language,Locale,TimeZone,AutomaticReplyStatus
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/MailboxSettings"
            $Results = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            $rptObj.MailboxName = $MailboxName
            $rptObj.Language = $Results.Language.DisplayName
            $rptObj.Locale = $Results.Language.Locale
            $rptObj.TimeZone = $Results.TimeZone
            $rptObj.AutomaticReplyStatus = $Results.AutomaticRepliesSetting.Status
            $rptCollection += $rptObj
        }
        Write-Output  $rptCollection
        
    }
}

function Get-EndPoint{
        param(
        [Parameter(Position=0, Mandatory=$true)] [psObject]$AccessToken,
        [Parameter(Position=1, Mandatory=$true)] [psObject]$Segment
    )
    Begin{
        $EndPoint = "https://outlook.office.com/api/v2.0"
        switch($AccessToken.resource){
            "https://outlook.office.com" { if($AccessToken.Beta){
                 $EndPoint = "https://outlook.office.com/api/beta/" + $Segment
             }else{
                 $EndPoint = "https://outlook.office.com/api/v2.0/" + $Segment
             } }     
            "https://graph.microsoft.com" {  if($AccessToken.Beta){
                    $EndPoint = "https://graph.microsoft.com/beta/" + $Segment
                }else{
                    $EndPoint = "https://graph.microsoft.com/v1.0/" + $Segment
                }
            }     
        }
        return , $EndPoint
        
    }
}

function  Get-People {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken   
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $RequestURL =  "https://outlook.office.com/api/beta/me/people/?`$top=1000&`$Select=DisplayName"
        $JsonObject =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        Write-Output $JsonObject 
    }
}

function  Get-UserPhotoMetaData {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken   
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "/" + $MailboxName + "/photo"
        $JsonObject =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        Write-Output $JsonObject 
    }
}

function  Get-UserPhoto {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
 
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL = $EndPoint + "/" + $MailboxName + "/photo/`$value"
        $Result =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -NoJSON
        Write-Output $Result.ReadAsByteArrayAsync().Result  
    }
}
function  Get-MailboxUser {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken   
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "/" + $MailboxName 
        $JsonObject =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        Write-Output $JsonObject 
    }
}

function  Get-CalendarGroups {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken   
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "/" + $MailboxName + "/CalendarGroups"
        $JsonObject =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        Write-Output $JsonObject 
    }
}

function  Invoke-EnumCalendarGroups {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken   
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "/" + $MailboxName + "/CalendarGroups"
        $JsonObject =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        foreach($Group in $JsonObject.Value)
        {
            Write-Host ("GroupName : " + $Group.Name) 
            $GroupId = $Group.Id.ToString()           
            $RequestURL =  $EndPoint + "/" + $MailboxName + "/CalendarGroups('$GroupId')/Calendars"
            $JsonObjectSub =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            foreach ($Calendar in $JsonObjectSub.Value) {
                Write-Host $Calendar.Name
            }
            $RequestURL =  $EndPoint + "/" + $MailboxName + "/CalendarGroups('$GroupId')/MailFolders"
            $JsonObjectSub =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            foreach ($Calendar in $JsonObjectSub.Value) {
                Write-Host $Calendar.Name
            }
         
        }  
        
        
    }
}

function Get-ObjectProp{
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$Name,  
        [Parameter(Position=1, Mandatory=$false)] [psObject]$PropList,
        [Parameter(Position=2, Mandatory=$false)] [switch]$Array              
    )
    Begin{
        $ObjectProp = "" | Select PropertyName,PropertyList,PropertyType,isArray
        $ObjectProp.PropertyType = "Object"
        $ObjectProp.isArray = $false
        if($Array.IsPresent){  $ObjectProp.isArray = $true }
        $ObjectProp.PropertyName = $Name
        if($PropList -eq $null){
            $ObjectProp.PropertyList = @()
        }
        else{
            $ObjectProp.PropertyList = $PropList
        }
        return ,$ObjectProp
        
    }
}

function Get-ObjectCollectionProp{
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$Name,  
        [Parameter(Position=1, Mandatory=$false)] [psObject]$PropList,
        [Parameter(Position=2, Mandatory=$false)] [switch]$Array              
    )
    Begin{
        $CollectionProp = "" | Select PropertyName,PropertyList,PropertyType,isArray
        $CollectionProp.PropertyType = "ObjectCollection"
        $CollectionProp.isArray = $false
        if($Array.IsPresent){  $CollectionProp.isArray = $true }
        $CollectionProp.PropertyName = $Name
        if($PropList -eq $null){
            $CollectionProp.PropertyList = @()
        }
        else{
            $CollectionProp.PropertyList = $PropList
        }
        return ,$CollectionProp
        
    }
}

function Get-ItemProp{
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$Name,  
        [Parameter(Position=1, Mandatory=$true)] [string]$Value,  
        [Parameter(Position=2, Mandatory=$false)] [switch]$NoQuotes          
    )
    Begin{
        $ItemProp = "" | Select Name,Value,PropertyType,QuoteValue
        $ItemProp.PropertyType = "Single"
        $ItemProp.Name = $Name
        $ItemProp.Value = $Value  
        if($NoQuotes.IsPresent){
            $ItemProp.QuoteValue = $false  
        }
        else{
            $ItemProp.QuoteValue = $true  
        }
        return ,$ItemProp
        
    }
}

function Get-ModernGroups {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken   
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }        
        $HttpClient =  Get-HTTPClient($MailboxName)
        $RequestURL =  Get-EndPoint -AccessToken $AccessToken -Segment "/groups"
        $JsonObject =  Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
        return $JsonObject       
        
    }
}

function Get-MailAppProps(){
        #Holder for Mail Apps
        $cepPropdef = Get-NamedProperty -DataType "String" -Guid "00020329-0000-0000-C000-000000000046" -Id "cecp-propertyNames" -Value ($Guid + ";") -Type "String"
        $cepPropValue = Get-NamedProperty -DataType "String" -Guid "00020329-0000-0000-C000-000000000046" -Id "cecp-" + $Guid -Value $value -Type "String"
}

function New-EmailAddress {
    param(
        [Parameter(Position=0, Mandatory=$false)] [string]$Name,
        [Parameter(Position=1, Mandatory=$true)] [string]$Address
    )
    Begin{
        $EmailAddress = "" | Select Name,Address
        if([String]::IsNullOrEmpty($Name)){
            $EmailAddress.Name = $Address
        }
        else{
            $EmailAddress.Name = $Name
        }        
        $EmailAddress.Address = $Address
        return, $EmailAddress
    }
}

#region Sending_Email
function  New-SentEmailMessage {
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [string]$FolderPath,
        [Parameter(Position=3, Mandatory=$false)] [PSCustomObject]$Folder,
        [Parameter(Position=4, Mandatory=$true)] [String]$Subject,
        [Parameter(Position=5, Mandatory=$false)] [String]$Body,
        [Parameter(Position=7, Mandatory=$true)] [psobject]$SenderEmailAddress,
        [Parameter(Position=8, Mandatory=$false)] [psobject]$Attachments,
        [Parameter(Position=9, Mandatory=$false)] [psobject]$ToRecipients,
        [Parameter(Position=10, Mandatory=$true)] [DateTime]$SentDate,
        [Parameter(Position=11, Mandatory=$false)] [psobject]$ExPropList,
        [Parameter(Position=12, Mandatory=$false)] [string]$ItemClass
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        } 
        $SentFlag = Get-TaggedProperty -DataType "Integer" -Id "0x0E07"  -Value "1"
        $SentTime = Get-TaggedProperty -DataType "SystemTime" -Id "0x0039"  -Value $SentDate.ToString("yyyy-MM-ddTHH:mm:ss.ffffzzz")
        $RcvdTime = Get-TaggedProperty -DataType "SystemTime" -Id "0x0E06"  -Value $SentDate.ToString("yyyy-MM-ddTHH:mm:ss.ffffzzz")
        if($ExPropList -eq $null){
            $ExPropList = @()
        }
        if(![String]::IsNullOrEmpty($ItemClass)){
            $ItemClassProp = Get-TaggedProperty -DataType "String" -Id "0x001A"  -Value $ItemClass
            $ExPropList += $ItemClassProp
        }        
        $ExPropList += $SentFlag
        $ExPropList += $SentTime
        $ExPropList += $RcvdTime
        $NewMessage = Get-MessageJSONFormat -Subject $Subject -Body $Body -SenderEmailAddress $SenderEmailAddress -Attachments $Attachments -ToRecipients $ToRecipients -SentDate $SentDate -ExPropList $ExPropList
        if($FolderPath -ne $null)
        {
            $Folder = Get-FolderFromPath -FolderPath $FolderPath -AccessToken $AccessToken -MailboxName $MailboxName    

        }        
        if($Folder -ne $null)
        {
            $RequestURL =  $Folder.FolderRestURI + "/messages"
            $HttpClient =  Get-HTTPClient($MailboxName)
            Invoke-RestPOST -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $NewMessage
        }

    }
}

function CreateFlatList{
    param(
        [Parameter(Position=0, Mandatory=$true)] [psobject]$EmailAddress
    )
    Begin{
         
         $FlatListEntry = new-object System.IO.MemoryStream
         $EntryOneOffid = "00000000812B1FA4BEA310199D6E00DD010F540200000190" + [BitConverter]::ToString([System.Text.UnicodeEncoding]::Unicode.GetBytes(($EmailAddress.Name + "`0"))).Replace("-","") + [BitConverter]::ToString([System.Text.UnicodeEncoding]::Unicode.GetBytes(("SMTP" + "`0"))).Replace("-","") + [BitConverter]::ToString([System.Text.UnicodeEncoding]::Unicode.GetBytes(($EmailAddress.Address + "`0"))).Replace("-","")
         $FlatListEntryBytes = HexStringToByteArray($EntryOneOffid)
         $FlatListEntry.Write([BitConverter]::GetBytes($FlatListEntryBytes.Length), 0, 4);
         $FlatListEntry.Write($FlatListEntryBytes, 0, $FlatListEntryBytes.Length);
         $InnerLength += $FlatListEntryBytes.Length
         $Modulsval = $FlatListEntryBytes.Length % 4;
         $PadingValue = 0;
         if ($Modulsval -ne 0)
         {
              $PadingValue = 4 - $Modulsval;
              for ($AddPading = 0; $AddPading -lt $PadingValue; $AddPading++)
              {
                   [Byte]$NullValue = 00;
                   $FlatlistStream.Write($NullValue, 0, 1);
              }
         }         
         $FlatListEntry.Position = 0
         $FlatListEntryBytes = $FlatListEntry.ToArray()
         $FlatList = new-object System.IO.MemoryStream
         $FlatList.Write([BitConverter]::GetBytes(1), 0, 4);  
         $FlatList.Write([BitConverter]::GetBytes($FlatListEntryBytes.Length), 0, 4); 
         $FlatList.Write($FlatListEntryBytes, 0,  $FlatListEntryBytes.Length);
         $FlatList.Position = 0
         return ,$FlatList.ToArray()   
     }
}


function Send-MessageREST{
        param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [string]$FolderPath,
        [Parameter(Position=3, Mandatory=$false)] [PSCustomObject]$Folder,
        [Parameter(Position=4, Mandatory=$true)] [String]$Subject,
        [Parameter(Position=5, Mandatory=$false)] [String]$Body,
        [Parameter(Position=7, Mandatory=$false)] [psobject]$SenderEmailAddress,
        [Parameter(Position=8, Mandatory=$false)] [psobject]$Attachments,
        [Parameter(Position=9, Mandatory=$false)] [psobject]$ReferanceAttachments,
        [Parameter(Position=10, Mandatory=$false)] [psobject]$ToRecipients,
        [Parameter(Position=11, Mandatory=$false)] [psobject]$CCRecipients,
        [Parameter(Position=12, Mandatory=$false)] [psobject]$BCCRecipients,
        [Parameter(Position=13, Mandatory=$false)] [psobject]$ExPropList,
        [Parameter(Position=14, Mandatory=$false)] [psobject]$StandardPropList,
        [Parameter(Position=15, Mandatory=$false)] [string]$ItemClass,
        [Parameter(Position=16, Mandatory=$false)] [switch]$SaveToSentItems,
        [Parameter(Position=17, Mandatory=$false)] [switch]$ShowRequest,
        [Parameter(Position=18, Mandatory=$false)] [switch]$RequestReadRecipient,
        [Parameter(Position=19, Mandatory=$false)] [switch]$RequestDeliveryRecipient,
        [Parameter(Position=20, Mandatory=$false)] [psobject]$ReplyTo
    )
    Begin{
        
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        } 
        if(![String]::IsNullOrEmpty($ItemClass)){
            $ItemClassProp = Get-TaggedProperty -DataType "String" -Id "0x001A"  -Value $ItemClass
            if($ExPropList -eq $null){
               $ExPropList = @()
            }
            $ExPropList += $ItemClassProp
        }     
        $SaveToSentFolder = "false"
        if($SaveToSentItems.IsPresent){
            $SaveToSentFolder = "true"
        }
        $NewMessage = Get-MessageJSONFormat -Subject $Subject -Body $Body -SenderEmailAddress $SenderEmailAddress -Attachments $Attachments -ReferanceAttachments $ReferanceAttachments -ToRecipients $ToRecipients -SentDate $SentDate -ExPropList $ExPropList -CcRecipients $CCRecipients -bccRecipients $BCCRecipients -StandardPropList  $StandardPropList -SaveToSentItems $SaveToSentFolder -SendMail -ReplyTo $ReplyTo -RequestReadRecipient $RequestReadRecipient.IsPresent -RequestDeliveryRecipient $RequestDeliveryRecipient.IsPresent
        if($ShowRequest.IsPresent){
            write-host $NewMessage
        }       
        $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
        $RequestURL =  $EndPoint + "/" + $MailboxName + "/sendmail"
        $HttpClient =  Get-HTTPClient($MailboxName)
        return Invoke-RestPOST -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -Content $NewMessage

    }
}

function Get-MessageJSONFormat {
    param(
        [Parameter(Position=1, Mandatory=$false)] [String]$Subject,
        [Parameter(Position=2, Mandatory=$false)] [String]$Body,
        [Parameter(Position=3, Mandatory=$false)] [psobject]$SenderEmailAddress,
        [Parameter(Position=5, Mandatory=$false)] [psobject]$Attachments,
        [Parameter(Position=5, Mandatory=$false)] [psobject]$ReferanceAttachments,
        [Parameter(Position=6, Mandatory=$false)] [psobject]$ToRecipients,
        [Parameter(Position=7, Mandatory=$false)] [psobject]$CcRecipients,
        [Parameter(Position=7, Mandatory=$false)] [psobject]$bccRecipients,
        [Parameter(Position=8, Mandatory=$false)] [psobject]$SentDate,
        [Parameter(Position=9, Mandatory=$false)] [psobject]$StandardPropList,
        [Parameter(Position=10, Mandatory=$false)] [psobject]$ExPropList,
        [Parameter(Position=11, Mandatory=$false)] [switch]$ShowRequest,
        [Parameter(Position=12, Mandatory=$false)] [String]$SaveToSentItems,
        [Parameter(Position=13, Mandatory=$false)] [switch]$SendMail,
        [Parameter(Position=14, Mandatory=$false)] [psobject]$ReplyTo,
        [Parameter(Position=17, Mandatory=$false)] [bool]$RequestReadRecipient,
        [Parameter(Position=18, Mandatory=$false)] [bool]$RequestDeliveryRecipient
    )
    Begin{
        $NewMessage = "{" + "`r`n"
        if($SendMail.IsPresent){
             $NewMessage += " `"Message`" : {" + "`r`n"
        }
        if(![String]::IsNullOrEmpty($Subject)){
            $NewMessage +=  "`"Subject`": `"" + $Subject + "`"" + "`r`n"
        }
        if($SenderEmailAddress -ne $null){
             if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  "`"Sender`":{" + "`r`n"
            $NewMessage +=  " `"EmailAddress`":{" + "`r`n"
            $NewMessage +=  " `"Name`":`"" + $SenderEmailAddress.Name + "`"," + "`r`n"
            $NewMessage +=  " `"Address`":`"" + $SenderEmailAddress.Address + "`"" + "`r`n"
            $NewMessage +=  "}}" + "`r`n"
        }
        if(![String]::IsNullOrEmpty($Body)){
             if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  "`"Body`": {"+ "`r`n"
            $NewMessage +=  "`"ContentType`": `"HTML`"," + "`r`n"
            $NewMessage +=  "`"Content`": `"" + $Body + "`"" + "`r`n"
            $NewMessage +=  "}" + "`r`n"
        }      
        
        $toRcpcnt = 0;
        if($ToRecipients -ne $null){
             if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  "`"ToRecipients`": [ " + "`r`n"
            foreach ($EmailAddress in $ToRecipients) {
                if($toRcpcnt -gt 0){
                    $NewMessage +=  " ,{ "+ "`r`n"   
                }
                else{
                    $NewMessage +=  " { "+ "`r`n"
                }           
                $NewMessage +=  " `"EmailAddress`":{" + "`r`n"
                $NewMessage +=  " `"Name`":`"" + $EmailAddress.Name + "`"," + "`r`n"
                $NewMessage +=  " `"Address`":`"" + $EmailAddress.Address + "`"" + "`r`n"
                $NewMessage +=  "}}" + "`r`n"
                $toRcpcnt++
            }
            $NewMessage +=  " ]" + "`r`n"  
        }
        $ccRcpcnt = 0
        if($CcRecipients -ne $null){
             if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  "`"CcRecipients`": [ " + "`r`n"
            foreach ($EmailAddress in $CcRecipients) {
                if($ccRcpcnt  -gt 0){
                    $NewMessage +=  " ,{ "+ "`r`n"   
                }
                else{
                    $NewMessage +=  " { "+ "`r`n"
                }           
                $NewMessage +=  " `"EmailAddress`":{" + "`r`n"
                $NewMessage +=  " `"Name`":`"" + $EmailAddress.Name + "`"," + "`r`n"
                $NewMessage +=  " `"Address`":`"" + $EmailAddress.Address + "`"" + "`r`n"
                $NewMessage +=  "}}" + "`r`n"
                $ccRcpcnt++
            }
            $NewMessage +=  " ]" + "`r`n"  
        }
        $bccRcpcnt = 0
        if($bccRecipients -ne $null){
             if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  "`"BccRecipients`": [ " + "`r`n"
            foreach ($EmailAddress in $bccRecipients) {
                if($bccRcpcnt -gt 0){
                    $NewMessage +=  " ,{ "+ "`r`n"   
                }
                else{
                    $NewMessage +=  " { "+ "`r`n"
                }           
                $NewMessage +=  " `"EmailAddress`":{" + "`r`n"
                $NewMessage +=  " `"Name`":`"" + $EmailAddress.Name + "`"," + "`r`n"
                $NewMessage +=  " `"Address`":`"" + $EmailAddress.Address + "`"" + "`r`n"
                $NewMessage +=  "}}" + "`r`n"
                $bccRcpcnt++
            }
            $NewMessage +=  " ]" + "`r`n"  
        }
        $ReplyTocnt = 0
        if($ReplyTo -ne $null){
            if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  "`"ReplyTo`": [ " + "`r`n"
            foreach ($EmailAddress in $ReplyTo) {
                if($ReplyTocnt -gt 0){
                    $NewMessage +=  " ,{ "+ "`r`n"   
                }
                else{
                    $NewMessage +=  " { "+ "`r`n"
                }           
                $NewMessage +=  " `"EmailAddress`":{" + "`r`n"
                $NewMessage +=  " `"Name`":`"" + $EmailAddress.Name + "`"," + "`r`n"
                $NewMessage +=  " `"Address`":`"" + $EmailAddress.Address + "`"" + "`r`n"
                $NewMessage +=  "}}" + "`r`n"
                $ReplyTocnt++
            }
            $NewMessage +=  " ]" + "`r`n"  
        }
        if($RequestDeliveryRecipient){
            $NewMessage +=  ",`"IsDeliveryReceiptRequested`": true`r`n"
        }
        if($RequestReadRecipient){
            $NewMessage +=  ",`"IsReadReceiptRequested`": true `r`n"
        }
        if($StandardPropList -ne $null){
            foreach ($StandardProp in $StandardPropList) {
                if($NewMessage.Length -gt 5){$NewMessage += ","}
                switch($StandardProp.PropertyType){
                    "Single" {
                        if($StandardProp.QuoteValue){
                           $NewMessage +=  "`"" + $StandardProp.Name + "`": `"" + $StandardProp.Value + "`"" + "`r`n" 
                        }
                        else{
                            $NewMessage +=  "`"" + $StandardProp.Name + "`": " + $StandardProp.Value +  "`r`n" 
                        }
                        
                   
                   }
                    "Object"  {
                              if($StandardProp.isArray){
                                    $NewMessage +=  "`"" + $StandardProp.PropertyName + "`": [ {"+ "`r`n"
                              }else{
                                    $NewMessage +=  "`"" + $StandardProp.PropertyName + "`": {"+ "`r`n"
                              }                              
                              $acCount = 0
                              foreach ($PropKeyValue in $StandardProp.PropertyList) {
                                    if($acCount -gt 0){
                                        $NewMessage += ","
                                    }
                                    $NewMessage +=  "`"" + $PropKeyValue.Name + "`": `"" + $PropKeyValue.Name + "`"" + "`r`n"
                                    $acCount++
                              }
                               if($StandardProp.isArray){
                                    $NewMessage +=  "}]" + "`r`n"
                               }else{
                                    $NewMessage +=  "}" + "`r`n"
                               }
                              
                    }
                    "ObjectCollection" {
                              if($StandardProp.isArray){
                                    $NewMessage +=  "`"" + $StandardProp.PropertyName + "`": ["+ "`r`n"
                              }else{
                                    $NewMessage +=  "`"" + $StandardProp.PropertyName + "`": {"+ "`r`n"
                              }     
                              foreach ($EnclosedStandardProp in $StandardProp.PropertyList) {
                                  $NewMessage +=  "`"" + $EnclosedStandardProp.PropertyName + "`": {"+ "`r`n"
                                   foreach ($PropKeyValue in $EnclosedStandardProp.PropertyList) {
                                       $NewMessage +=  "`"" + $PropKeyValue.Name + "`": `"" + $PropKeyValue.Name + "`"," + "`r`n"
                                  }
                                  $NewMessage +=  "}" + "`r`n"
                              }
                               if($StandardProp.isArray){
                                    $NewMessage +=  "]" + "`r`n"
                               }else{
                                    $NewMessage +=  "}" + "`r`n"
                               }
                    }
                   
                }
            }
        }                  
        $atcnt = 0
        $processAttachments = $false
        if($Attachments -ne $null){$processAttachments = $true}
        if($ReferanceAttachments -ne $null){$processAttachments = $true}
        if($processAttachments){
            if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  " `"Attachments`": [ " + "`r`n"
            if($Attachments -ne $null){
                foreach ($Attachment in $Attachments) {
                    $Item = Get-Item $Attachment
                    if($atcnt -gt 0)
                    {
                        $NewMessage +=  " ,{" + "`r`n"                       
                    }
                    else{
                        $NewMessage +=  " {" + "`r`n"
                    }               
                    $NewMessage +=  " `"@odata.type`": `"#Microsoft.OutlookServices.FileAttachment`"," + "`r`n"
                    $NewMessage +=  " `"Name`": `"" + $Item.Name + "`"," + "`r`n"
                    $NewMessage +=  " `"ContentBytes`": `" " + [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($Attachment)) + "`"" + "`r`n"
                    $NewMessage +=  " } " + "`r`n"
                    $atcnt++
                }
            }
            $atcnt = 0
            if($ReferanceAttachments -ne $null){
                foreach ($Attachment in $ReferanceAttachments) {
                    if($atcnt -gt 0)
                    {
                        $NewMessage +=  " ,{" + "`r`n"                       
                    }
                    else{
                        $NewMessage +=  " {" + "`r`n"
                    }               
                    $NewMessage +=  " `"@odata.type`": `"#Microsoft.OutlookServices.ReferenceAttachment`"," + "`r`n"
                    $NewMessage +=  " `"Name`": `"" + $Attachment.Name + "`"," + "`r`n"
                    $NewMessage +=  " `"SourceUrl`": `"" + $Attachment.SourceUrl + "`"," + "`r`n"
                    $NewMessage +=  " `"ProviderType`": `"" + $Attachment.ProviderType + "`"," + "`r`n"
                    $NewMessage +=  " `"Permission`": `"" + $Attachment.Permission + "`"," + "`r`n"
                    $NewMessage +=  " `"IsFolder`": `"" + $Attachment.IsFolder + "`"" + "`r`n"
                    $NewMessage +=  " } " + "`r`n"
                    $atcnt++
                }
            }
            $NewMessage +=  " ]" + "`r`n"
        }     
    
        if($ExPropList -ne $null){
             if($NewMessage.Length -gt 5){$NewMessage += ","}
            $NewMessage +=  "`"SingleValueExtendedProperties`": ["+ "`r`n"
            $propCount = 0
            foreach($Property in $ExPropList){
               if($propCount -eq 0){
                   $NewMessage +=  "{"+ "`r`n"
               }
               else{
                   $NewMessage +=  ",{"+ "`r`n"
               }
               if($Property.PropertyType -eq "Tagged"){
                     $NewMessage +=  "`"PropertyId`":`"" + $Property.DataType + " " + $Property.Id + "`", " + "`r`n"
               }
               else{
                   if($Property.Type -eq "String"){
                       $NewMessage +=  "`"PropertyId`":`"" + $Property.DataType + " " + $Property.Guid + " Name " + $Property.Id + "`", " + "`r`n"
                   }
                   else{
                       $NewMessage +=  "`"PropertyId`":`"" + $Property.DataType + " " + $Property.Guid + " Id " + $Property.Id + "`", " + "`r`n"
                   }
               }
               $NewMessage +=  "`"Value`":`"" + $Property.Value + "`""+ "`r`n"
               $NewMessage +=  " } " + "`r`n"
               $propCount++
            }
            $NewMessage +=  "]" + "`r`n"   
        } 
        if(![String]::IsNullOrEmpty($SaveToSentItems)){
            $NewMessage += "} ,`"SaveToSentItems`": `"" + $SaveToSentItems.ToLower() + "`""+ "`r`n"
        }   
        $NewMessage +=  "}"
        if($ShowRequest.IsPresent){
            Write-Host $NewMessage
        }
        return, $NewMessage
    }
}
#endregion
function HexStringToByteArray($HexString)
{
    $ByteArray =  New-Object Byte[] ($HexString.Length/2);
      for ($i = 0; $i -lt $HexString.Length; $i += 2)
    {
         $ByteArray[$i/2] = [Convert]::ToByte($HexString.Substring($i, 2), 16)
    } 
     Return @(,$ByteArray)

}
#region OneDrive
function Get-DefaultOneDrive{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/drive/root"
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            Add-Member -InputObject $JSONOutput -NotePropertyName DriveRESTURI -NotePropertyValue ( $EndPoint + "('$MailboxName')/drives('" + $JSONOutput.Id + "')" )
            return $JSONOutput            
            

    }
}
function Get-DefaultOneDriveRootItems{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/drive/root/children"
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName
            foreach ($Item in $JSONOutput.value) {
                Add-Member -InputObject $Item -NotePropertyName DriveRESTURI -NotePropertyValue (((Get-EndPoint -AccessToken $AccessToken -Segment "users") + "('$MailboxName')/drive") + "/items('" + $Item.Id + "')")
                write-output $Item
            }   
            
            

    }
}

function Get-OneDriveChildren{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [String]$DriveRESTURI,
        [Parameter(Position=3, Mandatory=$false)] [String]$FolderPath
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
          if([String]::IsNullOrEmpty($DriveRESTURI)){
               $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
               $RequestURL =  $EndPoint + "('$MailboxName')/drive/root:" + $FolderPath + ":/children"
           }
           else{
                 $RequestURL =  $DriveRESTURI + "/children"
           }            
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName   
            foreach ($Item in $JSONOutput.value) {
                Add-Member -InputObject $Item -NotePropertyName DriveRESTURI -NotePropertyValue (((Get-EndPoint -AccessToken $AccessToken -Segment "users") + "('$MailboxName')/drive") + "/items('" + $Item.Id + "')")
                write-output $Item
            }     

    }
}

function Get-OneDriveItem{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [String]$DriveRESTURI
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $RequestURL =  $DriveRESTURI 
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName 
            Add-Member -InputObject $JSONOutput -NotePropertyName DriveRESTURI -NotePropertyValue (((Get-EndPoint -AccessToken $AccessToken -Segment "users") + "('$MailboxName')/drive") + "/items('" + $JSONOutput.Id + "')")
            return $JSONOutput
    }
}

function Get-OneDriveItemFromPath{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [String]$OneDriveFilePath
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/drive/root:" + $OneDriveFilePath
            $JSONOutput = Invoke-RestGet -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName 
            Add-Member -InputObject $JSONOutput -NotePropertyName DriveRESTURI -NotePropertyValue (((Get-EndPoint -AccessToken $AccessToken -Segment "users") + "('$MailboxName')/drive") + "/items('" + $JSONOutput.Id + "')")
            return $JSONOutput
    }
}
function Invoke-UploadOneDriveItemToPath{
    param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$false)] [psobject]$AccessToken,
        [Parameter(Position=2, Mandatory=$false)] [String]$OneDriveUploadFilePath,
        [Parameter(Position=3, Mandatory=$false)] [String]$FilePath,
        [Parameter(Position=4, Mandatory=$false)] [Byte[]]$FileBytes
        
    )
    Begin{
        if($AccessToken -eq $null)
        {
              $AccessToken = Get-AccessToken -MailboxName $MailboxName          
        }  
            $HttpClient =  Get-HTTPClient($MailboxName)
            $EndPoint =  Get-EndPoint -AccessToken $AccessToken -Segment "users"
            $RequestURL =  $EndPoint + "('$MailboxName')/drive/root:" + $OneDriveUploadFilePath + ":/content"
            if([String]::IsNullOrEmpty($FileBytes)){
                $Content = ([System.IO.File]::ReadAllBytes($filePath))
            }
            else{
                $Content = $FileBytes
            }
            $JSONOutput = Invoke-RestPut -RequestURL $RequestURL -HttpClient $HttpClient -AccessToken $AccessToken -MailboxName $MailboxName -content $Content -contentheader "application/octet-stream"
            return $JSONOutput
    }
}
function New-ReferanceAttachment{
        param( 
        [Parameter(Position=0, Mandatory=$true)] [string]$Name,
        [Parameter(Position=1, Mandatory=$true)] [string]$SourceUrl,
        [Parameter(Position=2, Mandatory=$false)] [String]$ProviderType,
        [Parameter(Position=3, Mandatory=$true)] [String]$Permission,
        [Parameter(Position=4, Mandatory=$false)] [string]$IsFolder
        
    )
    Begin{
          $ReferanceAttachment = "" | Select Name,SourceUrl,ProviderType,Permission,IsFolder
          $ReferanceAttachment.IsFolder = "False"
          $ReferanceAttachment.ProviderType = "oneDriveBusiness"
          $ReferanceAttachment.Permission = $Permission
          $ReferanceAttachment.SourceUrl = $SourceUrl
          $ReferanceAttachment.Name = $Name
          if(![String]::IsNullOrEmpty($ProviderType)){
                $ReferanceAttachment.ProviderType = $ProviderType
          }
          if(![String]::IsNullOrEmpty($IsFolder)){
               $ReferanceAttachment.IsFolder = $IsFolder
          }
          return $ReferanceAttachment
    }
}
#emdregion