Update Office 365 User License Based on AD Attribute Powershell

Revised 2/10/16: Removed superfluous code, improved error reporting

I hate to see a good script just collect dust so I’m publishing this script in hopes somebody else can benefit from the hours I invested in this. There is certainly room for improvement in this script. As I make revisions, I’ll update this post.

The goal of this script was to key off a preset value in an extensionAttribute and then update the license assignment in Office 365. There were several scenarios that needed to be accommodated that this script was built around. We need to change users from Enterprise Pack to Standard Pack, Change Standard Pack to Enterprise Pack, both having or not having email accounts, removing licenses and creating net new accounts.

Some notes:

I use a switch statement instead of an extensive IF statement. The functions could be better “parameter-ized” to make truly stand-alone cmdlets.

<#
.Synopsis
    This script checks extensionattribute11 on enabled, AD users and compares that value to the license value in Office 365
    Script is built around 3 primary scenarios: unlicensed users, license changed users, deprovisioned users
.DESCRIPTION
    This script checks extensionattribute11 on enabled, AD users and compares that value to the license value in Office 365
    The script performs the following tasks
     1. Connect to Microsoft Online and Exchange On Premise
     2. Functions: 
         Create-RemoteMailbox: Creates a remote mailbox in the onpremise environment
         Change-Office365E1NoMail: Changes the current license to an E1 with no mail
         Assign-Office365E1NoMail: Assigns an E1 license with no mail
         Change-Office365E3NoMail: Changes the current license to an E3 with no mail
         Assign-Office365E3NoMail: Assigns an E3 license with no mail
         Change-Office365E1Full: Changes the current license to an E1 with mail
         Assign-Office365E1Full: Assigns an E1 license with mail
         Change-Office365E3Full: Changes the current license to an E3 with mail
         Assign-Office365E3Full: Assigns an E3 license with mail
         Enable-EmailOptionE3: Turns on the email option for an E3 account where the email option was disabled
         Add-ExchStdLic: Turns onthe email option for an E1 account where the email option was disabled
         Remove-Office365Lic: Unlicense a user in MSOL and deprovisions the mailuser in onpremise exchange
     3. Switch statement invokes one of the functions based on the matching critera
         Criteria #1: EA11 like "Office365*", OnPrem Recipienttypedetails EQ $null
                Function called: Create-remotemailbox
         Criteria #2: EA11 EQ Office365E1, OnPrem RecipientTypeDetails EQ MailUser, MSOL License LIKE "enterprisepack"
                Function called: Change-Office365E1NoMail
         Criteria #3: EA11 EQ Office365E1, OnPrem RecipientTypeDetails EQ MailUser, MSOL acct is not licensed
                Function called: Assign-Office365E1NoMail
         Criteria #4: EA11 EQ "Office365E3", OnPrem Recipienttypedetails EQ MailUser, MSOL License LIKE "standardpack"
                Function called: Change-Office365E3NoMail
         Criteria #5: EA11 EQ "Office365E3", OnPrem RecipientTypeDetails EQ MailUser, MSOL account is not licensed
                Function called: Assign-Office365E3NoMail
         Criteria #6: EA11 EQ "Office365E1", OnPrem RecipientTypeDetails EQ RemoteMailbox ,MSOL Like "enterprisepack"
                Function called: Change-Office365E1Full
         Criteria #7: EA11 EQ Office365E1, OnPrem RecipientTypeDetails EQ RemoteMailbox, MSOL is not licensed
                Function called: Assign-Office365E1Full
         Criteria #8: EA11 EQ Office365E3, OnPrem RecipientTypeDetails EQ RemoteMailbox, MSOL license like "standardpack"
                Function called: Change-Office365E3Full
         Criteria #9: EA11 EQ "Office365E3", OnPrem RecipientTypeDetails EQ RemoteMailbox, MSOL is not licensed
                Function called: Assign-Office365E3Full
         Criteria #10: EA11 EQ "Office365E1", OnPrem RecipientTypeDetails EQ RemoteMailbox, 
                            $msoluser.Licenses.servicestatus.provisioningstatus -like "*Disabled*" `
                            $msoluser.Licenses.servicestatus.serviceplan.servicename -like "*EXCHANGE_S_STANDARD*"
                 Function called: Add-ExchStdLic
        Criteria #11: EA11 EQ "Office365E3", OnPrem RecipientTypeDetails EQ RemoteMailbox,
                            $msoluser.Licenses.servicestatus.serviceplan[8].servicename -eq "EXCHANGE_S_ENTERPRISE" `
                            $msoluser.Licenses.servicestatus.provisioningstatus[8] -eq "Disabled"
                 Function called: Enable-EmailOptionE3
        Critera #12: EA11 EQ "NoLicense", MSOL user is licensed
                 Function called: Remove-Office365Lic

     As MSOL does not natively support -WHATIF, I've added a parameter -TESTMODE to simulate MSOl changes without committing them

