Category Archives: Office 365

Exchange Online (Archive) offboarding – “Multiple objects with Guid were found”

Quite a few customers that are using Exchange Online Archiving in an Exchange Hybrid environment, where they have their primary mailboxes on-premises and the archive mailboxes in Exchange Online. This makes a very flexible solution and a perfect start to utilize but not go all in to the cloud.

Just as in a “regular” hybrid environment, you have the possibility to on and offboard the archive mailboxes from and to the on-premises Exchange server. While testing offboarding of an archive mailbox a while ago, I ran in to an interesting error related to the ExchangeGuid attribute.

The exact error message trying to start the moverequest was as follows:
“Multiple objects with Guid 1b2eaa95-0d64-4469-9fb2-d8f9be3e28ce were found”

Multiple objects with the same Guid, could that really be true? Investigating further confirmed the error message. All users in Exchange Online/Office365 had the same value in the ExchangeGuid attribute, even if they didn’t have an archive mailbox.

The following kb describes a similiar problem in a hybrid environment, but the exact solution could not be applied in my scenario since the RemoteMailbox-cmdlets isn’t working in EOA scenarios. It do however state that in order for a successful move, the ExchangeGuid should be the same on-premises as in the cloud.

After doing the change on one user as below the move started and completed successfully.

#Connect to Exchange Online and Exchange On Premises with prefixes
$cred = Get-Credential
$OnPremSess = New-PSSession -ConfigurationName Microsoft.Exchange `
                            -Authentication Kerberos `
                            -ConnectionUri http://exchangeserver/powershell
$O365Sess = New-PSSession -ConfigurationName Microsoft.Exchange `
                          -Authentication Basic `
                          -ConnectionUri `
                          -AllowRedirection:$true `
                          -Credential $cred
Import-PSSession $O365Sess -Prefix Cloud -AllowClobber
Import-PSSession $OnPremSess -Prefix OnPrem -AllowClobber

#Get the On-Premises mailbox
$Mailbox = Get-OnPremMailbox -Identity
#Get the Exchange Online MailUser
$MailUser = Get-CloudMailUser -Identity
#Change the ExchangeGuid to match the on-premises Guid
$MailUser | Set-CloudMailUser -ExchangeGuid $Mailbox.ExchangeGuid

Since it worked on one user, I then decided to to the change on all users that had EOA activated, as below:

