EWS Script: Recover Email Items out of the Purges and Deletions based on a timeframe for when the email items were deleted.

Scenario:  You want to recover email items that were deleted from the mailbox and were moved into the backend Purges and Deletions Recoverable folders. You want to recover only items that were deleted  between a timeframe. You also want to place these deleted items into a single folder available in the mailbox.

Scriptlets:

Declare your Variables:

#Variables
    $cred = Get-credential  #credentials will fullaccess to access the mailbox
    $mailboxname = "Jane@Domain.Com"  #The Mailbox you wish to perform the query and restore on
    $EWS_DLL = "C:Program FilesMicrosoftExchange ServerV15BinMicrosoft.Exchange.WebServices.dll"
    $EWS_URL = "https://mail.domain.com/ews/exchange.asmx"
    [datetime]$StartDate  = "6/5/2017" #Used for the LastModifiedTime
    [datetime]$EndDate = "6/19/2017" #Used for the LastModifiedTime
    $RestoreFolder = "Recovered Items"  #The folder thats is already created in the mailbox to restore to

Configure the EWS connection properties

#Configure connection to EWS
    Import-Module -Name $EWS_DLL
    $service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.Exchangeversion]::exchange2013)
    $service.Url = new-object System.Uri($EWS_URL)
    $service.UseDefaultCredentials = $false
    $service.Credentials = $cred.GetNetworkCredential()

Attach to the following folders:  Purges, Deletions, and Recoverable Items (created in mailbox to restore content to)

#Attach to Purges
    $folderidpurges = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemspurges,$MailboxName)
    $purgesFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderidpurges)
#Attach to Deletions
    $folderidDeletions = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemsDeletions,$MailboxName)
    $DeletionsFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderidDeletions)
#Attach to Recovered Items Folder
    $PathToSearch = $restorefolder  
    $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$mailboxname)   
    $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
    $fldArray = $PathToSearch.Split("") 
    for ($lint = 1; $lint -lt $fldArray.Length; $lint++) { 
        $fldArray[$lint] 
        #Perform search based on the displayname of each folder level 
        $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1) 
        $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint]) 
        $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView) 
        if ($findFolderResults.TotalCount -gt 0){ 
            foreach($folder in $findFolderResults.Folders){ 
                $tfTargetFolder = $folder                
            } 
        } 
        else{ 
            "Error Folder Not Found"  
            $tfTargetFolder = $null  
            break  
        }     

Create your Search Filter and perform the search

#Create and Apply an Search Filter
    $sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
    $Sfgt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::LastModifiedTime, $StartDate)
    $Sflt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::LastModifiedTime, $EndDate)
    $sfCollection.add($Sfgt)
    $sfCollection.add($Sflt)
    $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(2000000)
    $miMailItems = $DeletionsFolder.FindItems($sfCollection,$view)

Lastly,  Move your email Items into the Recovery folder

    ###Moves your Email Items
    $MiMailItems | %{
        "Moving: "+ $_.LastModifiedTime +": " + $_.Subject.ToString()
        [VOID]$_.Move($tftargetfolder.id)
    }

 

 

 

 

 

 

 

 

 

Powershell: List all mailbox folder delegate access for a mailbox

Scenario: You want to determine all mailbox folder delegate access for a mailbox. You wish to not include the anonymous and default permissions.

Script:

$mbx = "steve"
$permissions = @()
$Folders = Get-MailboxFolderStatistics $mbx | % {$_.folderpath} | % {$_.replace(“/”,””)}
$list = ForEach ($F in $Folders)
   {
    $FolderKey = $mbx + ":" + $F
    $Permissions += Get-MailboxFolderPermission -identity $FolderKey -ErrorAction SilentlyContinue | Where-Object {$_.User -notlike “Default” -and $_.User -notlike “Anonymous” -and $_.AccessRights -notlike “None”}
   }
$permissions

Remove a user/group permission on an AD Object via PowerShell

Scenario: You want to remove a users permission to an AD Object via PowerShell. This is the equivalent of opening Active Directory Users and Computers, finding your AD object (user, computer, ect), and removing the users permission from the Security Tab.