.EXAMPLE
   assignoffice365license.ps1 -user jsmith -verbose

   Run the script against a single user with verbose output

.EXAMPLE
   assignoffice365license.ps1 -searchbase "OU=OrgUnit,DC=DOMAIN,DC=COM"

   Run against all users contained in an OU

.EXAMPLE
    assignoffice365license.ps1 -user jsmith -testmode $true -verbose

    Testmode is my custom built "whatif" to simulate MSOL commands without committing them
#>
[cmdletbinding(SupportsShouldProcess)]
Param(
        # User -- samaccountname of user to update
        [Parameter(Mandatory=$True,Position=0)]
        [string]
        $User,

        # Searchbase of the users you run script against
        [Parameter(Mandatory=$true)]
        [string]
        $searchbase,

        # hostname of exchange cas server for powershell connection
        [Parameter(Mandatory=$true)]
        [string]
        $exchcas,

        # testmode -- run script in simulation (msol cmdlets do not natively support -whatif)
        [Parameter(Mandatory=$false)]
        [boolean]
        $testmode = $true
)

Start-Transcript ".\transcript.txt"

##########################
# connect to MSOL service#
##########################
Connect-MsolService -Credential (Get-Credential -Message "Enter your Microsoft Online (Office 365) Username and Password")

#################################
# connect to Exchange On Premise#
#################################
$exchCred = Get-Credential -Message "Enter your Exchange On Prem (AD) Username and Password"
$exchSession = New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri ("http://" + $exchcas + "/PowerShell/") `
-Authentication Kerberos -Credential $exchCred `
-AllowRedirection
Import-PSSession $exchSession -AllowClobber

################################
# Connect to AD                #
################################
$PSDefaultParameterValues = @{"*-AD*:Credential"= $exchCred}
Import-Module activedirectory

###########
#variables#
###########
$myerrvar = $null

$logfile = ".\log.txt"
$errfile = ".\err.txt"

$enterprisePackSKU = "tenantname:ENTERPRISEPACK"
$standardPackSKU = "tenantname:STANDARDPACK"
$exchArchiveAddon = "tenantname:EXCHANGEARCHIVE_ADDON"
$optExchEntDisabled = New-MsolLicenseOptions -AccountSkuId $EnterprisePackSKU -DisabledPlans EXCHANGE_S_ENTERPRISE
$optExchStdDisabled = New-MsolLicenseOptions -AccountSkuId $standardPackSKU -DisabledPlans EXCHANGE_S_STANDARD
$optExchStdEnabled =  New-MsolLicenseOptions -AccountSkuId $standardPackSKU
$optExchEntEnabled = New-MsolLicenseOptions -AccountSkuId $enterprisePackSKU
$erroraction = "Continue" # Continue | Ignore | Inquire | SilentlyContinue | Stop | Suspend
 
#initiate new log file
New-Item $logfile -ItemType File -ErrorAction $erroraction
Add-Content -Path $logfile -Value (Get-Date)

###########
#Functions#
###########
Function Create-RemoteMailbox {
    if($testmode -eq $true){Enable-RemoteMailbox -Identity $i.userprincipalname -Alias $i.SamAccountname `
        -RemoteRoutingAddress ($i.samaccountname +"@tenantname.mail.onmicrosoft.com") `
        -WhatIf}
    else{Enable-RemoteMailbox -Identity $i.userprincipalname -Alias $i.SamAccountname `
        -RemoteRoutingAddress ($i.samaccountname +"@tenantname.mail.onmicrosoft.com") `
        }}

Function Change-Office365E1NoMail{
    if ($testmode -eq $true)
        {write-host ($upn + " Change to Office 365 E1 No Mail")}
    else 
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -userprincipalname $msoluser.userprincipalname `
            -RemoveLicenses $enterprisePackSKU -AddLicenses $standardPackSKU -LicenseOptions $optExchStdDisabled}}

Function Assign-Office365E1NoMail {
    if ($testmode -eq $true)
        {write-host ($upn + " Assign Office 365 365 E1 No Mail")}
    else 
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -userprincipalname $upn `
        -AddLicenses $standardPackSKU -LicenseOptions $optExchStdDisabled}
}

