Create a function to connect to and disconnect from Exchange Online

[Update 26/03/2013] I wanted to share a quick update to the script with you. Over the past few weeks, I have hitting an issue at some customers that used an outbound authenticating proxy which prevented me from connecting to Exchange Online using the functions from my profile. As such, I have tweaked the script a bit to now include a switch (-ProxyEnabled) that, when used, will trigger PowerShell to authenticate the session against the proxy.

The new script now looks like this:

function Connect-ExchangeOnline{
    [CmdLetBinding()]
    param(
        [Parameter(Position=0,Mandatory=$false)]
        [Switch]
        $ProxyEnabled
    )

	if($ProxyEnabled){
	    $Session = New-Pssession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential (Get-Credential) -Authentication Basic -AllowRedirection -sessionOption (New-PsSessionOption -ProxyAccessType IEConfig -ProxyAuthentication basic)
	}
	else{
	    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Authentication Basic -AllowRedirection -Credential (get-credential)
	}

	Import-PSSession $session
}

function Disconnect-ExchangeOnline {
	Get-PSSession | ?{$_.ComputerName -like "*outlook.com"} | Remove-PSSession
}

[Original Post]

Office 365 allows you to connect remotely to Exchange online using PowerShell. However, if you had to type in the commands to connect every time, you would be losing quite some time:

$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri <a href="https://ps.outlook.com/PowerShell">https://ps.outlook.com/PowerShell</a> -Authentication Basic -Credential (Get-Credential) -AllowRedirection

Import-PSSession $session

Equally, to disconnect, you’d have to type in the following command each time

Get-PSSession  | ?{$_.ComputerName -like "*.outlook.com"} | Remove-PSSession

However, it is relatively easy to add both commands into a function which you can afterwards add them into your profile:

Function Connect-ExchangeOnline{
   $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri <a href="https://ps.outlook.com/powershell">https://ps.outlook.com/powershell</a> -Authentication Basic -AllowRedirection -Credential (Get-Credential)
   Import-PSSession $session
}

Function Disconnect-ExchangeOnline{
   Get-PSSession  | ?{$_.ComputerName -like "*.outlook.com"} | Remove-PSSession
}

To add these functions to your PowerShell profile, simply copy-past them into your profile. To find out where your profile-file is located, type the following:

PS C:\> $profile
C:\Users\Michael\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

To start using the functions, all you have to do is to call them from the PowerShell prompt:

PS C:\> Connect-ExchangeOnline

cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential

WARNING: Your connection has been redirected to the following URI:
https://pod51014psh.outlook.com/PowerShell-LiveID?PSVersion=3.0

PS C:\> Disconnect-ExchangeOnline

Note   there is no output for the Disconnect-ExchangeOnline function

How-To's Office 365 PowerShell

Updating the FriendlyName property of a certificate using PowerShell

Update: you actually *can* update the property (even if it’s not there). Seems I was just too blind to notice it earlier. Thanks to Michel De Rooij for pointing this out.

In one of my earlier articles, I wrote about how to integrate Office Web Apps with Exchange Server 2013. As part of that process you had to configure the Office Web Apps farm with the name of the certificate that the farm would use.

The certificate attribute that you have to use is stored in the “Friendly Name”-property of the certificate. Although it’s pretty easy using the MMC (duh!), it’s always nice being able to do something through PowerShell.

According to an article I found, certutil.exe could be used to add a Friendly Name to a certificate. Although CertUtil.exe certainly proved its value in the past, I’m not particularly fond of it either.

Unsurprisingly, the solutions with PowerShell is pretty easy! Using the Set-Location cmdlet, you can change your active namespace to the certificate store:

Set-Location cert:

From there, navigate to the location where the certificate you want to add (or change) the property for. For instance:

cd .\\LocalMachine\My

Using Get-ChildItem we can retrieve a list of all the certificates in the store:

Get-ChildItem
PS Cert:\CurrentUser\my> Get-ChildItem

Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\my