Scriptlets:  We are going to remove the user   domainjdoe from the AD Computer  test_computer.

 

#Set these variables
$DistinguishedName = "CN=test_computer,OU=Test,OU=Domain,DC=com"
$user = "domainjdoe"

#Collect the current ACL
$Acl = Get-Acl $DistinguishedName 

#Loop each access permission in the ACL
foreach ($access in $acl.Access) {
        if ($access.IdentityReference.Value -eq $user) {
        $acl.RemoveAccessRule($access)
        } 
}

#Set the ACL Back to the AD Object
set-acl $DistinguishedName  -AclObject $acl

 

Lets say you wanted to do this for every ADObject in a specific OU, run the following

#Set these Variables
$strOU = "CN=test,DC=Domain,DC=Com"
$Obj = get-adobject -searchbase $strOU -properties DistinguishedName,DisplayName -Filter * | Select DisplayName,DistinguishedName
$Obj = $obj | Sort DisplayName
$user = "domainjdoe"
$counter = 0

#Set AD as the location to find the user objects.
Set-Location ad:

#Loop it
$Obj | Select -first 5 | %{
#Increase counter
$Counter++
#Display Output
"
$counter / $($obj.count): Removing $User from $_.DisplayName - $_.DistinguishedName
-----------------------------------------------------------------------
"
#Get the current ACL for the AD Object
$DN = $_.DistinguishedName
$Acl = Get-Acl $DN 

#Loop each Access Level in the ACL And Remove for the User
foreach ($access in $acl.Access) {
       if ($access.IdentityReference.Value -eq $user) {$acl.RemoveAccessRule($access)} }

#Setting the Modified ACL back to the AD Object
set-acl $DN  -AclObject $acl

#reset variables
$ACL = $Null
$DN = $null
}

 

 

Script: Set NTFS Permissions on a Folder via PowerShell

Scenario: You want to set NTFS permissions on a folder via Powershell. You want the permissions to inherit down to SubFolders and files as well.

Scriptlet:

$folder = "\FileSvr1filestest12345"
$user = "DomainJdoe"

# Get the ACL for an existing folder
$existingAcl = Get-Acl -Path $folder

# Set the permissions that you want to apply to the folder
$permissions = $user, 'Read,Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow'

# Create a new FileSystemAccessRule object
$rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permissions

# Modify the existing ACL to include the new rule
$existingAcl.SetAccessRule($rule)

# Apply the modified access rule to the folder
$existingAcl | Set-Acl -Path $folder

 

EWS Script: Monitor EWS is working properly by querying for an email item on a timely basis

Scenario:  After your frontend client access gateway has crashed a couple of times, you would like to test that your frontend client access gateway continues to work properly for EWS. You would like an EWS script to validate that not only can it login to a mailbox, but it can query for a specific item in the mailbox.   If it cannot query for the email item in the mailbox, then you want to be notified.

Script:

  1. First create a mailbox that you will monitor.
  2. Send an email to that mailbox so you can query that email by the subject line.

Create your Variables:

#Mailbox Variables
$MailboxName = "EWSMonitor@Domain.com #Mailbox to Query
$Subject = "EWS Test" #Message to query


#Script Variables
$cred1 = get-credential  #This account has access to the mailbox
$Counter = 0 #If you want the counter to stop after # of loops
$sleep = 600 #10 minutes
$EndLoop = "Run Forever" #Set as value so it stops in a timely manor


#Email Variables
[string[]]$to = "steve@domain.com","billy@domain.com"
$smtp = "mail.domain.com"
$from = "EWS_Monitor@domain.com"
$subject1 = "EWS FAILURE" 


The Looping Script