Function Change-Office365E3NoMail{
    if ($testmode -eq $true)
        {write-host ($upn + " Change to Office 365 E3 No Mail")}
    else
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -userprincipalname $msoluser.userprincipalname `
        -RemoveLicenses $standardPackSKU -AddLicenses $enterprisePackSKU -LicenseOptions $optExchEntDisabled}}

Function Assign-Office365E3NoMail{
    if($testmode -eq $true)
        {write-host ($upn + " Assign Office 365 E3 No Mail")}
    else
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -userprincipalname $msoluser.userprincipalname `
        -AddLicenses $enterprisePackSKU -LicenseOptions $optExchEntDisabled}}

Function Change-Office365E1Full{
    if($testmode -eq $true)
        {write-host ($upn + " Change to Office 365 E1 Full")}
    else 
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -UserPrincipalName $msoluser.UserPrincipalName `
        -RemoveLicenses $enterprisePackSKU -AddLicenses $standardPackSKU,$exchArchiveAddon}}

Function Assign-Office365E1Full{
    if ($testmode -eq $true)
        {write-host ($upn + " Assign Office 365 E1 Full")}
    else 
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -UserPrincipalName $msoluser.UserPrincipalName `
        -AddLicenses $standardPackSKU,$exchArchiveAddon}}

Function Change-Office365E3Full {
    if($testmode -eq $true)
        {write-host ($upn + " Change to Office 365 E3 Full")}
    else
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
            try
            {
                Set-MsolUserLicense -UserPrincipalName $upn `
                -RemoveLicenses $standardPackSKU,$exchArchiveAddon -AddLicenses $enterprisePackSKU -ErrorAction Stop
            }
            finally
            {
                Set-MsolUserLicense -UserPrincipalName $upn `
                -RemoveLicenses $standardPackSKU -AddLicenses $enterprisePackSKU
            }
        
        }
}

Function Assign-Office365E3Full{
    if($testmode -eq $true)
        {write-host ($upn + " Assign to Office 365 E3 Full")}
    else
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -UserPrincipalName $upn -AddLicenses $enterprisePackSKU}
}

Function Enable-EmailOptionE3{
    if($testmode -eq $true)
        {write-host ($upn + " Enable Email Option")}
    else
        {Set-MsolUser -UserPrincipalName $upn -UsageLocation "US";
        Set-MsolUserLicense -UserPrincipalName $upn -LicenseOptions $optExchEntEnabled}
}

Function Add-ExchStdLic{
    if($testmode -eq $true)
        {write-host ($upn + " Add Exchange Standard License")}
    else
        {Set-MsolUserLicense -UserPrincipalName $upn -LicenseOptions $optExchStdEnabled -AddLicenses $exchArchiveAddon}
}

Function Remove-Office365Lic{
    if($testmode -eq $true)
        {write-host ($upn + " Removed Licenses")}
    else
        {
            $licenses = $msoluser.Licenses.accountskuid
            foreach ($l in $licenses)
            {Set-MsolUserLicense -UserPrincipalName $msoluser.UserPrincipalName -RemoveLicenses $l}
            Disable-RemoteMailbox $i.DistinguishedName -confirm:$false
        }
}


#################
#Assign Licenses#
#################
 
If($User)
 {
    $ADUser = get-aduser $User -Properties extensionattribute11,msExchRecipientTypeDetails,accountnamehistory
 }