Thumbprint Subject
---------- -------
FEA21BCDB0FBFC2B00EBE4DA8A524D0C0999FBDC E=michael@vanhorenbeeck.be, CN=michael@vanhorenbeeck.be, Description=fgt8C...
100953EB6F74F5B60937BB0C7329037D9AE9927A CN=xowas.xylos.com, O=DO_NOT_TRUST, OU=Created by http://www.fiddler2.com
070D4C36B95D9550488F4A2DDCEAF76F5B6C7AAA CN=outlook.linkedinlabs.com, O=DO_NOT_TRUST, OU=Created by http://www.fidd...
0224B3E25491F1A7F71D8367B147F41F3C1250D5 CN=www.google.com, O=DO_NOT_TRUST, OU=Created by http://www.fiddler2.com

Once you’ve determined what certificate you want to update, we need to query the certificate and update the FriendlyName property as follows:

$cert = GCI
$cert.FriendlyName = “FriendlyName”
PS Cert:\CurrentUser\my> $cert = gci 070D4C36B95D9550488F4A2DDCEAF76F5B6C7AAA
PS Cert:\CurrentUser\my> $cert.FriendlyName = "FriendlyName"

That’s it! To verify that the property was set successfully, do the following:

gci
 | fl name,FriendlyName
PS Cert:\CurrentUser\my> gci 070D4C36B95D9550488F4A2DDCEAF76F5B6C7AAA | fl ThumbPrint,FriendlyName

Thumbprint   : 070D4C36B95D9550488F4A2DDCEAF76F5B6C7AAA
FriendlyName : FriendlyName

Exchange 2013 PowerShell

Retrieving Exchange Autodiscover SCP information from AD via PowerShell

As part of the Autodiscover process, Outlook will query Active Directory in search for the Autodiscover SCP which it will use to discover the Autodiscover URL where it should send its request to.

The configuration information for Autodiscover can easily be retrieved with the Get-ClientAccessServer cmdlet, which will show you important information like:

  • AutoDiscoverSiteScope
  • AutoDiscoverServiceInternalUri
  • WhenCreated

The reason that I’m referring to these three items is because of the way Outlook will handle the retrieved Autodiscover information. In a nutshell, it will query AD and will retrieve a list of SCPs. This list will first be ordered based on the Site Scope (the site in which the client resides). If not, the list will be ordered by creation date (older entries will get queried first).

If you wish to find out more about this process, have a look at the following article: http://technet.microsoft.com/en-us/library/bb332063(v=exchg.80).aspx

What I wanted to do, is “mimic” this process without running the Get-ClientAccessServer cmdlet. One of the reasons is that Get-ClientAccessServer cmdlet depends on the Exchange Management Shell (or more accurately, the Exchange snapin).

Below you will find a code example which uses PowerShell’s ability to query AD directly (ADSI). It will use exactly the same query as Outlook does to retrieve a list of SCPs and will then query these SCPs for the information mentioned above. In the end, the information is displayed on screen, where the SCP records are filtered based on SiteScope and Creation date.

This should give you a pretty good idea of what URL/server your Outlook client will connect to (first) during the AutoDiscover process. Enjoy!

$obj = @()

$ADDomain = Get-ADDomain | Select DistinguishedName
$DSSearch = New-Object System.DirectoryServices.DirectorySearcher
$DSSearch.Filter = '(&(objectClass=serviceConnectionPoint)(|(keywords=67661d7F-8FC4-4fa7-BFAC-E1D7794C1F68)(keywords=77378F46-2C66-4aa9-A6A6-3E7A48B19596)))'
$DSSearch.SearchRoot = 'LDAP://CN=Configuration,'+$ADDomain.DistinguishedName
$DSSearch.FindAll() | %{

$ADSI = [ADSI]$_.Path
$autodiscover = New-Object psobject -Property @{
Server = [string]$ADSI.cn
Site = $adsi.keywords[0]
DateCreated = $adsi.WhenCreated.ToShortDateString()
AutoDiscoverInternalURI = [string]$adsi.ServiceBindingInformation
}
$obj += $autodiscover

}