#Loop#############################################
Do{
    $miMailItems = $null
    "Starting Loop"
    $Counter++
    #Connect to EWS
    Import-Module -Name "C:Program FilesMicrosoftExchange ServerV15BinMicrosoft.Exchange.WebServices.dll"
    $service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.Exchangeversion]::exchange2013)
    $service.Url = new-object System.Uri("https://email.domain.com/EWS/Exchange.asmx")
    $service.UseDefaultCredentials = $false
    $service.Credentials = $cred1.GetNetworkCredential()
    #Attach to Inbox
    $folderidInbox = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
    $InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderidInbox)
    #Search Filter
    $Sfsub = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject, $Subject)
    $sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
    #Build your Search Collection
    $sfCollection.add($Sfsub)
    $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(2000000)
    $miMailItems = $InboxFolder.FindItems($sfCollection,$view)
    "Found MailItem in StaciATest = $($MiMailItems.count)"
    If($MiMailItems.count -NE 1){      
        "MailItem Count -eq Null, Sending Message"
        send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject1 -Priority high
         }
   "Sleeping $Sleep"
    Sleep $sleep
}While($Counter -ne $EndLoop)
#####################################################################################

 

 

 

Remotely configure a certificate after importing the new certificate to your Exchange Servers

Scenario:  You imported a new certificate to your Exchange servers and you want to configure the certificate on each server remotely.  There is OWA/LYNC integration and the thumbprint of the older certificate needs to be updated with the thumbprint of the new certificate for the IMCertificateThumbprint property in each servers OWA web.config file.

Scriptlets:

Declare your variables for the Servers and for the thumbprints of the Old and New Cert.

#Declare Variables
$OldCert = "B8FE4323EEdAAB31258C2F44283001004EEACB23"
$NewCert = "A8E457DE801F7831317C2F5F5450007EA238DDE3"
$Servers = Get-exchangeserver Ex* | Select -ExpandProperty Name

We are going to backup the web.config file as well in the event of a mistake.

#Backup Web.Config and Save it locally
$Servers | %{
"Copying $_"
MD C:TempWebConfig$_
Copy-item "\$_c$Program FilesMicrosoftExchange ServerV15ClientAccessOwaweb.config" "C:TempWebConfig$_"
}

Because OWA/LYNC integrations exists, we need to modify the IMCertificateThumbprint OWA Web.Config file so it updates/overwrites the old thumbprint with the new thumbprint.

#Edit the Web.Config on each Server
$Servers | Sort | %{
"Editing WebConfig for $_"
$WebConfigFile = "\$_c$Program FilesMicrosoftExchange ServerV15ClientAccessOwaweb.config"
(Get-Content $webconfigfile).replace('$OldCert', 'NewCert') | Set-Content $Webconfigfile
}

Enable the Services on the new Certificate

#Enable UM, IIS, SMTP, UMCallRouter on new Cert
$Servers | %{
Enable-ExchangeCertificate -Server $_ -thumbprint $NewCert -services IIS,SMTP,UM,UMCallRouter -force -confirm:$false
}

Finally, Restart IIS & UM Services on each server

#Restart IIS & UM Services
$Servers | %{
iisreset $_
get-service msexchangeUM* -computername $_ | Restart-service
}

 

PowerShell: Search Multiple servers to see if a specific program is installed

Scenario:  You want to quickly search multiple remote servers to see if a specific program is installed.

Script: The following script checks to see if Wireshark is installed on any of our Exchange servers.

#Declare Server Variable
$Ex2013 = Get-exchangeserver ex* | Select -expandproperty Name

#Perform the Query against each server
$Ex2013 | %{"Checking WireShark on $_";Invoke-Command -Computer $_ -ScriptBlock {

#Declare App
$appToMatch = '*Wireshark*'

#Determine Uninstall Directory
if ([IntPtr]::Size -eq 4) {
        $regpath = 'HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall*'
    } else {
        $regpath = @(
            'HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall*'
            'HKLM:SoftwareWow6432NodeMicrosoftWindowsCurrentVersionUninstall*'
        )
    }

#Get all Programs Installed
$1 = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |Sort DisplayName

#Filter based on program
$result = $1 | where {$_.DisplayName -like $appToMatch}

#Display the result if found
If($result -ne $null){
    Write-Host "
    The following matching $appToMatch are Installed on $($env:computername) " -ForegroundColor Yellow
    Write-Host "$($result.displayName)
    " -ForegroundColor Yellow
}
 }}