ElseIf ($searchbase)
 {
    $response = Read-Host -Prompt "You are about to run this on all AD users. 
Are you sure? Y/N"
    If ($response = "Y")
        {
        $ADUser = Get-ADUser -Filter {(extensionattribute11 -like "*") -and (enabled -eq $true)} `
        -Properties extensionattribute11,msExchRecipientTypeDetails,accountnamehistory,CanonicalName `
        -SearchBase $searchbase `
        
        }
    Else {break}

 }


foreach ($i in $ADUser){
$msoluser = get-msoluser -UserPrincipalName $i.UserPrincipalName 
$upn = $i.userprincipalname

switch ($i){
{($i.extensionattribute11 -like "Office365E*") `
    -and ([string]::IsNullOrEmpty($i.msExchRecipientTypeDetails)) `
    -and ($msoluser.IsLicensed -eq $False)}
    {Create-RemoteMailbox 
         Add-Content -path $logfile ($upn + " Create-RemoteMailbox " + $i.DistinguishedName)
         Write-Verbose "SWITCH: $upn Create-Remotemailbox"
    }

{($i.extensionattribute11 -eq "Office365E1") `
    -and ($i.msExchRecipientTypeDetails -eq "128")`
    -and ($msolUser.Licenses.accountskuid -like $enterprisePackSKU)}
    {Change-Office365E1NoMail 
        Add-Content -path $logfile ($upn + " Change-Office365E1NoMail " + $i.DistinguishedName)
        Write-Verbose "SWITCH $upn Change-Office365E1NoMail"
    }

{($i.extensionattribute11 -eq "Office365E1") `
    -and ($i.msExchRecipientTypeDetails -eq "128")`
    -and ($msolUser.IsLicensed -eq $False)}
    
    {Assign-Office365E1NoMail 
        Add-Content -path $logfile ($upn + " Assign-Office365E1NoMail "  + $i.DistinguishedName)
        Write-Verbose "SWITCH $upn Assign-Office365E1NoMail"
    }

{($i.extensionattribute11 -eq "Office365E3") `
    -and ($i.msExchRecipientTypeDetails -eq "128")`
    -and ($msolUser.Licenses.accountskuid -like $standardPackSKU)}
    {Change-Office365E3NoMail 
        Add-Content -path $logfile ($upn + " Change-Office365E3NoMail " + $i.DistinguishedName)
        Write-Verbose "SWITCH $upn Change-Office365E3NoMail"g3
    }

{($i.extensionattribute11 -eq "Office365E3") `
    -and ($i.msExchRecipientTypeDetails -eq "128")`
    -and ($msolUser.IsLicensed -eq $False)}
    {Assign-Office365E3NoMail 
     Add-Content -path $logfile ($upn + " Assign-Office365E3NoMail " + $i.DistinguishedName)
     Write-Verbose "SWITCH $upn Assign-Office365E3NoMail"
     }

{($i.extensionattribute11 -eq "Office365E1") `
    -and ($i.msExchRecipientTypeDetails -eq "2147483648")`
    -and ($msolUser.Licenses.accountskuid -like $enterprisePackSKU)}
    {Change-Office365E1Full 
    Add-Content -path $logfile ($upn + " Change-Office365E1Full " + $i.DistinguishedName)
    Write-Verbose "SWITCH $upn Change-Office365E1Full"
    }

{($i.extensionattribute11 -eq "Office365E1") `
    -and ($i.msExchRecipientTypeDetails -eq "2147483648")`
    -and ($msolUser.IsLicensed -eq $False)}
    {Assign-Office365E1Full 
    Add-Content -path $logfile ($upn + " Assign-Office365E1Full " + $i.DistinguishedName)
    Write-Verbose "SWITCH $upn Assign-Office365E1Full"
    }

{($i.extensionattribute11 -eq "Office365E3") `
    -and ($i.msExchRecipientTypeDetails -eq "2147483648")`
    -and ($msolUser.Licenses.accountskuid -like $standardPackSKU)}
    
    {Change-Office365E3Full 
    Add-Content -path $logfile ($upn + " Change-Office365E3Full " + $i.DistinguishedName)
    Write-Verbose "SWITCH $upn Change-Office365E3Full"
    }

{($i.extensionattribute11 -eq "Office365E3") `
    -and ($i.msExchRecipientTypeDetails -eq "2147483648")`
    -and ($msolUser.IsLicensed -eq $False)}
    {Assign-Office365E3Full 
    Add-Content -path $logfile ($upn + " Assign-Office365E3Full " + $i.DistinguishedName)
    Write-Verbose "SWITCH $upn Assign-Office365E3Full"
    }

{($i.extensionattribute11 -eq "Office365E1") `
    -and ($msoluser.IsLicensed -eq "True") `
    -and $msoluser.Licenses.servicestatus.provisioningstatus -like "*Disabled*" `
    -and $msoluser.Licenses.servicestatus.serviceplan.servicename -like "*EXCHANGE_S_STANDARD*" `
    -and ($i.msExchRecipientTypeDetails -eq "2147483648")}
    {Add-ExchStdLic 
    Add-Content -path $logfile ($upn + " Enabled Exchange Option E1 " + $i.DistinguishedName)
    Write-Verbose "SWITCH $upn Enabled Exchange Option E1"
    }

{($i.extensionattribute11 -eq "Office365E3") `
    -and ($msoluser.IsLicensed -eq "True") `
    -and ($msoluser.Licenses.servicestatus.serviceplan[8].servicename -eq "EXCHANGE_S_ENTERPRISE") `
    -and ($msoluser.Licenses.servicestatus.provisioningstatus[8] -eq "Disabled") `
    -and ($i.msExchRecipientTypeDetails -eq "2147483648")}
    {Enable-EmailOptionE3 
    Add-Content -Path $logfile ($upn + " Enabled Exchange Option E3 " + $i.DistinguishedName)
    Write-Verbose "SWITCH $upn Enabled Exchange Option E3"
    }

{($i.extensionattribute11 -eq "NoLicense") `
    -and ($msoluser.isLicensed -eq "True")}
    {Remove-Office365Lic  
    Add-Content -path $logfile ($upn + " Removed Licenses " + $i.DistinguishedName)
    Write-Verbose "SWITCH $upn Removed Licenses"
    }

Default {Write-Verbose "SWITCH $upn did not meet any matching criteria"}
}}