Write-Output $obj | Select Server,Site,DateCreated,AutoDiscoverInternalURI | ft -AutoSize
Exchange How-To's PowerShell

Using New-Migrationbatch to perform local mailbox moves in Exchange Server 2013

Along with a whole bunch of other improvements and new features, New-Migrationbatch is one of my favorite new additions to the Management Shell.

Prior, if you were moving mailboxes between mailbox servers in e.g. Exchange 2010, you had to use New-MoveRequest to start/request a mailbox move from one database/server to the other. If you had multiple mailboxes you wanted to move at once, you had several options:

    • Use Get-Mailbox and pipe the results along to New-Moverequest
    • Import a CSV-file and pipe the results along to New-Moverequest
    • Create an individual move request for each users

While these options are still valid in Exchange Server 2013, you now also have the ability to create a migration batch using the New-MigrationBatch cmdlet.

This cmdlet will allow you to submit new move requests for a batch of users between two Exchange servers, local or remote (other forest) and on-prem or in the cloud (on-boarding/off-boarding). If you have been performing migrations to Office 365, this cmdlet shouldn’t be new to you as it was already available there.

The nice thing about migration batches is that you can easily create them, without having to start or complete them immediately. Although with a little effort you could’ve also done this using New-Moverequest, it’s not only greatly simplified, the use of migraiton batches also gives you additional benefits like:

  • Automatic Reporting/Notifications
  • Endpoint validation
  • Incremental Syncs
  • Pre-staging of data

Just as in Exchange Server 2010, the switchover from one database to the other is performed during the “completion phase” of the move. During this process, the remainder of items that haven’t been copied from the source mailbox to the target mailbox before are copied over after which the user is redirected to his “new” mailbox on the target database. (for purpose of staying on track with this article, I’ve oversimplified the explanation of what happens during the “completion” phase)

Creating a migration batch

To create a migration batch, you’ll need to have a CSV-file that contains the email addresses of the mailboxes you are going to move. These can be any of the email addresses that are assigned to the mailbox. There is – to my knowledge – no requirement to use the user’s primary email address.

Also, the file should have a ‘heading’ called “EmailAddress”:

image

Next, open the Exchange Management Shell and run the following cmdlet:

New-MigrationBatch –Name <name> –CSVData ([System.IO.File]::ReadAllBytes(“<full path to file>”)) –Local –TargetDatabase <dbname>

Running this cmdlet will start a local mailbox move between two Exchange server in the same forest. However, it will not automatically start moving the mailboxes as we haven’t used the –Autostart parameter. Furthermore, the moves won’t be completed automatically either because the –AutoComplete parameter wasn’t used either.

Note   It’s important that you specify the full path to where the csv-file is stored (e.g. C:\Files\Batch1.csv). Otherwise the cmdlet will fail because it will search for the file in the sytem32-folder by default.

Once the batch is created (and you didn’t use the –AutoStart parameter), you can launch the moves by running the following cmdlet:

Get-Migrationbatch | Start-MigrationBatch

Please note that if you have multiple migration batches, this cmdlet will start all of them.

Polling for the status of a migration

You can query the current status of a migration on a per-mailbox basis using the Get-MigrationUserStatistics cmdlet. The cmdlet will return the current status of the mailbox being moved and the amount of items that have been synced/skipped so far.

Get-MigrationUser | Get-MigrationUserStatistics

image

Note   Alternatively, you can also use the –NotificationEmails parameter during the creation of the migration batch. This parameter will allow you to specify an admin’s email address to which a status report is automatically sent. If you don’t use this parameter, no report is created/sent.

Completing the migration

If you didn’t specify the –AutoComplete parameter while creating the migration batch, you will have to manually start the “completion phase”. This can easily be done using the Complete-MigrationBatch cmdlet.

Get-MigrationBatch | Complete-MigrationBatch

When you take a look at the migration statistics, you’ll see that the status will be “Completing”:

