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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s