Connect to Exchange 2016 / EWS (Export UM wav file)

Hello -

I’m brand new to this forum, and was hoping to get some guidance. We recently began migrating users from Exchange UM to XMedius (AVST), but in the process got some users that are a bit annoyed that they need to re-record their greeting. I was able to get a script to work that will get one user at a time, but what I’d ultimately like is for this to look at the whole organization for any user who is UM enabled, see if they have the Standard Greeting, and then save it to a location. Whether I call for every UM enabled mailbox by Get-UMMailbox, or I import from a csv file, that works. I’ve provided the script that I’ve found on the web (One Simple Script) and what I was thinking. Any positive thoughts are welcome.

# Parameters for the script
[CmdletBinding(DefaultParametersetName = "Common")]
param(
    [Parameter(Mandatory = $true)] $User,
    [switch] $SaveAudio,
    [string] $Path
)

#Get Creds - Global Administrator prefered
#$UserCredential = Get-Credential
#Import API DLL
$dllpath = 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'
Import-Module $dllpath
#Connect To Exchange 2016
#$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://domain.com/powershell/ -Credential $UserCredential -Authentication Kerberos -AllowRedirection
#Import-PSSession $Session -AllowClobber
#Set Global Admmin User used to login to exchange online above To Allow Application Impersonation to use EWS So This Thing Works!
#Enable-OrganizationCustomization -ErrorAction SilentlyContinue
#New-ManagementRoleAssignment -Role "ApplicationImpersonation" -User $UserCredential.UserName

#Setup User Impersonation for the $Service variable
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016
## Define UPN of the Account that has impersonation rights
$AccountWithImpersonationRights = $UserCredential.UserName
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
#Get valid Credentials using UPN for the ID that is used to impersonate mailbox
$psCred = $UserCredential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(), $psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
########Set the URL of the CAS (Client Access Server) #######
$service.Url = new-object Uri("https://owa.domain.com/ews/exchange.asmx")

# Get the UM mailbox
if ( !($Mailbox = Get-Mailbox $User -ErrorAction SilentlyContinue) ) {
    throw "Mailbox could not be found for user '$($User)'."
}

if ( $Mailbox.Count -gt 1 ) {
    throw "Multiple mailboxes found. Input must be a unique mail-enabled user."
}

If ( $SaveAudio -and -not($Path) ) {
    throw "Path must be specified when -SaveAudio is set"
}

If ( $Path ) {
    If (-not (Test-Path $Path) ) {
        Throw "Path does not exist!"
    }
    If (-not ((Get-Item $Path) -is [System.IO.DirectoryInfo])) {
        Throw "Path must be to a directory!"
    }

}

# Get the SMTP address from the mailbox
$SmtpAddress = $Mailbox.PrimarySmtpAddress.ToString()

# Use EWS Impersonation to locate the root folder in the mailbox
$Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $SmtpAddress)
$FolderId = [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root
$Folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $FolderId)

If (-not $Folder) {
    Throw "Unable to bind to mailbox root folder"
}

# Create the search filter to find the UM custom greetingsd
$searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass, "IPM.Configuration.Um.CustomGreetings", [Microsoft.Exchange.WebServices.Data.ContainmentMode]::Substring, [Microsoft.Exchange.WebServices.Data.ComparisonMode]::IgnoreCase);

