Tag Archives: dirsync

Updating Windows Management Framework breaking DirSync: “Invalid namespace”

Disclaimer
This blog post was written for an older version of the Azure AD Connect Synchronization Service, and has not been tested in the latest version. Always make sure that you have a valid backup before making any changes to your system.

After updating Windows Management Framework you might get an error message in the Event Viewer, “Invalid namespace”. The symptoms includes stopped scheduled synchronizations, and event 0 logged in the Event Viewer when trying to run the synchronization manually.

dirsync-event-0

The problem has to do with the WMI Performance Counters being updated when updating Windows Management Framework.

SOLUTION
The solution is to rebuild and recompile the MOF files used for the Performance Counters.

First, open a command prompt and change directory to %Program Files%\Windows Azure Active Directory Sync\SYNCBUS\Synchronization Service\Bin

 cd "%Program Files%\Windows Azure Active Directory Sync\SYNCBUS\Synchronization Service\Bin"

Open mmswmi.mof in Notepad.exe, and add the following text at the top in the file

#PRAGMA AUTORECOVER

Load the modified MOF file into the WMI repository by running the following command:

mofcomp mmswmi.mof

mofcomp

Register the Microsoft Identity Integration Server dll

regsvr32 /s mmswmi.dll

Restart Windows Management Instrumentation

net stop winmgmt
net start winmgmt

Re-run the DirSync Configuration Wizard, by running %Program Files%\Windows Azure Active Directory Sync\ConfigWizard.exe

Check the Event Viewer again and make sure that the Syncroniation was successful!

/ Andreas

Advertisement

Exchange Online: How to create a dirsynced Resource Mailbox

The idea with DirSync is to keep your user administration on-prem. A problem arise when you decomission the on-premises Exchange server and want to create a Shared Mailbox or a Resource Mailbox. There is no simple way to create such mailbox without assigning a license. It is possible to create a new regular user, assign a license, and then convert it to a Shared Mailbox or a Resource Mailbox, but the drawback with this method is that it requires a license during the process. On the other hand your user account will be fully managed in your on-prem environment, and the goal is achieved.

Another possibility is to create a Resource Mailbox with a Cloud Identity, and then connect it to an account synced from your Active Directory. This is what I will show you now. Lets start with disabling DirSync. This step is not necessary, but we might get some problems if our accounts are synced before they are ready.

Stop-Service MSOnlineSyncScheduler

Then we create a user account in Active Directory that we will later sync to Office 365:

Import-Module ActiveDirectory
$ADUserProperties = @{
    Name =               'Meeting Room 1'
    Path =               'CN=Users,DC=365lab,DC=net'
    SamAccountName =     'room1'
    UserPrincipalName =  'room1@365lab.net'
    DisplayName =        'Meeting Room 1'
    EmailAddress =       'room1@365lab.net'
    OtherAttributes = @{
        ProxyAddresses = 'SMTP:room1@365lab.net'
    }
}
$ADUser = New-ADUser @ADUserProperties -PassThru

The next step is to create a new Resource Mailbox in Office 365. This can be done either with GUI or PowerShell, I prefer PowerShell.

$O365cred = Get-Credential
$O365sess = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $O365cred -Authentication Basic -AllowRedirection
$importcmd = Import-PSSession $O365sess

$O365UserProperties = @{
    DisplayName =        'Meeting Room 1'
    Name =               'room1'
}

$RoomMailbox = New-Mailbox @O365UserProperties -Room

Now we have two separate accounts, one in Active Directory with the managed attributes, and one in the cloud that we want to connect to our on-prem identity. The connection is done by populating the ImmutableID attribute with the corresponding ObjectGuid from Active Directory. Also, we change the UserPrincipalName in Office 365 to match our domain account.

$ObjectGuid = $ADUser.objectGuid
$ImmutableId = [System.Convert]::ToBase64String($ObjectGuid.ToByteArray())

Import-Module MSOnline
Connect-MsolService 

Set-MsolUserPrincipalName -UserPrincipalName $RoomMailbox.UserPrincipalName -NewUserPrincipalName $ADUser.UserPrincipalName -ImmutableId $ImmutableId

Now our UserPrincipalNames are the same in both our Active Directory and in Office 365, and we have linked then together using the ObjectGuid/ImmutableId. Time to start our DirSync service again and force a synchronization to run.

Start-Service MSOnlineSyncScheduler

Import-Module DirSync
Start-OnlineCoexistenceSync -FullSync

Now the Cloud Identity is converted to a DirSynced Identity, and the attributes in Active Directory are syned to our new Resource Mailbox. From now on all user administration tasks for this account can be managed in our on-prem Active Directory.

/ Andreas

Quick Tip: Forcing Office 365 Directory Synchronization to run