#Get all Mailboxes with EOA Activated
$Mailboxes = Get-OnPremMailbox -Filter {(Archivestate -eq "HostedProvisioned")}
foreach ($Mailbox in $Mailboxes) {
    $CloudUser = Get-CloudMailUser -Identity $mailbox.userprincipalname
    if ($CloudUser.ExchangeGuid -ne $mailbox.ExchangeGuid) {
        $clouduser | Set-CloudMailUser -ExchangeGuid $mailbox.ExchangeGuid

I still have a MS support case open regarding how the attribute ended up the same on all users and will update the post when we have identified the root cause.

Let me know if you have questions!



Creating AD users with help from SharePoint Online and PowerShell

Creating users in different systems is a common task that in many cases can be quite challenging. It gets even more challenging having migrated email to Office 365, decommisioned the Exchange Servers, still keeping the local Active Directory. Using Directory Synchronization/Password sync, as of today, creating users in the correct way in your local Active Directory is not optional. By the correct way, I mean a user with proper UserPrincipalName, proxyAddresses, mail etc, which usually is where we get problems.

We do of course have lots of tools, like FIM/MIM and others to help us do this in an automated and predictable way. But if we don’t have that, what are the options?

In this post, I will give you an example on how to create a “poor mans solution” on this topic, using tools that you already have if using Office 365. SharePoint Online and PowerShell. If you have System Center Orchestrator in your environment, the integration with SharePoint Online can be handled by an integration pack instead.

Overview on that the post will cover:

  • Read list items from an already existing list in SharePoint Online.
  • Create an AD user in a predictable way based on input from SharePoint Online.
  • Send an email to the new users manager with the account information.
  • Report back status to SharePoint  with status and username/email address to the SharePoint list item.

First of all, you need to create a SharePoint list with the columns that you want to use as input for your new users. In my example I have called the list “OnBoarding” and created  the columns as below. The status column will in my case be extra important since I will use that to determine if the user has been created or not. (there are other ways to do that of course).

2015-05-14_13-33-52 2015-05-14_15-07-11

Next up is preparing our “automation server” with the right tools. We will use the Client Side Object Model (CSOM) in SharePoint to integrate with SharePoint Online. For that we need the SharePoint Server 2013 Client Components SDK, which can be downloaded from here.  You also need to have the ActiveDirectory PowerShell module on the machine running the script.

First of all, we need input data in the SharePoint list for the user to create. 


Then configure the script to fit your AD Settings, SPO credentials, addresses and the column name mappings in the $SPListItemColumns hashtable. Note that spaces will be replaced by ‘_x0020_’, so for example ‘Last Name’ will be ‘Last_x0020_Name’.

Apart from the above, the example script consists of the simple functions described below:

  • Convert-ToLatinCharacters – will get rid of all the non-unicode characters and some more creating the username. (Thanks to Johan Akerstrom)
  • Get-JDDNFromUPN – returns distinguishedname of a userprincipalname, to support adding a manager to the AD User.
  • New-JDSamAccountName – Generates a samaccountname for the user, based on firstname + lastname (default 3+3 characters). Will handle duplicates.
  • New-JDUPNAndMail – Generates a userprincipalname and emailaddress based on firstname.lastname. Will also handle duplicates but assumes that UPN and Email are the same in your environment.
  • New-JDADUser  Creates the AD user and returns sAMAccountName, UserPrincipalName and the Password. Password generated by the System.Web GeneratePassword method (there are of course other and better methods)
  • Send-JDWelcomeEmail – Basic function to send a “Welcome Email” to the manager with the account information. Lots of room for design improvement in the email sent out, but it works. The email will contain the users password in clear text, so be careful with the recipient.


    The scripts creates and active directory users based on input from a SharePoint list in SharePoint Online.
    For more details, read at
   .\CreateADUsersFromSPO.ps1 -Verbose
    File Name: CreateADUsersFromSPO.ps1
    Author   : Johan Dahlbom, johan[at]
    Blog     : 
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
    Requires PowerShell Version 3.0!
    $UPNSuffix = "",
    $OrganizationalUnit = "OU=Users,OU=365lab Inc.,DC=ad,DC=365lab,DC=net",
    $PasswordLength = 12
#Import the Required Assemblys from the Client components SDK (
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName(“System.Web”) | Out-Null
#Import Active Directory Module
Import-Module ActiveDirectory -Verbose:$false
#Define SharePoint Online Credentials with proper permissions
$User = ''
$Pass = ConvertTo-SecureString 'password' -AsPlainText -Force
#SharePoint online address and list name
$SPOUrl = ""
$SPOList = "UserOnboarding"
#Column Name mapping in SharePoint 
$SPListItemColumns = @{
        FirstName = "First_x0020_Name"
        LastName = "Last_x0020_Name"
        Title = "Title"
        Manager = "Manager"
        Status = "Status"
        Mail = "EmailAddress"

#region functions
function Convert-ToLatinCharacters {
function Get-JDDNFromUPN {
    param (
        [ValidateScript({Get-ADUser -Filter {UserprincipalName -eq $_}})] 
        $ADUser = Get-ADUser -Filter {UserprincipalName -eq $UserPrincipalName} -ErrorAction stop
        return $ADUser.distinguishedname
function New-JDSamAccountName {
    param (
        [parameter(Mandatory=$false)][int]$FirstNameCharCount = 3,
        [parameter(Mandatory=$false)][int]$LastNameCharCount = 3
    #Construct the base sAMAccountName
    $BaseSam = "{0}{1}" -f (Convert-ToLatinCharacters $FirstName).Substring(0,$FirstNameCharCount),(Convert-ToLatinCharacters $LastName).Substring(0,$LastNameCharCount)
    #Add a number until you find a free sAMAccountName
    if (Get-ADUser -Filter {samaccountname -eq $BaseSam} -ErrorAction SilentlyContinue) {
        $index = 0
        do {
            $sAMAccountName = "{0}{1}" -f $BaseSam.ToLower(),$index
        } until (-not(Get-ADUser -Filter {samaccountname -eq $sAMAccountName } -ErrorAction SilentlyContinue))
    } else {
        $sAMAccountName = $BaseSam.tolower()
    return $sAMAccountName
function New-JDUPNAndMail {
    param (
    #Construct the base userPrincipalName
    $BaseUPN = "{0}.{1}@{2}" -f (Convert-ToLatinCharacters $FirstName).replace(' ','.').tolower(),(Convert-ToLatinCharacters $LastName).replace(' ','.').tolower(),$UPNSuffix
    if (Get-ADUser -Filter {userprincipalname -eq $BaseUPN} -ErrorAction SilentlyContinue) {
        $index = 0
        do {
            $UserPrincipalName = "{0}{1}@{2}" -f $BaseUPN.Split("@")[0],$index,$UPNSuffix
        } until (-not(Get-ADUser -Filter {userprincipalname -eq $UserPrincipalName} -ErrorAction SilentlyContinue))
    } else {
        $UserPrincipalName = $BaseUPN
    return $UserPrincipalName
function New-JDADUser {
    param (
        [Parameter(Mandatory=$true)][int]$PasswordLength = 12
     #Generate a password
     $Password = [System.Web.Security.Membership]::GeneratePassword($PasswordLength,2)
     #Construct the user HT
     $ADUserHt = @{
        GivenName = $FirstName
        SurName = $LastName
        ChangePasswordAtLogon = $true
        EmailAddress = $UserPrincipalName
        UserPrincipalName = $UserPrincipalName
        sAMAccountName = $sAMAccountName
        Title = $Title
        Name = "$FirstName $LastName ($sAMAccountName)"
        Displayname = "$FirstName $LastName"
        Manager = $Manager
        Path = $OU
        AccountPassword = (ConvertTo-SecureString -String $Password -AsPlainText -Force)
        Enabled = $true
        OtherAttribute = @{proxyAddresses = "SMTP:$UserPrincipalName"}
     try {
        #Create the user and return a custom object
        New-ADUser @ADUserHt -ErrorAction Stop 
        Write-Verbose "Successfully created the user $($ADUserHt.Name)"
        [pscustomobject] @{
            sAMAccountName = $ADUserHt.sAMAccountName 
            UserPrincipalName = $ADUserHt.UserPrincipalName 
            Password = $Password
     } catch {
        Write-Warning "Error creating the user $($ADUserHt.Name) `r`n$_"
function Send-JDWelcomeEmail {
    param (
        [Parameter(Mandatory=$false)][string]$Sender = "",
    $message = @"
    A user with the following account details was created in Active Directory:
    Username: $usAMAccountName
    EmailAddress: $uUserPrincipalName
    Password: $uPassword (needs to be changed at first logon)
    Best Regards
    365lab IT department
    $MailMessageHt = @{
        From = $Sender
        To = $Recipient
        Body = $message
        SmtpServer = ""
        Subject = "User $uUserPrincipalName was created"
    Send-MailMessage @MailMessageHt
#endregion functions
#Connect to SharePoint Online
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SPOUrl)
$Context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($User, $Pass)
#Get the list
$List = $Context.Web.Lists.GetByTitle($SPOList)
$Query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery(100) 
$Items = $List.GetItems($Query)
#Loop through list items
foreach($item in $Items) {
    #Get list item for later reference
    $ListItem = [Microsoft.SharePoint.Client.ListItem]$listItem = $List.GetItemById($Item.FieldValues["ID"])
    if ($item.FieldValues[$SPListItemColumns.Status] -eq "New") {
        Write-Verbose "Processing list item $Firstname $LastName with ID=$($item.FieldValues["ID"])"
        #Put the fieldvalues in variables.
        $FirstName = $item.FieldValues[$SPListItemColumns.FirstName]
        $LastName = $item.FieldValues[$SPListItemColumns.LastName]
        $Title = $item.FieldValues[$SPListItemColumns.Title]
        $Manager = $item.FieldValues[$SPListItemColumns.Manager].LookupValue
        #Generate Sam, UserPrincipalName/Email and the managers DN
        try {
            $sAMAccountName = New-JDSamAccountName -FirstName $Firstname -LastName $LastName
            $UPNandMail = New-JDUPNAndMail -FirstName $Firstname -LastName $LastName -UPNSuffix $UPNSuffix
            $ManagerDN = Get-JDDNFromUPN -UserPrincipalName $Manager
            #Create the user in Active Directory
            $NewAdUserHt = @{
                FirstName = $Firstname
                LastName = $LastName
                Manager = $ManagerDN
                sAMAccountName = $sAMAccountName
                UserPrincipalName = $UPNandMail
                Title = $Title
                OU = $OrganizationalUnit
                PasswordLength = $PasswordLength
            $ADUser = New-JDADUser @NewAdUserHt -ErrorAction Stop
            Write-Verbose "Created the AD user $sAMAccountName with UPN $UPNandMail"
            #Update EmailAddress in the SharePoint List
            $ListItem[$SPListItemColumns.Mail] = $ADUser.UserPrincipalName
            $ListItem[$SPListItemColumns.Status] = "Completed"
            Write-Verbose "Updated the SharePoint list item with status=Completed"
            #Send information to manager
            Send-JDWelcomeEmail -Recipient $Manager -uUserPrincipalName $UPNandMail -usAMAccountName $sAMAccountName -uPassword $ADUser.Password
            Write-Verbose "Sent account information to $Manager"
        } catch {
            Write-Warning $_
        #Execute the changes

Having understood all the functions and settings, we are now ready to run the script!
The script executed successfully, which means we should now be able to add the script as a scheduled task to automate the task even further.

We now have a properly created AD user according to my organizations needs, and the manager has got an email with the account information needed. The only thing left now is waiting for the directory synchronization and licensing scripts to kick in, and the user will be all set.

Created AD user

Updated SharePoint list item

Information email sent to manager

Hopefully this has given you some useful information regarding how you can create your simple self service tool with some help from things you already have. Let me know if you have any feedback!


Get in control of your Office 365 usage with PowerShell reporting!

Having deployed different Office 365 workloads often makes you want to get some statistics on how the services are used.
You can find quite a few reports for most of the services in the reporting part of the admin UI. 2015-04-18_20-57-28
The most common ones used are the inactive users and mailboxes reports, but there are also reports on how users are utilizing Lync Online, OneDrive for Business etc.

What not so many people know about is that all these reports and some more are available through Exchange Online PowerShell as well.
As seen above, we have 61 cmdlets related to reporting in EXO PowerShell, some of them quite interesting. For example, Get-LicenseVsUsageSummaryReport, that will give you a brief overview of how many active users you have compared to the amount of assigned licenses. Detailed information about the cmdlets can be found here.

Active users per workload in comparison to the amount of licenses assigned to the tenant.

Except the report above, two reports that many of my customers schedule and send out as an email to to admins, are Get-StaleMailboxReport and Get-StaleMailboxDetailReport. Those reports can in many cases help us avoid having to use Get-MailboxStatistics that has been the only option earlier.

Happy reporting!


Unable to Connect to Skype for Business using old BPOS account

Recently I had to make a change in the Skype for Business Online environment of a customer. When I was trying to connect with Remote PowerShell I got an error message saying that it was “Unable to query AutoDiscover URL”.


It turned out that this customer was once using BPOS, and the domain used at that time was This domain is not supported to use with Skype for Business, since important DNS records are missing.

Luckily there are two solutions for this problem:

Solution 1

Connect using the -OverrideAdminDomain option, as described in KB2909536.

$cssession = New-CsOnlineSession `
        –Credentials $cred `
        –OverrideAdminDomain ''

This will force the cmdlet to look for DNS records at the domain

Solution 2

This solution requires you to change the user name of the admin account to a supported domain (or create a new account). Renaming accounts can be tricky, but it is worth the job, since the domain support is deprecated.


By using a new admin account I was able to connect.


/ Andreas

Get rid of your Office 365 Scheduled tasks with Azure Automation!

Implementing Office 365 in a production environment you most often end up with quite a few scheduled scripts for licensing, shared mailbox maintenance and other similiar tasks. When moving more and more services to the cloud, moving from infrastructure services to platform services is the way of nature.

One service that can help you to get more efficient when it comes to scheduling scripts is Azure Automation. Instead of having a server where you schedule your scripts, you simply schedule them within the automation service that takes care of the rest. As of now, Azure Automation does not support use of the Azure Active Directory Powershell module, which means we cannot use the service for our licensing scripts. Make sure to vote for the suggestion to fix that here.

Exchange Online, Lync Online and SharePoint Online do however work well, so that’s what my example is going to be about. To make it easy for me I’m using a script from one of my older posts about Dynamically adding mailbox permissions through groups, with some adjustments to fit Azure Automation.

1. First of all, if you don’t already have one, you need an Azure Automation account. I’m choosing to create mine in the West Europe region. By default, it will use a free automation plan that gives you 500 minutes of job runtime per month.
2. Now create an asset/setting that will store your Exchange Online admin credentials in a secure way. We will call the asset ‘EXOCreds’ so we easliy can pick them up later in the script.2015-04-05_17-03-15
3. Create a runbook in the automation account you created earlier, in my case I called it ‘Sync-EXOSharedMailboxPermissions’.
4. Now it’s time to author the runbook, in my example I’ve just changed some minor things with parameters and credentials. If you’re a PowerShell WF Pro, there might be a lot of things you can do to improve the script.
Below you’ll find the code I’ve used:


workflow Sync-EXOSharedMailboxPermissions {
    The script will automatically assign mailbox and recipient permissions on shared mailboxes based on groups.
    Modified 2015-04-05 to support Azure automation and PowerShell workflows
    File Name: SharedMailboxViaGroups.ps1
    Author   : Johan Dahlbom, johan[at]
    Blog     :
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
    Requires PowerShell Version 3.0!
    #Credentials to connect to Exchange Online
    $Credentials = Get-AutomationPSCredential -Name 'EXOCreds'
    #Prefix to search for
    $Prefix = 'SM-'
    function Connect-ExchangeOnline {
    param (
        #Clean up existing PowerShell Sessions
        Get-PSSession | Remove-PSSession
        #Connect to Exchange Online
        $Session = New-PSSession –ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $Creds -Authentication Basic -AllowRedirection
        $Commands = @("Add-MailboxPermission","Add-RecipientPermission","Remove-RecipientPermission","Remove-MailboxPermission","Get-MailboxPermission","Get-User","Get-DistributionGroupMember","Get-DistributionGroup","Get-Mailbox")
        Import-PSSession -Session $Session -Prefix "Cloud" -DisableNameChecking:$true -AllowClobber:$true -CommandName $Commands | Out-Null
    Connect-ExchangeOnline -Creds $Credentials
    inlineScript {
        function Add-JDMailboxPermission {
            try {
                Add-CloudMailboxPermission -Identity $SharedMailboxName -User $Identity -AccessRights FullAccess -ErrorAction stop | Out-Null
                Add-CloudRecipientPermission -Identity $SharedMailboxName -Trustee $Identity -AccessRights SendAs -Confirm:$False -ErrorAction stop | Out-Null
                Write-Output "INFO: Successfully added $Identity to $SharedMailboxName"
            } catch {
                Write-Warning "Cannot add $Identity to $SharedMailboxName`r`n$_"
        function Remove-JDMailboxPermission {
            try {
                Remove-CloudMailboxPermission -Identity $SharedMailboxName -User $Identity -AccessRights FullAccess -Confirm:$False -ErrorAction stop -WarningAction ignore | Out-Null
                Remove-CloudRecipientPermission -Identity $SharedMailboxName -Trustee $Identity -AccessRights SendAs -Confirm:$False -ErrorAction stop -WarningAction ignore  | Out-Null
                Write-Output "INFO: Successfully removed $Identity from $SharedMailboxName"
            } catch {
                Write-Warning "Cannot remove $Identity from $SharedMailboxName`r`n$_"
        function Sync-EXOResourceGroup {
                [string]$Prefix = 'SM-'
            #Get All groups to process mailboxes for
            $MasterGroups = Get-CloudDistributionGroup -ResultSize Unlimited -Identity "$Prefix*"
            foreach ($Group in $MasterGroups) {
                #Remove prefix to get the mailbox name
                $MbxName = $Group.Name.Replace("$Prefix",'')
                $SharedMailboxName =  (Get-CloudMailbox -Identity $MbxName -ErrorAction ignore -WarningAction ignore).WindowsLiveID
                if ($SharedMailboxName) {
                    Write-Verbose -Message "Processing group $($Group.Name) and mailbox $SharedMailboxName"
                    #Get all users with explicit permissions on the mailbox
                    $SharedMailboxDelegates = Get-CloudMailboxPermission -Identity $SharedMailboxName -ErrorAction Stop -ResultSize Unlimited | Where-Object {$_.IsInherited -eq $false -and $_.User -ne "NT AUTHORITY\SELF" -and $_.User -notmatch 'S-\d-\d-\d+-\d+-\d+-\d+-\w+' -and $_.User -notlike "$Prefix*"} |  Select-Object @{Name="User";Expression={(Get-CloudUser -identity $_.User).WindowsLiveID }}
                    #Get all group members
                    $SharedMailboxMembers = Get-CloudDistributionGroupMember -Identity $Group.Identity -ResultSize Unlimited
                    #Remove users if group is empty
                    if (-not($SharedMailboxMembers) -and $SharedMailboxDelegates) {
                        Write-Warning "The group $Group is empty, will remove explicit permissions from $SharedMailboxName"
                        foreach ($user in $SharedMailboxDelegates.User) {
                            Remove-JDMailboxPermission -Identity $user -SharedMailboxName $SharedMailboxName
                        #Add users if no permissions are present
                    } elseif (-not($SharedMailboxDelegates)) {
                        foreach ($user in $SharedMailboxMembers.WindowsLiveID) {
                            Add-JDMailboxPermission -Identity $user -SharedMailboxName $SharedMailboxName
                        #Process removals and adds
                    } else {
                        #Compare the group with the users that have actual access
                        $Users = Compare-Object -ReferenceObject $SharedMailboxDelegates.User -DifferenceObject $SharedMailboxMembers.WindowsLiveID 

                        #Add users that are members of the group but do not have access to the shared mailbox
                        foreach ($user in ($users | Where-Object {$_.SideIndicator -eq "=>"})) {
                            Add-JDMailboxPermission -Identity $user.InputObject -SharedMailboxName $SharedMailboxName
                        #Remove users that have access to the shared mailbox but are not members of the group
                        foreach ($user in ($users | Where-Object {$_.SideIndicator -eq "<="})) {
                            Remove-JDMailboxPermission -Identity $user.InputObject -SharedMailboxName $SharedMailboxName
                } else {
                    Write-Warning "Could not find the mailbox $MbxName"
        #Start Processing groups and mailboxes
        Sync-EXOResourceGroup -Prefix $Using:Prefix -Verbose

5. Test the runbook by clicking the test button. You will be asked to save the runbook before you test. Hopefully your output will be as nice looking as mine. 🙂
6. If all went good, you’re now ready to publish and schedule the runbook. I’m choosing to schedule mine to run every three hours. Depending on your script runtime, you might want to change this due to cost or other factors. (Remember your free 500 minutes!)

My runbook is now published and scheduled, now it’s just to wait for the magic to happen. Hope this gives you an idea what Azure Automation can do for you!

Additional resources to get started can be found below:
Get Started with Azure Automation
Automating the Cloud with Azure Automation (MVA)

Until next time! 🙂


Real world example on Network Security Groups in Azure

I have got many follow up questions regarding my post series on building your SSO infrastructure in Azure. One of the most common questions asked, have the one regarding how to configure the internal firewalls (Network Security Groups) between the perimeter subnet and the internal subnet in the Azure Vnet.

To make it as simple as possible, I am reusing the Vnet configuration from the first post in the ADFS series, as below.
2014-11-22 13-42-09
The example below assumes that your WAP servers is not joined to the domain. Please note that after you attach an NSG to a subnet, you will have to create specific rules for each endpoint that you have created (example RDP, WinRM etc.).

See the high level sketch below with the subnets including firewall rules.
To make the configuration as easy as possible, I’m using the NSG on a subnet level.

Note: As of today, you need to create and configure your Network Security Groups using Azure PowerShell. Prior doing any of the configuration below, you do need to connect to your Azure subscription with PowerShell.

1. Create a new NSG with the command below. They are created on a location basis, which in my case will be North Europe.

New-AzureNetworkSecurityGroup -Name "North Europe Perimeter" -Location "North Europe"

After the NSG has been created, a good way to check out the rule set in detail is by running the following command and redirecting the output to GridView.

(Get-AzureNetworkSecurityGroup -Name "North Europe Perimeter" -Detailed).Rules |
    Select-Object * | Out-GridView


2. Attach the NSG to the subnet with the below command. Note that all inbound endpoints will stop working if you haven’t created a proper rule set at this point.

Set-AzureNetworkSecurityGroupToSubnet -Name "North Europe Perimeter" `
                                      -VirtualNetworkName "365lab-Azure" `
                                      -SubnetName "Azure-Perimeter"

3. Now it is time to configure the rule set. To make it a bit easier to get an overview of the rules, I am using a CSV file as input. Remember that the priority of the rules are very important. You can download an example of the csv file here.

#Get the created NSG
$NSG = Get-AzureNetworkSecurityGroup -Name "North Europe Perimeter"
#Import the csv with the rules
$Rules = Import-Csv .\NSG-Ruleset.csv 

foreach ($Rule in $Rules) {
    try {
        Set-AzureNetworkSecurityRule -Name $Rule.Name `
                                     -Type $Rule.Type `
                                     -Priority $Rule.Priority `
                                     -Action $Rule.Action `
                                     -SourceAddressPrefix $Rule.SourceAddressPrefix `
                                     -SourcePortRange $Rule.SourcePortRange `
                                     -DestinationAddressPrefix $Rule.DestinationAddressPrefix `
                                     -DestinationPortRange $Rule.DestinationPortRange `
                                     -Protocol $Rule.Protocol `
                                     -NetworkSecurityGroup $NSG -ErrorAction Stop | Out-Null
        Write-Output "Created rule $($Rule.Name) successfully"
    } catch {
        Write-Warning "Error creating rule $($Rule.Name)`r`n$_"

After a little while, you should have simliar output as below in your PowerShell console.

You have now configured a DMZ/Perimeter network subnet in Azure to support your ADFS/WAP setup. The above method can of course be used creating all kinds of rules in NSG’s. Let me know if you have any questions!


Azure AD Sync – Configure attribute based filtering using PowerShell

Most often when synchronizing your directories to AAD, you don’t want all your users to get synchronized. One of the most common methods of filtering out who should get synced and not is by using attributes.
Since AADSync arrived the process of doing this has changed a bit. In this post I will go through how to configure the filtering with PowerShell. Read here about the other methods for filtering objects in AADSync.

In this particular example I will filter out users by the following criteria:

  • UserPrincipalName DOES NOT END with

I have created a PowerShell function to make the creation a bit easier to configure the filtering. If not specifying a domain with the -DomainName parameter, it will create the rule for all your domains connected to AADSync (if you have more than one). To create a filtering configuration as in my example, just run the cmdlet as below.

New-JDAADSyncFilteringRule -Name "In from AD - User NoSync Filter" `
                           -Attribute "userPrincipalName" `
                           -Value "" `
                           -Operator NOTENDSWITH `
                           -Precedence 50

Please note that the filter is not quite as forgiving as most things usually are nowdays when it comes to case-sensitivity.


function New-JDAADSyncFilteringRule {
        The function will create AADSync filtering rules based on attributes and conditions
        New-JDAADSyncFilteringRule "Inbound from AD" -Attribute "userPrincipalName" -Value "" -Operator ENDSWITH -Precedence 50
        File Name: New-JDAADSyncFilteringRule
        Author   : Johan Dahlbom, johan[at]
        Blog     :
        The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
        Requires PowerShell Version 3.0!
        [ValidateScript({Get-ADSyncConnector -Name $_})]

    #Import ADSync Module
    Import-Module ADSync
    #Check if connector/domain name has been provided
    if ($DomainName) {
        $ADConnectors = Get-ADSyncConnector -Name $DomainName
    } else {
        $ADConnectors = Get-ADSyncConnector | Where-Object {$_.Type -eq "AD"}

    foreach ($ADConnector in $ADConnectors) {
        try {
            #Create the Scope Filter Object
            $Scopefilter = New-Object Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition
            $Scopefilter.Attribute = $Attribute
            $Scopefilter.ComparisonValue = $Value
            $Scopefilter.ComparisonOperator =  $Operator
            #Create the Attribute Flow
            $AttrFlowMappings = New-Object Microsoft.IdentityManagement.PowerShell.ObjectModel.AttributeFlowMapping
            $AttrFlowMappings.Source = "True"
            $AttrFlowMappings.Destination = "cloudFiltered"
            $AttrFlowMappings.FlowType = "constant"
            $AttrFlowMappings.ExecuteOnce = $False
            $AttrFlowMappings.ValueMergeType = "Update"
            #Add the Scope Filter to a Scope Group
            $ScopeFilterGroup = New-Object Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeConditionGroup

            $SyncRuleHt = @{
                Connector = $ADConnector.Identifier.Guid
                Name =  $Name
                SourceObjectType = "user"
                TargetObjectType = "person"
                Direction = "inbound"
                AttributeFlowMappings = $AttrFlowMappings
                LinkType = "Join"
                Precedence = $Precedence
                ScopeFilter = $ScopeFilterGroup
            Add-ADSyncRule @SyncRuleHt | Out-Null
            Write-Output "Added the Syncrule $Name ($Precedence) for the attribute $Attribute with the condition $Operator $Value"
        } catch {
            Write-Warning "$_"

Using the SyncRulesEditor.exe (or the cmdlet Get-ADSyncRule) in the folder where you have installed AADSync (most commonly C:\Program Files\Microsoft Azure AD Sync\UIShell\) and verify that your settings successfully has been saved/configured.2015-02-09_00-21-54

As always, if you have suggestions for improvements of changes in the scripts or posts, let us know! 🙂


Office 365: Deploying your SSO Identity Infrastructure in Microsoft Azure (Using Azure AD Connect) – Part 3

This is the last part of 3 in the series where we go through how to create a highly available SSO infrastructure for Office 365 in Microsoft Azure. In this part we will finish off the configuration and put all the pieces together. To make it a bit more interesting, I’ve also decided to try out the Preview of Azure AD Connect to configure the AADSync, ADFS and WAP servers.
Part 1 of the series can be found here.
Part 2 of the series can be found here.

If you haven’t already promoted your DC in Azure, the following example snippet will promote the domain controller to your existing domain using the the account you’re logged on with. Note that the Database/Log/Sysvol paths has been changed to the additional disk that was added to the DC. We do this since we need to use a separate volume that is not using host caching for the AD databases in Azure.The server will also automatically reboot after the promotion has been done.

Import-Module ADDSDeployment
$DCPromotionSettings = @{
    NoGlobalCatalog = $false
    CreateDnsDelegation = $false
    CriticalReplicationOnly = $false
    DatabasePath = "F:\Windows\NTDS"
    LogPath = "F:\Windows\NTDS"
    SysvolPath = "F:\Windows\SYSVOL"
    DomainName = "365lab.internal"
    InstallDns = $true
    NoRebootOnCompletion = $false
    SiteName = "Azure-IAAS-Dublin"
    Force = $true
    SafeModeAdministratorPassword = (ConvertTo-SecureString -String 'YourStrongDSRMPassword!' -AsPlainText -Force)
Install-ADDSDomainController @DCPromotionSettings

Instead of using pure PowerShell to configure the servers, I’ve chosen to use the new Azure AD Connect Preview, a one stop shopping-wizard for setup and configuring AADSync, ADFS, WAP against Azure AD. Sounds very promising right?

1. Download and install(AzureADConnect.msi) the tool from here. In my case I am running the wizard on the AZURE-AADSYNC1 server.
2. Go through the Prerequisite and Azure tenant wizard as below. Your Azure AD Credentials should of course be a service Account with Global administrator permissions.
3. Since we want to deploy ADFS and WAP during the installation, we click customize to be able to do that.
4. Single sign on it is! In this example we’ll use the federation service name of
5. Set up connections to your AD forest(s). Note that the credentials used here should be a proper configured service account. Check out this script for a good way to configure delegation on the service account.
6. In my case I’ll have both an Exchange Hybrid deployment and Password Write back through AADP enabled.
7. Since I have only have one forest/domain, I’m just using the default settings for the next two steps.
8. Import the .pfx file for your service. In my case I have a certificate with the CN that will be imported to all ADFS and WAP machines. If you want to set up this in a lab environment, you can use (gives you 1 year free single name certificates trusted by most browsers).
9. Now point out your ADFS and WAP servers, in my case I have two of each (as deployed in the last post). Note that you will not be able to add the servers to the wizard unless they have PS Remoting enabled. This can be enabled by running the PowerShell command ‘Enable-PSRemoting -Force’ on each machine (or put it in the deployment script :))
The server with only lower case letters will be the primary ADFS server in the farm.
It gives me a warning regarding the WAP servers since I’ve pre-deployed the WAP role.
10. Now specificy an account with Local Admin credentials on the primary ADFS server, in order to create a trust between the WAP servers and the federation servers.
11. Choose which service Account that should be used for the ADFS farm. I do recommend using Group Managed Service accounts if possible (requires minimum 2012 DC’s). In the GMSA case, the wizard will actually create a KDS Root key in your domain if you haven’t one since before. Note that this is also done with WinRM through the Domain Controller, so make sure you have that enabled there as well.
12. Choose the domain you want to use for the Federated setup. As it seems right now with this preview, you can only create federation for one domain.
13. Review your configuration. If don’t want to start off by synchronize your entire directory, uncheck “Start the synchronization..” and look in to the following site on how to filter your synchronization scope. Fire off the installation by clicking Install.
14. Installation complete! PUH! If you run in to any errors during the installation, or cancel the installation, you’ll be able to continue from where you left.
As seen above, you’ll also have the option of verifying your ADFS service DNS records.
In my case, I’ve configured the service name to point to the Azure Internal Load balancer IP ( internally, and to the WAP Cloud service name externally (, as seen below:
If you don’t want to rely on your VPN Connection for the internal STS, you could publish the internal ADFS farm through the external cloud service name, and create access rules to only allow your external public IP’s.
And my DNS records turned out to be OK according to the wizard! 🙂

In a very simple to understand wizard we’ve done a normally quite complex task as easy as it can possibly be. The end result in this case is an highly available SSO Identity infrastructure with a little help of Azure IAAS. One thing to be aware of though (might be changed later since this is a preview):

  • Since the AAD Connect Wizard (Preview) only supports one domain, it will convert the the domain to federated without the -SupportMultipleDomain. This means you’ll have to convert the first domain to standard and then back again using the -SupportMultipleDomain switch if federating with more than vanity domain. Hopefully this is something that will change when it goes RTM.
    We have now finished configuring with our highly available SSO Identity infrastructure for our Office 365/Azure Active Directory. Not to hard with the help of PowerShell and the new Azure AD Connect Wizard. I will follow this series up with some additional topics with more detailed information regarding how to create firewall rules between our subnets in Azure, and more.

    If you have any questions or suggestions, let me know!