PowerShell – Easy Way to Manipulate Parts of a FileName

Scenario:  You want to use PowerShell to extract parts of the following filename:   C:temp123.csv

 

Script:

$f =  “C:temp123.csv”

[io.fileinfo] $f | % Basename     #Result:  123.csv

[io.fileinfo] $f | % Extension      #Result:   .csv

[io.fileinfo] $f | % Name        #Result:      123.csv

[io.fileinfo] $f | % DirectoryName      #Result:    C:temp

[io.fileinfo] $f | % FullName   #Result:   C:temp123.csv

 

Install Microsoft Unified Communications Managed API 4.0 Remotely Via PowerShell

Scenario:  You want to install Microsoft’s Unified Communications Managed API 4.0 to multiple servers remotely

Script:

  1. Collect your Servers by Querying AD
Import-Module ActiveDirectory
$strOU = "OU=Exchange2016,DC=XYZ,DC=COM"
$servers = get-adcomputer -searchbase $strOU -properties Name -Filter *|  where {$_.name -like "Ex16-*"} | Select -ExpandProperty Name

2. Copy your Installer File to a folder on each server (or a single network share). We have copied it to C:softwareUCMA.exe on each Exchange Server.

3. Run the Installer by invoking the command on each server.

$servers | %{"Installing UCMA on $_"; Invoke-Command -Computer $_ -ScriptBlock {
    #Set Variables
    $file = "C:softwareUcma.exe" 
    #check to see if its installed
    if (Get-ItemProperty "HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstallUCMA4" -ErrorAction SilentlyContinue) { 
        Write-host "Unified Communications Managed API 4.0 Runtime is already installed." -ForegroundColor Cyan 
        } else {
               #testing
               If (Test-Path $file){ 
                    Write-host "The installer file exists:$file"  -ForegroundColor Green
                     #Installing
                     Write-Host "Installing Microsoft UM API..." -ForegroundColor yellow 
                     $arg = "/quiet /norestart" 
                     $status = (Start-Process $file -ArgumentList $arg -Wait -PassThru).ExitCode 
                     if ($status -eq 0) { write-host "Successfully installed $file" -ForegroundColor Green } 
                     if ($status -ne 0) { write-host "Failed!" -ForegroundColor Red }                           
            } else {Write-host "$file does not exist" -ForegroundColor red}
}
}}

 

 

 

 

Set the Paging File Size for all Exchange Servers remotely via PowerShell

Scenario:  You want a script to set the Paging File Size to 32GB on multiple servers remotely.

Solution:  Run this script from a computer that has the ActiveDirectory Module for PowerShell installed.

#Query AD for your Servers
Import-Module ActiveDirectory
$strOU = "OU=Exchange2016,DC=XYZ,DC=COM"
$servers = get-adcomputer -searchbase $strOU -properties Name -Filter *|  where {$_.name -like "Ex16-*"} | Select -ExpandProperty Name

#Loop through all Exchange Servers and set the Custom Paging size to 32GB 
$servers | Sort Name | %{"Setting Page File Size on $_"; Invoke-Command -Computer $_ -ScriptBlock {
[int]$InitialSize = 32778
[int]$MaximumSize = 32778
$ComputerSystem = $null
$CurrentPageFile = $null
$modify = $false
$ComputerSystem = Get-WmiObject -Class Win32_ComputerSystem -EnableAllPrivileges
if ($ComputerSystem.AutomaticManagedPagefile) {$ComputerSystem.AutomaticManagedPagefile = $false; $ComputerSystem.Put()}
$CurrentPageFile = Get-WmiObject -Class Win32_PageFileSetting
if ($CurrentPageFile.InitialSize -ne $InitialSize) {$CurrentPageFile.InitialSize = $InitialSize;$modify = $true}
if ($CurrentPageFile.MaximumSize -ne $MaximumSize) {$CurrentPageFile.MaximumSize = $MaximumSize;$modify = $true}
if ($modify) { $CurrentPageFile.Put()}
}}