Disclaimer
This blog post was written for an older version of the Azure AD Connect Synchronization Service. For information regarding the current version please look at the documentation from Microsoft.

Sometimes you need changes in your on-premises Active Directory to sync to Office 365 as soon as possible. You might, for example, have a new employee at your doorstep one morning, and you quickly have to create an account for him. Normally a sync is only done every three hours. With these commands your data is synced immediately.

Depending on your version of DirSync/AADSync the commands are a bit different:

DirSync prior to version 6862.0000

To start a manual sync you launch the DirSyncConfigShell Console File, located at C:\Program Files\Windows Azure Active Directory Sync.

A sync is triggered by the command

Start-OnlineCoexistenceSync

You can also trigger a full sync by using the -FullSync parameter.

If you are using DirSync with Password Sync you can also run a full password sync with the following lines of PowerShell code:

Set-FullPasswordSync
Restart-Service FIMSynchronizationService -Force

The DirSync result can be viewed in the FIM Client, but to see the result of Password Sync you need to look at the Event Viewer in Windows.

DirSync version 6862.0000 and later

Starting with DirSync version 6862.0000 released on June 5 2014 there is no longer a DirSyncConfigShell Console file in the Program Files folder. Instead you just start a normal PowerShell window and run Import-Module DirSync. After that the Start-OnlineCoexistenceSync cmdlet is available.

Forcing a Password Sync uses the same lines of PowerShell code as the previous versions:

Set-FullPasswordSync
Restart-Service FIMSynchronizationService -Force

Azure Active Directory Synchronization Services (AAD Sync)

A Directory Synchronization is triggered by running an exe file:

C:\Program Files\Microsoft Azure AD Sync\Bin\DirectorySyncClientCmd.exe

force_dirsync

DirectorySyncClientCmd.exe accept two parameters, initial and delta. Running DirectorySyncClientCmd.exe initial initiates to a FullSync, and running DirectorySyncClientCmd.exe delta (or without any parameters) initiates an incremental sync.

The delta synchronization is triggered every 3 hours, and it can also be started manually by running the Scheduled Task “Azure AD Sync Scheduler” in the Task Scheduler.

To force a Password Sync the following lines of PowerShell code is needed. Modify the first two lines to match your environment.

$adConnector  = 'ad.contoso.com'
$aadConnector = 'contoso.onmicrosoft.com - AAD'

Import-Module ADSync

$c = Get-ADSyncConnector -Name $adConnector
$p = New-Object Microsoft.IdentityManagement.PowerShell.ObjectModel.ConfigurationParameter 'Microsoft.Synchronize.ForceFullPasswordSync', String, ConnectorGlobal, $null, $null, $null
$p.Value = 1
$c.GlobalParameters.Remove($p.Name)
$c.GlobalParameters.Add($p)
$c = Add-ADSyncConnector -Connector $c
Set-ADSyncAADPasswordSyncConfiguration -SourceConnector $adConnector -TargetConnector $aadConnector -Enable $false
Set-ADSyncAADPasswordSyncConfiguration -SourceConnector $adConnector -TargetConnector $aadConnector -Enable $true 

/ Andreas

DirSync Configuration Error: “There is no such object on the server”

Recently, I ran across an issue deploying DirSync at a Customer.
The installation went well, but running the “Directory Sync Configuration Wizard” failed with the error message “There is no such object on the server”.
2014-02-03 09-14-26

Investigating the event logs, the configuration seemed to stop at the following event:
2014-02-03 09-19-06
Looking in to AD after the wizard had failed, no account had been created.
I then noticed that the Users container in the domain root was missing.
Since it is there DirSync by default tries to create the sync account and the container didn’t exist, it failed.

After recreating the Users container (can be done with either AdsiEdit or PowerShell), the Configuration Wizard completed successfully.

Recreate the users container with PowerShell