# Define the EWS search view
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(100, 0, [Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
$view.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$view.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Associated

# Create the object to return
$Output = New-Object System.Object
$Output | Add-Member -type NoteProperty -name "DisplayName" -value $Mailbox.DisplayName
$Output | Add-Member -type NoteProperty -name "PrimarySmtpAddress" -value $SmtpAddress
$Output | Add-Member -type NoteProperty -name "HasCustomVoicemailGreeting" -value $False
$Output | Add-Member -type NoteProperty -name "VoicemailGreetingModifiedDate" -value $Null
$Output | Add-Member -type NoteProperty -name "HasCustomAwayGreeting" -value $False
$Output | Add-Member -type NoteProperty -name "AwayGreetingModifiedDate" -value $Null

# Do the search and enumerate the results
If ($results = $service.FindItems( $FolderId, $searchFilter, $view )) {
    If ($SaveAudio) {
        # Define the property set required to get the binary audio data
        $psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

        # Add the binary audio data property to the property set
        $PidTagRoamingBinary = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x7C09, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
        $psPropset.Add($PidTagRoamingBinary)

        # Load the new properties
        [Void]$service.LoadPropertiesForItems($results, $psPropset)
    }

    # Loop through the results
    $Item = $Null
    ForEach ($Item in $Results.Items) {
        # If this is the main greeting, set the flag to true
        If ($Item.ItemClass -eq "IPM.Configuration.Um.CustomGreetings.External" ) {
            $Output.HasCustomVoicemailGreeting = $True
            $Output.VoicemailGreetingModifiedDate = $Item.LastModifiedTime
            $Filename = $Mailbox.Name + "_Standard.wav"
        }

        # If this is the extended away greeting, set the flag to true
        If ($Item.ItemClass -eq "IPM.Configuration.Um.CustomGreetings.Oof" ) {
            $Output.HasCustomAwayGreeting = $True
            $Output.AwayGreetingModifiedDate = $Item.LastModifiedTime
            $Filename = $Mailbox.Name + "_Extended.wav"
        }

        If ($SaveAudio) {
            # Write the audio data to the file
            [IO.File]::WriteAllBytes("$Path\$Filename", $Item.ExtendedProperties.Value)
        }
    }
}

return $Output

Hi, just wanted to let you know that I’ve reformatted the code you posted following the guide lines. Please find below the instructions to format the code in the forum.

https://powershell.org/forums/topic/read-me-before-posting-youll-be-glad-you-did/

Just started playing with the EWS API, I’ve not seen a way to search All mailboxes with filter, everything I’ve seen is connecting to a single mailbox. With that said, you’d probably need to create a wrapper around the code that is getting information around every mailbox, something like this:

# Parameters for the script
# [CmdletBinding(DefaultParametersetName = "Common")]
# param(
#     [Parameter(Mandatory = $true)] $User,
#     [switch] $SaveAudio,
#     [string] $Path
# )

#Get Creds - Global Administrator prefered
$UserCredential = Get-Credential
#Import API DLL
$dllpath = 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'
Import-Module $dllpath
#Connect To Exchange 2016
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://domain.com/powershell/ -Credential $UserCredential -Authentication Kerberos -AllowRedirection
Import-PSSession $Session -AllowClobber
#Set Global Admmin User used to login to exchange online above To Allow Application Impersonation to use EWS So This Thing Works!
Enable-OrganizationCustomization -ErrorAction SilentlyContinue
New-ManagementRoleAssignment -Role "ApplicationImpersonation" -User $UserCredential.UserName

#Setup User Impersonation for the $Service variable
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016
## Define UPN of the Account that has impersonation rights
$AccountWithImpersonationRights = $UserCredential.UserName
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
#Get valid Credentials using UPN for the ID that is used to impersonate mailbox
$psCred = $UserCredential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(), $psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
########Set the URL of the CAS (Client Access Server) #######
$service.Url = new-object Uri("https://owa.domain.com/ews/exchange.asmx")

# Get the UM mailbox
# if ( !($Mailbox = Get-Mailbox $User -ErrorAction SilentlyContinue) ) {
#     throw "Mailbox could not be found for user '$($User)'."
# }

# if ( $Mailbox.Count -gt 1 ) {
#     throw "Multiple mailboxes found. Input must be a unique mail-enabled user."
# }

# If ( $SaveAudio -and -not($Path) ) {
#     throw "Path must be specified when -SaveAudio is set"
# }

# If ( $Path ) {
#     If (-not (Test-Path $Path) ) {
#         Throw "Path does not exist!"
#     }
#     If (-not ((Get-Item $Path) -is [System.IO.DirectoryInfo])) {
#         Throw "Path must be to a directory!"
#     }

# }

$mailboxes = Get-MailBox -Filter *

$results = foreach ( $mailbox in $mailboxes ) {
    # Get the SMTP address from the mailbox
    $SmtpAddress = $Mailbox.PrimarySmtpAddress.ToString()

    # Use EWS Impersonation to locate the root folder in the mailbox
    $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $SmtpAddress)
    $FolderId = [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root
    $Folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $FolderId)

    If (-not $Folder) {
        Throw "Unable to bind to mailbox root folder"
    }

    # Create the search filter to find the UM custom greetingsd
    $searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass, "IPM.Configuration.Um.CustomGreetings", [Microsoft.Exchange.WebServices.Data.ContainmentMode]::Substring, [Microsoft.Exchange.WebServices.Data.ComparisonMode]::IgnoreCase);

    # Define the EWS search view
    $view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(100, 0, [Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
    $view.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
    $view.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Associated

    # Create the object to return
    $Output = New-Object System.Object
    $Output | Add-Member -type NoteProperty -name "DisplayName" -value $Mailbox.DisplayName
    $Output | Add-Member -type NoteProperty -name "PrimarySmtpAddress" -value $SmtpAddress
    $Output | Add-Member -type NoteProperty -name "HasCustomVoicemailGreeting" -value $False
    $Output | Add-Member -type NoteProperty -name "VoicemailGreetingModifiedDate" -value $Null
    $Output | Add-Member -type NoteProperty -name "HasCustomAwayGreeting" -value $False
    $Output | Add-Member -type NoteProperty -name "AwayGreetingModifiedDate" -value $Null

    # Do the search and enumerate the results
    If ($results = $service.FindItems( $FolderId, $searchFilter, $view )) {
        If ($SaveAudio) {
            # Define the property set required to get the binary audio data
            $psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

            # Add the binary audio data property to the property set
            $PidTagRoamingBinary = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x7C09, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
            $psPropset.Add($PidTagRoamingBinary)

            # Load the new properties
            [Void]$service.LoadPropertiesForItems($results, $psPropset)
        }

        # Loop through the results
        $Item = $Null
        ForEach ($Item in $Results.Items) {
            # If this is the main greeting, set the flag to true
            If ($Item.ItemClass -eq "IPM.Configuration.Um.CustomGreetings.External" ) {
                $Output.HasCustomVoicemailGreeting = $True
                $Output.VoicemailGreetingModifiedDate = $Item.LastModifiedTime
                $Filename = $Mailbox.Name + "_Standard.wav"
            }

            # If this is the extended away greeting, set the flag to true
            If ($Item.ItemClass -eq "IPM.Configuration.Um.CustomGreetings.Oof" ) {
                $Output.HasCustomAwayGreeting = $True
                $Output.AwayGreetingModifiedDate = $Item.LastModifiedTime
                $Filename = $Mailbox.Name + "_Extended.wav"
            }

            If ($SaveAudio) {
                # Write the audio data to the file
                [IO.File]::WriteAllBytes("$Path\$Filename", $Item.ExtendedProperties.Value)
            }
        }
    }

    return $Output
}

$results