image

Once mailbox moves have been completed successfully, the status will change to “Completed”Confused smile

Summary

As you can see, the New-Migrationbatch will certainly prove useful (e.g. if you want to pre-stage data without performing the actual switchover). Of course, there are other use cases as well: it’s the perfect companion to use for cross-forest moves and moves to/from Office 365 as  it contains numerous parameters that can be used to make your life easier. For instance the Test-MigrationEndpoint cmdlet can be used to verify if the remote host (to/from which you are migrating) is available and working correctly. This is especially useful in remote mailbox moves (cross-forest) or between on-prem/cloud.

If you want to find out more about the cmdlet, go and have a look at the following page:

Alternatively, you could also run Get-Help Get-NewMigrationBatch –Online from the Exchange Management Shell which will take you to the same page!

Until later!

Michael

Exchange 2013 How-To's PowerShell

Checking for administrative permissions in PowerShell

Some PowerShell cmdlets require you to have administrative permissions to run them. If you’re creating a script and you’re using such a cmdlet (e.g. writing a file to the root), it would be nice to check up front if the user who is running the script has the required permissions. After all, what good is it to run the script anyway and throw an error?

Fortunately, there’s an easy way in PowerShell to do this. Add the following code to your script and that’s it. It’s a simple if-statement that will stop the script if you don’t have the required permissions. If you do have administrative permissions, nothing happens and the script will continue processing.

If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] “Administrator”))
{
    Write-Warning “You do not have sufficient permissions to run this script!`nPlease re-run this script as an Administrator!”
    Break
}

What happens is that we’re querying the current identity (user who is running the script) and then check whether or not the identity is part of the Built-in Role “Administrator”.

Have fun!

Cheers,

Michael

P.S.: Thanks to Microsoft’s Scripting Guy “Ed Wilson”. Check out his blog: http://blogs.technet.com/b/heyscriptingguy/

How-To's PowerShell

Get a list of installed applications using PowerShell

I was recently at a customer who needed to  get a report of software that was running on each computer. Since they did not have (or at least not fully deployed) a solution that could do that for them (e.g. System Center Configuration Manager), I  proposed to write a PowerShell script which would remotely check a computer using WMI.

Usage

The script accepts a single parameter to indicate the computer you want to get a list of installed applications from:

Get-InstalledApplications –Computer <computername>

The output can be formatted in different ways and even be exported to a file or printed on screen:

Get-InstalledApplications –Computer <computername> | Out-File <file>
or
Get-InstalledApplications -Computer <computername> | Out-GridView

The script

Just copy-paste the code below and save it as a .PS1 file. You can also add the script to your profile so that the function is loaded whenever you open PowerShell.

<#
.Synopsis
   Get a list of the installed applications on a (remote) computer.
.DESCRIPTION
Using WMI (Win32_Product), this script will query a (remote) computer for all installed applications and output the results.
   If required, these results can be exported or printed on screen.
   Please keep in mind that you need to have access to the (remote) computer’s WMI classes.
.EXAMPLE
   To simply list the installed applications, use the script as follows:
   
   Get-InstalledApplications -computer <computername>

.EXAMPLE
   If required, the output of the script can be modified. For instance, viewing the results on screen:
   
   Get-InstalledApplications -computer <computername> | Out-GridView
#>
function Get-InstalledApplications
{
   [CmdletBinding()]
   [OutputType([int])]
   Param
   (
      # defines what computer you want to see the inventory for
      [Parameter(Mandatory=$true,
      ValueFromPipelineByPropertyName=$true,
      Position=0)]
      $computer
   )

   Begin
   {
   }

   Process
   {
      $win32_product = @(get-wmiobject -class ‘Win32_Product’ -computer $computer)

      foreach ($app in $win32_product){
         $applications = New-Object PSObject -Property @{
         Name = $app.Name
         Version = $app.Version
         }

         Write-Output $applications | Select-Object Name,Version
      }
   }

   End
   {
   }
}
How-To's PowerShell