New-ADObject -Description "Default container for upgraded user accounts" ` 
             -Name Users ` 
             -Type Container `
             -ProtectedFromAccidentalDeletion $true

Hope this helps you if you are running in to this problem!

/Johan

Office 365: Migrating DirSync to new AD domain

Disclaimer
This blog post was written for older versions of Office 365 and Azure AD, and has not been tested in the latest version. Always make sure that you have a valid backup before making any changes to your system.

In a migration scenario you might need to replace the Active Directory domain used to sync your users to Office 365. I will go through the steps you need to change AD domain.

In this scenario I assume that ADFS is not used. If it is, you first have to disable federation. I suggest looking at my previous post where I described how to switch from ADFS to Password Sync.

The steps we have to go through are:

  1. Disable DirSync in old domain
  2. Populate new AD domain with users and attributes
  3. Prepare Office 365 users for new domain
  4. Install DirSync in the new domain.
  5. Verify Sync

First, disabling DirSync is very easy. Just go to the Office 365 admin center and click the Deativate link under users and groups.

dirsync-change-domain-1This will take up to 72 hours. When this process is over all user accounts are managed in the Office 365 portal, and there is no connection to your old domain. This change will not cause any service interruption, all users will be able to use their services as normal.

In the meantime you can uninstall the Azure Active Directory Sync tool on the old DirSync server.

The second step is to populate your new AD domain with all user accounts. It is now important that you copy all information from the old domain, (i.e. phone numbers, titles etc), and for Exchange Online it is especially important that these attributes are copied:

  • userPrincipalName
  • proxyAddresses
  • legacyExchangeDN

UserPrincipalName is your login name to Office 365. I don’t think I have to explain why this attribute is important 🙂 . ProxyAddresses are all your email addresses, both primary and alias. The last attribute, legacyExchangeDN, is used if you previously have migrated from an Exchange on-premises to Office 365. It is used for internal addressing in Exchange. If it is removed you will not be able to reply to old emails, meeting invitations, and your Suggested Contacts will also fail.

I will not go through here how to migrate these attributes here, check out our post on how to sync attributes from cloud identities to active directory.

The third step is where the magic happens. Office 365 uses the ObjectGUID attribute to keep track of the user accounts in in your on-premises Active Directory. It is translated to an ImmutableID in Azure Active Directory. If you rename your users, the ObjectGUID is untouched. Hence the name ImmutableID.

dirsync-change-domain-2The problem is that when you move to a new domain, all ObjectGUIDs are changed, and we need to generate a new ImmutableID.

Office 365 generates these IDs for us, we just have to clear the attribute on all users in Office 365. This is easily done with PowerShell:

Set-MsolUser -UserPrincipalName "aaron.beverly@365lab.net" -ImmutableId "$null"

The next step is to activate DirSync in the Office 365 portal again, and then reinstall the Azure Active Directory Sync tool on a server in the new domain. I strongly recommend using a new server for this step. Re-using the old server (after joining it to the new domain) might break your sync.

After the installation a full sync is done. The Sync tool will identify and match the users in Office 365 and Active Directory by the primary email address. If a match is found a new ImmutableID is created and written to Azure Active Directory.

Finally, after the initial sync is done we can look in Synchronization Service Manager to check that all users were matched and that we don’t have any sync errors.

/ Andreas

Managing Office 365 e-mail addresses easy with PowerShell when using DirSync

In most cases you uninstall your local Exchange server after migrating your e-mail to Exchange Online. If you also choose to implement DirSync you place the administration in your local domain instead of the Office 365 Administration Portal.

This means that there is no longer a GUI tool to handle some of the settings related to your mailboxes. Of course you can keep a machine with your old Exchange 2010 Management Console, but often the reason for moving to the cloud is to reduce the complexity and minimize the number of machines.

We don’t have the same problem with other attributes, for example displayname or phone numbers that you’ll find in Users and Computers or the Administrative Center introduced in Windows Server 2008. It does get a bit trickier when you are about to modify primary email address or add alias addresses.

Currently there are no available GUI tools from Microsoft to handle these types of updates easy (but keep your eyes open, things might change 😉 ). However, there are a few third-party tools to achieve this, unless you want to use the Attribute Editor in Users and Computers, or Adsiedit.

Since I’m a big fan of PowerShell I wanted to build a small set of tools to handle simple changes of email addresses. These functions are easy for you to use in your own scripts.

First of all, a function that lists all email addresses of a user:

function Get-O365AliasAddress {
<#
.SYNOPSIS
    Displays all email addresses assigned to a user.
.PARAMETER Identity
    The user to query.
.EXAMPLE
    Get-O365AliasAddress -Identity user01
.EXAMPLE
    Get-ADUser user01 | Get-O365AliasAddress
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user to get addresses of")]
        [string]$Identity
    )

    Import-Module ActiveDirectory

    $result = @()

    (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | foreach {
        $proxy =  $_.split(":")
        $object = New-Object System.Object
        $object | Add-Member –Type NoteProperty –Name Type –Value $proxy[0]
        $object | Add-Member –Type NoteProperty –Name Address –Value $proxy[1]
        $object | Add-Member –Type NoteProperty –Name IsPrimary –Value ($proxy[0] -ceq $($proxy[0].ToUpper()))
        $result += $object
    }
    return $result
}

Next, a function that adds an alias to an existing user

function Add-O365AliasAddress {
<#
.SYNOPSIS
    Adds an alias address to a user.
.PARAMETER Identity
    The user to modify.
.PARAMETER Address
    The address to add.
.PARAMETER Type
    The type of address. Default is smtp.
.PARAMETER SetAsDefault
    Indicates if the address should be de default address of the user
.EXAMPLE
    Add-O365AliasAddress -Identity user01 -Address test@365lab.net -SetAsPrimary
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user")]
        [string]$Identity,

        [Parameter(Mandatory=$True,
        HelpMessage="The address to add")]
        [string]$Address,

        [Parameter(
        HelpMessage="The type of the address to add")]
        [string]$Type="smtp",

        [Parameter(
        HelpMessage="Indicates that the address will be set as the default address")]
        [switch]$SetAsDefault
    )

    Import-Module ActiveDirectory

    $Type = $Type.ToLower()

    $defaultaddress = ''
    $proxyaddresses = ''
    $proxyaddress = ''

    #Get all existing proxyAddresses of the same type
    $proxyaddresses = (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | where-object { $_ -like "$Type*" }

    #Get current default address of this type
    foreach ($proxyaddress in $proxyaddresses) {
        $pa = $proxyaddress.split(':')
        if ($pa[0] -ceq $pa[0].ToUpper()) {
            $defaultaddress = $proxyaddress
        }
    }

    #If this is the first address, it will be the default
    if ($proxyaddresses.count -eq 0) {
        $SetAsDefault = $true
    }

    if ($SetAsDefault) {

        #New default address to set. Start by removing the old one, but keep it as alias.
        if ($defaultaddress) {
            $pa = $defaultaddress.split(':')
            $newdefaultaddress = "$($pa[0].ToLower()):$($pa[1])"
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses=$defaultaddress} -Add @{proxyaddresses=$newdefaultaddress}
        }

        #Set new default address. In case it already exists, remove it first (it might already be used as alias)
        if ($Type -eq 'SMTP') {
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type.ToLower()):$Address" } -Add @{proxyaddresses="$($Type.ToUpper()):$Address" } -EmailAddress $Address
        } else {
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type.ToLower()):$Address" } -Add @{proxyaddresses="$($Type.ToUpper()):$Address" }
        }

    } else {
        #Just add the new address
        Set-ADUser -Identity $Identity -Add @{proxyaddresses="$($Type):$Address" }
    }
}

Finally we also need a script to delete addresses

function Remove-O365AliasAddress {
<#
.SYNOPSIS
    Removes an alias address from a user.
.PARAMETER Identity
    The user to modify.
.PARAMETER Address
    The address to remove.
.PARAMETER Type
    The type of address. Default is smtp.
.EXAMPLE
    Remove-O365AliasAddress -Identity user01 -Address test@365lab.net
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user")]
        [string]$Identity,

        [Parameter(Mandatory=$True,
        HelpMessage="The address to remove")]
        [string]$Address,

        [Parameter(
        HelpMessage="The type of the address to remove")]
        [string]$Type="smtp"

    )

    Import-Module ActiveDirectory

    $Type = $Type.ToLower()
    $defaultaddress = ''
    $newdefaultaddress = ''
    $proxyaddresses = ''

    #Get all existing proxyAddresses of the same type
    $proxyaddresses = (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | where-object { $_ -like "$Type*" }

    #Get current default address of this type
    foreach ($proxyaddress in $proxyaddresses) {
        $pa = $proxyaddress.split(':')
        if ($pa[0] -ceq $pa[0].ToUpper()) {
            $defaultaddress = $proxyaddress
        }
    }

    if ($defaultaddress -eq "$($Type):$Address") {
        #We are trying to remove the default address. Now it becomes a bit tricky...
        #First, find the next address of the same type that we can use as default address
        foreach ($proxyaddress in $proxyaddresses) {
            if ($proxyaddress -ne "$($Type):$Address") {
                $newdefaultaddress = $proxyaddress
                break
            }
        }
    }

    #Now we can remove the address
    Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type):$Address"}
    if ($Type -eq 'smtp' -and $defaultaddress -eq "$($Type):$Address") {
        Set-ADUser -Identity $Identity -Clear mail
    }

    if ($newdefaultaddress) {
        #Set $newdefaultaddress as new default address
        Write-Warning "New default address set: $newdefaultaddress"
        Add-O365AliasAddress -Identity $Identity -Address $newdefaultaddress.split(":")[1] -Type $Type -SetAsDefault
    }
}

Now we can play with these functions:

O365AliasAddress

I have put together a PowerShell Module for you to import. Just load it with the Import-Module cmdlet.

Import-Module O365ProxyAddresses

The module can be downloaded here.

I hope that you will find these functions useful in your daily user administration tasks. Please leave your comments below and feel free to suggest improvements.

Happy coding!

/ Andreas

Office 365 PowerShell tip: Automatically assign licenses based on AD attribute

Important note: The end of an era with licensing scripts is near… and the beginning of a new one with Azure AD Group Based Licensing is here. Group Based Licensing is now in preview and currently requires a paid Azure AD Subscription. Try it out and give Microsoft your feedback on how they can make it even better! 

Using DirSync in combination with Office 365 / Windows Azure Active Directory is great.
It automates user creation and makes you able to master all user creation changes from on premises.
There is just one(or two) things you need to do manually, assign licenses…
This can be done both in the portal or with PowerShell.

Depending on how your license structure looks like and how large environment you have, you might want to automate this in a more convenient way. There is a Wiki article on TechNet with a few examples on how to automate it as well (both with Security Groups and AD attributes). http://social.technet.microsoft.com/wiki/contents/articles/15905.how-to-use-powershell-to-automatically-assign-licenses-to-your-office365-users.aspx

Until Microsoft has come up with an integrated solution for this in DirSync or something else, we have to stick with PowerShell…

The case:
In my case we want to assign Office 365 licenses based on a local AD attribute of your choice fully automated and minimal input.
We also want a bit of logging so we are able to find and fix errors as easy as possible.

In order to assign a license in Office 365, we need to assign two attributes on a user(of course the user must exist…):
UsageLocation and Licenses
Also, if a user has a valid license assigned, the boolean IsLicensed will be set to True.
2013-12-30 15-26-24

The ‘Licenses’ attribute contains “tenantname:LICENSESKU”, which in my case above is “mstlegacy:ENTERPRISEPACK” for an E3 license.
So, when assigning a license with PowerShell we need to know the SKUID of the particular license we are using.

We can also disable specific parts of a license, for example SharePoint when we assign the license. More details about ‘manual’ PowerShell assignment of licenses in Office 365 you’ll find here.

Solution

My script activates Office 365 users based on the AD attribute of your choise.
It requires you to populated the AD attribute with a string that identifies the license type for the particular user.
Default AD attribute used in the script is employeeType.

Supported license types with AD attributes as below (attribute to the left):
E1 – Office 365 for Enterprises E1
E3 – Office 365 for Enterprises E3
K1 – Deskless user without Office
E2 – Deskless user with Office
A1S – Office 365 for Education A1 (Students)
A2S – Office 365 for Education A2 (Students)
A3S – Office 365 for Education A3 (Students)
A1F – Office 365 for Education A1 (Faculty)
A2F – Office 365 for Education A2 (Faculty)
A3F – Office 365 for Education A3 (Faculty)
2014-01-01 15-31-22
In case the AD attribute Country is populated in your AD, it will automatically use that attribute to populate UsageLocation of the user in Office 365, otherwise it will default back to the parameter $DefaultCountry.

It will log all changes and errors to the logfile MSOL-UserActivation.log within the same folder you run the script.
Running the script
Prereqs:

Example – first time use:
As a preparation have to change the default value of $AdminUser parameter or use the parameter -AdminUser to an actual adminuser of your tenant.
On first use it’ll then ask you for the password and then store encrypted with DPAPI to a file in the same folder where you run the script. This so you can run without user interaction in the future.

.\ActivateMSOLUser.ps1 -AdminUser DirSync@mstlegacy.onmicrosoft.com

2013-12-30 16-31-16
(Hopefully you will type in the correct password… 🙂 )

After you’ve finished with the first time configuration, you are ready to actually start assigning licenses.
Example 1 – Activate all K1,K2 and E3 licenses with default AD attribute (employeeType)

.\ActivateMSOLUser.ps1 -AdminUser dirsync@mstlegacy.onmicrosoft.com -Licenses K1,K2,E3

2013-12-30 16-45-58
As seen above, 2 unlicensed users were found in Office 365, but only one of them had the required local AD attribute (employeeType in this case), set to ‘E3’.
I also go two errors since I didn’t have any K1 or K2 licenses in my tenant.

Example 2 – Activate all E3 licenses with custom AD attribute (msDS-cloudExtensionAttribute1) and MasterOnPremise

.\ActivateMSOLUser.ps1 -AdminUser dirsync@mstlegacy.onmicrosoft.com -Licenses E3 -LicenseAttribute msDS-cloudExtensionAttribute1 -MasterOnPremise

2013-12-30 16-57-28
In the above example we found one user to activate, but with the switch -MasterOnPremise we looked in to our local ad instead checking Office 365 for unlicensed users, and reported back to the attribute when the license was successfully assigned.
This can be useful if you for some reason have a lot of unlicensed users in Office 365 that you intend to keep that way.

Note: Since the -MasterOnPremise function writes back to your AD, the account that runs the script will in that case need write permissions to that AD attribute.

2013-12-30 17-03-59

Next Step
In order to make this fully automated, you will also need to schedule this as a task that runs (preferably) as often as your DirSync goes which by default is every 3 hours, an article on how to do that is here.

I’ve been running the script for a while and it works very well, of course some parts can be done more efficient. If you find any bugs or other issues, let me know!

The script can be downloaded from here, or cut’n’paste it from below…

Happy Licensing!
/Johan

ActivateMSOLUsers.ps1

<# .SYNOPSIS     The script automatically assigns licenses in Office 365 based on AD-attributes of your choice. .DESCRIPTION     Fetches your Office 365 tenant Account SKU's in order to assign licenses based on them.     Sets mandatory attribute UsageLocation for the user in Office 365 based on local AD attribute Country (or default)     If switch -MasterOnPremise has been used:     Assigns Office 365 licenses to the user based on an AD-attribute and reports back to the same attribute if a license was successfully assigned.     If not:     Looks after unlicensed users in Office365, checks if the unlicensed user has the right attribute in ad to be verified, otherwise not. .PARAMETER MasterOnPremise     Switch, If used, it  Assigns Office 365 licenses to the user based on an AD-attribute and reports back to the same attribute if a license was successfully assigned.     .PARAMETER Licenses     Array, defines the licenses used and to activate, specific set of licenses supported. "K1","K2","E1","E3","A1S","A2S","A3S","A1F","A2F","A3F" .PARAMETER LicenseAttribute     String, the attribute  used on premise to identify licenses .PARAMETER AdminUser     Adminuser in tenant .PARAMETER DefaultCountry     Defaultcountry for users if not defined in active directory              .NOTES     File Name: ActivateMSOLUsers.ps1     Author   : Johan Dahlbom, johan[at]dahlbom.eu     The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.     #>
param(
[parameter(Mandatory=$false)]
[Switch]
$MasterOnPremise = $false,

[parameter(Mandatory=$false,HelpMessage="Please provide the SKU's you want to activate")]
[ValidateSet("K1","K2","E1","E3","A1S","A2S","A3S","A1F","A2F","A3F")]
[ValidateNotNullOrEmpty()]
[array]
$Licenses,

[parameter(Mandatory=$false)]
[string]
$LicenseAttribute = "employeeType",

[parameter(Mandatory=$false)]
[string]
$AdminUser = "admin@tenant.onmicrosoft.com",

[parameter(Mandatory=$false)]
[string]
$DefaultCountry = "SE"
)
#Define variables
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$Logfile = ($PSScriptRoot + "\MSOL-UserActivation.log")
$TimeDate = Get-Date -Format "yyyy-MM-dd-HH:mm"
$SupportedSKUs = @{
                    "K1" = "DESKLESSPACK"
                    "K2" = "DESKLESSWOFFPACK"
                    "E1" = "STANDARDPACK"
                    "E3" = "ENTERPRISEPACK"
                    "A1S" = "STANDARDPACK_STUDENT"
                    "A2S" = "STANDARDWOFFPACK_STUDENT"
                    "A3S" = "ENTERPRISEPACK_STUDENT"
                    "A1F" = "STANDARDPACK_FACULTY"
                    "A2F" = "STANDARDWOFFPACK_FACULTY"
                    "A3F" = "ENTERPRISEPACK_FACULTY"

                    }

################################################
##### Functions

#Function to Import Modules with error handling
Function Import-MyModule
{
Param([string]$name)
    if(-not(Get-Module -name $name))
    {
        if(Get-Module -ListAvailable $name)
        {
            Import-Module -Name $name
            Write-Host "Imported module $name" -ForegroundColor Yellow
        }
        else
        {
            Throw "Module $name is not installed. Exiting..."
        }
    }
    else
    {
        Write-Host "Module $name is already loaded..." -ForegroundColor Yellow }
    }

#Function to log to file and write output to host
Function LogWrite
{
Param ([string]$Logstring)
Add-Content $Logfile -value $logstring -ErrorAction Stop
Write-Host $logstring
}
#Function to activate your users based on ad attribute
Function Activate-MsolUsers
{
Param([string]$SKU)

    begin {
        #Set counter to 0
        $i = 0
    }

        process {

            #Catch and log errors
            trap {
                $ErrorText = $_.ToString()
		        LogWrite "Error: $_"
                $error.Clear()
                Continue
            }

            #If the switch -MasterOnPremise has been used, start processing licenses from the AD-attribute

            if ($MasterOnPremise) {

		    $UsersToActivate = Get-ADUser -filter {$LicenseAttribute -eq $SKU} -Properties $LicenseAttribute,Country -ErrorAction Stop

                if ($UsersToActivate)
			    {
			    $NumUsers = ($UsersToActivate | Measure-Object).Count

			    LogWrite "Info: $NumUsers user(s) to activate with $SKU"
                foreach($user in $UsersToActivate) {

                          trap {
                                $ErrorText = $_.ToString()
		                        LogWrite "Error: $_"
                                $error.Clear()
                                Continue
                            }
                        $UPN = $user.userprincipalname
                        $Country = $user.Country
                        LogWrite "Info: Trying to assign licenses to: $UPN"
                                if (!($country)) {
                                    $Country = $DefaultCountry }

                        if ((Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop)) {

                                Set-MsolUser -UserPrincipalName $UPN -UsageLocation $country -Erroraction Stop
                                Set-MsolUserLicense -UserPrincipalName $UPN -AddLicenses $SKUID -Erroraction Stop
									#Verify License Assignment
									if (Get-MsolUser -UserPrincipalName $UPN | Where-Object {$_.IsLicensed -eq $true}) {
										Set-ADUser $user -Replace @{$LicenseAttribute=$SKU+' - Licensed at ' + $TimeDate}
										LogWrite "Info: $upn successfully licensed with $SKU"
                                        $i++;
									}
									else
									{
										LogWrite "Error: Failed to license $UPN with $SKU, please do further troubleshooting"

									}
                        }
				    }
			    }
            }
	#If no switch has been used, process users and licenses from MSOnline
            else {
			    $UsersToActivate = Get-MsolUser -UnlicensedUsersOnly -All
				    if ($Userstoactivate)
				    {
				    $NumUsers = $UsersToActivate.Count
				    LogWrite "Info: $NumUsers unlicensed user(s) in tenant: $($SKUID.ToLower().Split(':')[0]).onmicrosoft.com"
				    foreach ($user in $UsersToActivate) {
                                trap {
                                        $ErrorText = $_.ToString()
		                                LogWrite "Error: $_"
                                        $error.Clear()
                                        Continue
                                }

					        $UPN = $user.UserPrincipalName
					        $ADUser = Get-Aduser -Filter {userPrincipalName -eq $UPN} -Properties $LicenseAttribute,Country -ErrorAction Stop
					        $Country = $ADUser.Country
							    if (!($Country)) {
								$Country = $DefaultCountry
							    }
					        if ($ADUser.$LicenseAttribute -eq $SKU) {
						        LogWrite "Info: Trying to assign licenses to: $UPN"
						        Set-MsolUser -UserPrincipalName $UPN -UsageLocation $country -Erroraction Stop
						        Set-MsolUserLicense -UserPrincipalName $UPN -AddLicenses $SKUID -Erroraction Stop

						        #Verify License Assignment
						        if (Get-MsolUser -UserPrincipalName $UPN | Where-Object {$_.IsLicensed -eq $true}) {
							    LogWrite "Info: $upn successfully licensed with $SKU"
                                $i++;
						        }
						   else
						        {

                                LogWrite "Error: Failed to license $UPN with $SKU, please do further troubleshooting"

						        }
				        }
			        }
		        }
	        }
        }

    End{
    LogWrite "Info: $i user(s) was activated with $license ($SKUID)"
	}
}
################################################
#Import modules required for the script to run
Import-MyModule MsOnline
Import-MyModule ActiveDirectory

	#Start logging and check logfile access
	try
	{
		LogWrite -Logstring "**************************************************`r`nLicense activation job started at $timedate`r`n**************************************************"
	}
	catch
	{
		Throw "You don't have write permissions to $logfile, please start an elevated PowerShell prompt or change NTFS permissions"
	}

    #Connect to Azure Active Directory
	try
	{
		$PasswordFile = ($PSScriptRoot + "\$adminuser.txt")
		if (!(Test-Path  -Path $passwordfile))
			{
				Write-Host "You don't have an admin password assigned for $adminuser, please provide the password followed with enter below:" -Foregroundcolor Yellow
				Read-Host -assecurestring | convertfrom-securestring | out-file $passwordfile
			}
		$password = get-content $passwordfile | convertto-securestring -ErrorAction Stop
		$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $adminuser,$password -ErrorAction Stop
		Connect-MsolService -credential $credentials -ErrorAction Stop
	}
	catch [system.exception]
	{
        $err = $_.Exception.Message
		LogWrite "Error: Could not Connect to Office 365, please check connection, username and password.`r`nError: $err"
        exit
	}

if (!$licenses) {
    LogWrite "Error: No licenses specified, please specify a supported license`r`nInfo: Supported licenses are: K1,K2,E1,E3,A1S,A2S,A3S,A1F,A2F,A3F!"
}
else
{

#Start processing license assignment
    foreach($license in $Licenses) {

        $SKUID = (Get-MsolAccountSku | Where-Object {$_.AccountSkuId -like "*:$($SupportedSKUs.Get_Item($license))"}).AccountSkuId

            if ($SKUID)
	        {
		        Activate-MsolUsers -SKU $license
	        }
	        else
	        {
		    LogWrite "Error: No $license licenses in your tenant!"
	        }
    }
}
LogWrite -Logstring "**************************************************"

Office 365 PowerShell Tip – Keeping EmailAddress and UPN in Sync

UPN’s are a nice way for our users to remember their usernames.  A UPN essentially has the same format as an email address.  Rather than having users login to their workstations or intranet resources as CLOUD\Username, they can login as user.lastname@cloud.com which can be the same as their email address.

Problem
When moving to Office 365 and are using DirSync with or without ADFS, your users need to have a UPN with a public routable domain in it. The easiest and most logical way to do that for your users sake is to keep UPN and primary email address the same.

Solution(s)
There are of course a lot of different solutions out there for achieveing the above, many of them include PowerShell and csv-files. My solution is based on PowerShell and are utilizing either Exchange Powershell cmdlets or Active Directory cmdlets.

Solution 1 (Exchange 2007 and above)
The below example is a very quick one that you run in Exchange Management Shell in Exchange 2007 and above. It simply copies the “WindowsPrimaryEmailAddress” for all your user mailboxes to the userPrincipalName attribute, without any question.

Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox | ForEach { Set-Mailbox -Identity $_.Guid.ToString() -UserPrincipalName $_.WindowsEmailAddress.ToString() } 

To verify that the change has been properly done, you can run the below command to list all mailboxes that have different primary email address and UPN:

Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox | Where-Object {$_.UserPrincipalName -ne $_.WindowsEmailAddress.ToString() }

Solution 2 (Exchange 2003 and above)
I had a case where the customer was running Exchange 2003, but had 2008R2 domain controllers. The customer also wanted a log file with the old and the new UPN after the change had been done. The script utilizes the ActiveDirectory module for PowerShell and copies the primary email address from the proxyAddresses attribute.
Running the script without any switches makes it run in test mode and do the AD-changes with the -WhatIf, so no changes will be done here.

2014-01-01 16-09-06

Running the script with the switch -Production makes it do the actual changes.2014-01-01 16-09-22

For backup purposes, the script are also creating a log file with the old and new attribute in the same folder where you run the script.

<#  
.SYNOPSIS 
    Script that copies the primary emailaddress from proxyAddresses to the userPrincipalName attribute.  
    It runs in test mode and just logs the changes that would have bee done without any parameters.  
    It identifies an exchange user with the legacyExchangeDN-attribute.  
.PARAMETER Production 
    Runs the script in production mode and makes the actual changes. 
.NOTES 
    Author: Johan Dahlbom 
    Blog: 365lab.net 
    Email: johan[at]dahlbom.eu 
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.     
#>
param(
[parameter(Mandatory=$false)]
[switch]
$Production = $false
)
#Define variables
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$DateStamp = Get-Date -Format "yyyy-MM-dd-HH-mm-ss"
$Logfile = $LogFile = ($PSScriptRoot + "\ProxyUPNSync-" + $DateStamp + ".log")
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
Write-Host $logstring
}
    try
    {
        Import-Module ActiveDirectory -ErrorAction Stop
    }
    catch
    {
        throw "Module ActiveDirectory not Installed"
    }

#For each AD-user with a legacyExchangeDN, look up primary SMTP: in proxyAddresses
#and use that as the UPN
$CollObjects=Get-ADObject -LDAPFilter "(&(legacyExchangeDN=*)(objectClass=user))" -Properties ProxyAddresses,distinguishedName,userPrincipalName

            foreach ($object in $CollObjects)
            {
                $Addresses = $object.proxyAddresses
                $DN=$object.distinguishedName
                    foreach ($Address In $Addresses)
                    {
                        $ProxyArray=($ProxyArray + "," + $Address)
                        If ($Address -cmatch "SMTP:")
                            {
                                $PrimarySMTP = $Address
                                $UserPrincipalName=$Address -replace ("SMTP:","")
                                    #Found the object validating UserPrincipalName
                                    If ($object.userPrincipalName -notmatch $UserPrincipalName) {
                                        #Run in production mode if the production switch has been used
                                        If ($Production) {
                                            LogWrite ($DN + ";" + $object.userPrincipalName + ";NEW:" + $UserPrincipalName)
                                            Set-ADObject -Identity $DN -Replace @{userPrincipalName = $UserPrincipalName}
                                        }
                                        #Runs in test mode if the production switch has not been used
                                        else {
                                            LogWrite ($DN + ";" + $object.userPrincipalName + ";NEW:" + $UserPrincipalName)
                                            Set-ADObject -Identity $DN -WhatIf -Replace @{userPrincipalName = $UserPrincipalName}
                                        }
                            }
                            else
                            {
                            Write-Host "Info: User $($object.UserPrincipalName) are already OK!"

                            }
                        }
                    }
            }

Hope the above was helpful to you, please let me know if you have any questions!

/Johan