if ($myerrvar -ne $null){
$count = ($myerrvar).count
 for ($c=0; $c -le $count; $c++){Add-Content -path $logfile -value $myerrvar[$c].exception}
 }


#################
#rename log file#
#################

# Check the file exists
if (-not(Test-Path $logfile)){break}
else {$logfile = Get-ChildItem $logfile}

# Get the date
$DateStamp = get-date -uformat "%Y-%m-%d@%H-%M-%S"

$extOnly = $logfile.extension

if ($extOnly.length -eq 0) {
   $nameOnly = $logfile.Name
   rename-item "$logfile" "$nameOnly-$DateStamp.txt"
   }
else {
   $nameOnly = $logfile.Name.Replace( $logfile.Extension,'')
   rename-item "$logfile" "$nameOnly-$DateStamp$extOnly.txt"
   }

Stop-Transcript

Exchange 2010 Set-WebServicesVirtualDirectory error: Unable to access the configuration system on the remote server

Problem:

When trying to run the Exchange powershell command Set-WebServicesVirtualDirectory the following error is returned:

Unable to access the configuration system on the remote server

Solution:

The fix is to re-register the application pool using the aspnet_regiis command (http://msdn.microsoft.com/en-us/library/k6h9cz8h(v=vs.140).aspx)

Identify if the application pool uses 32 bit or 64 bit by opening Task Manager, the w3wp.exe will show *32 next to the image name if the w3wp is running in 32-bit mode, if no *32 is shown next to the image name, then the worker process is running in 64-bit mode.

  • From the command prompt,
    • If its  V2.0 32 bit .Net 2.0 Framework then traverse to C:\windows\microsoft.net\framework\v2.0.50727
    • If its V2.0 64 bit .Net 2.0 Framework then traverse to C:\windows\microsoft.net\framework64\v2.0.50727
    • If its V4.0 32 bit .Net 2.0 Framework then traverse to C:\windows\microsoft.net\framework64\v4.0.30319
    • If its V4.0 64 bit .Net 2.0 Framework then traverse to C:\windows\microsoft.net\framework64\v4.0.30319
  • Then run the command aspnet_regiis -config+

Root Cause:

So why was it necessary to run this command at all? The Exchange Server was upgraded from 2008 R2 to 2012 R2. For several months Exchange ran fine post upgrade without any issue. The issue was only noticed when I tried to run the set-webservicesvirtualdirectory command.

Exchange 2010 New-MailboxExportRequest IncludeFolders Sub-Folders

To export all folders under a folder use following syntax:

new-mailboxexportrequest -mailbox [mailbox] `
-filepath [unc] -IncludeFolders "TestFolder/*"

If you try to export a sub folder, be sure to replace “/” with “//”, for example, if you want export a folder named “SubFolder”:

new-mailboxexportrequest -mailbox [mailbox] `
-filepath [unc] -IncludeFolders "TestFolder\\SubFolder"

And if you want to ensure you have the proper name of a folder to export, this command is handy:

Get-MailboxFolderStatistics -Identity [mailbox] |`
 ft identity

Powershell: Compare 2 Folders and Remove Matching Files

$Folder1 = Get-ChildItem -Path "C:\Folder1" -filter *.txt
$Folder2 = Get-ChildItem -Path "C:\Folder2" -filter *.txt
$FileList = Compare-Object $Folder1 $Folder2 -IncludeEqual -ExcludeDifferent
foreach($i in $filelist){remove-item -path ("C:\Folder1\" + $i.inputobject)}

$filelist is going to contain a list of file names that were found in both Folder1 and Folder2. In this case I wanted to remove the files in Folder1 that were also found in Folder2.

2X ApplicationGateway Printer Redireciton Failing

Error in the 2X log:

TS Agent hostname: [E 08/00000013] Failed to enum printers (The RPC server is unavailable.   [0x000006ba])

This error is different from the other, more typical errors related to printer redirection where the driver is missing. This error is inidicating a problem with the 2X Universal Printer driver.

Running a repair install of the Universal Printer driver resolved this problem.

C:\Program Files\2X\ApplicationServer\UniversalPrinter\x64>2XUnivPrnInst.exe /R