Wednesday, November 11, 2015

Modifying License Assignments in Office 365

Automating the assigning of licenses in Office 365 isn't terribly hard - unless you have multiple subscription packages... and multiple Active Directory domains... and you're in hybrid mode with Exchange on-premises... and you only want to assign certain features of those subscriptions, and then only to certain users. This is one of those stories. Our company originally purchased an E3 subscription for our corporate "knowledge workers", and a K2 subscription for our store associates. Last year we added a K1 subscription for our seasonal associates, and I recently learned that the K2 subscription had been discontinued, so we had to increase our K1 subscription to cover all store associates. Due to this, I recently had to convert a large group of users from the discontinued K2 subscription to the K1 subscription, and only enable them for the sharepoint online feature.

After connecting to our tenant...

connect-msolservice

... I configured the K1 license options to exclude the Exchange Online feature...

$StoreK1License = New-MsolLicenseOptions -AccountSkuId "tenantname:DESKLESSPACK" -DisabledPlans EXCHANGE_S_DESKLESS

Next, I ran a query against our on-premises Active Directory, to build a list of user accounts that needed to be changed...

$k2users = get-aduser -filter {(extensionattribute11 -notlike "Regular") -and ((company -eq "Company1") -or (company -eq "Company2"))} -Server mydomain.local -SearchBase "ou=store employees,ou=stores,dc=mydomain,dc=local"

Now that we have our array of user accounts, we'll use the UserPrincipalname property to locate those users in Office 365. First, we check to see if they do indeed have the K2 license assigned...

foreach ($k2user in $k2users) {
if ((Get-MsolUser -UserPrincipalName $k2user.userprincipalname).licenses[0].accountsku.skupartnumber -eq "DESKLESSWOFFPACK") {

If the call to Get-MsolUser returns true, we proceed to (1) make sure the Usage Location is set to "US", then (2) we remove the K2 license and assign the new K1 license, with our previously defined options, all in one fell swoop...

write-host "Converting $($k2user.userprincipalname) from K2 to K1..." -foreground yellow
set-msoluser -userprincipalname $k2user.userprincipalname -usagelocation "US"
Set-MsolUserLicense -UserPrincipalName $k2user.userprincipalname -RemoveLicenses "tenantname:DESKLESSWOFFPACK" -AddLicenses "tenantname:DESKLESSPACK" -LicenseOptions $StoreK1License

And if the call to Get-MsolUser returned false, the user did not have a K2 license, so we just send a message to the screen and move on to the next user in the list.

} else {
write-host "K2 not found for $($k2user.userprincipalname)..." -foreground darkgray
}
}

I try not to use write-host commands in my regular scripting, but this was a quick and dirty task, and if I had left them out, since the Set-MsolUser and Set-MsolUserLicense commands produce no output (unless they encounter an error), write-host is in there just to provide evidence of progress. My AD query returned over 25000 user accounts, and it takes quite a while to process all those license changes, so the write-host output was good enough for the task at hand.

Here's the whole script:

connect-msolservice

$StoreK1License = New-MsolLicenseOptions -AccountSkuId "tenantname:DESKLESSPACK" -DisabledPlans EXCHANGE_S_DESKLESS

$k2users = get-aduser -filter {(extensionattribute11 -notlike "Regular") -and ((company -eq "Company1") -or (company -eq "Company2"))} -Server mydomain.local -SearchBase "ou=store employees,dc=mydomain,dc=local"


foreach ($k2user in $k2users) {
if ((Get-MsolUser -UserPrincipalName $k2user.userprincipalname).licenses[0].accountsku.skupartnumber -eq "DESKLESSWOFFPACK") {
write-host "Converting $($k2user.userprincipalname) from K2 to K1..." -foreground yellow
set-msoluser -userprincipalname $k2user.userprincipalname -usagelocation "US"
Set-MsolUserLicense -UserPrincipalName $k2user.userprincipalname -RemoveLicenses "tenantname:DESKLESSWOFFPACK" -AddLicenses "tenantname:DESKLESSPACK" -LicenseOptions $StoreK1License
} else {
write-host "K2 not found for $($k2user.userprincipalname)..." -foreground darkgray
}
}


Wednesday, October 28, 2015

Problems Deleting a 2007 Public Folder Database

It's finally time to decommission our Exchange 2007 environment, and after several days of messing around with public folder replicas, I finally got them all moved off and was ready to delete the public folder database. Denied!

The error was something like "object is read only because it was created by a newer version of Exchange." Balls.

Hopped on an Exchange 2010 server, opened an EMS console and ran Get-PublicFolderDatabase. Hmm. Just shows me the 2010 databases. There must be a way.

"Help Get-PublicFolderDatabase" revealed a previously unknown (to me) switch named "IncludePreExchange2010". A-HA!

The solution was to select the 2007 database, and then pipe the object to Remove-PublicFolderDatabase.

Get-PublicFolderDatabase 'DBNAME' -IncludePreExchange2010 | Remove-PublicFolderDatabase

Worked like a charm. All that's left to do is uninstall Exchange 2007 and it'll finally be history.

Wednesday, August 5, 2015

Adding Useful Headers to Inbound Mail with Ironport Message Filters

We recently had a health check performed on our Ironport appliances by Cisco, and the following message filter was recommended to me by Dalton Hamilton, the engineer who worked the engagement for Cisco.

This filter adds several useful headers to all inbound mail.

addHeaders:  if (sendergroup != "RELAYLIST")
{
     insert-header("X-IronPort-RemoteIP", "$RemoteIP");
     insert-header("X-IronPort-MID", "$MID");
     insert-header("X-IronPort-Reputation", "$Reputation");
     insert-header("X-IronPort-Listener", "$RecvListener");
     insert-header("X-IronPort-SenderGroup", "$Group");
     insert-header("X-IronPort-MailFlowPolicy", "$Policy");
}


Thanks, Dalton!

Monday, January 19, 2015

Powershell Quickie: Selectively Applying an ActiveSync Mailbox Policy

I recently ran into a situation where I had to apply a custom ActiveSync policy to a specific subset of the mailboxes in our organization. We have an MDM solution that targets only our domestic users, and in the process of rolling it back (yes, we're no longer requiring users to use our MDM solution), management finally agreed that we could enforce most of our password and security requirements by simply using an ActiveSync policy and eliminate the complexity of the MDM solution. I would have preferred to modify the Default policy, but they wanted the change done quickly and we didn't have time to notify our international users. So, I needed a quick way to set the ActiveSync policy on only our domestic users' mailboxes.

The domestic users are all in one site, but the site is more of a North America site, rather than a USA-only site, so one of the requirements is to modify the domestic mailboxes without touching the Canadian mailboxes. To do this, I needed to target specific OUs.

Our domestic Exchange site has multiple mailbox databases, but we let the system balance the mailboxes across the databases, so the Canadian mailboxes are mixed in with the domestic mailboxes. The database names are similar - GV1DB01, GV1DB02, GV1DB03, etc., so to gather up all the mailboxes, we'll use a wildcard to get the databases, then pipe the results to Get-Mailbox and limit the results to just the OUs we want.

Get-MailboxDatabase GV1* | Get-Mailbox -OrganizationalUnit "ou=Headquarters,dc=domain,dc=local -ResultSize unlimited

After running that command and verifying the results, all we need to do is re-run the command and pipe the results to Set-CASMailbox.

Get-MailboxDatabase GV1* | Get-Mailbox -OrganizationalUnit "ou=Headquarters,dc=domain,dc=local" -ResultSize unlimited | Set-CASMailbox -ActiveSyncMailboxPolicy "DomesticASPolicy"

Once we've brought the international community up to speed with our password and security policies, we'll modify the Default ActiveSync policy, then use the same command to set all the domestic mailboxes back to the Default policy.

Yes, I fully realize that the password and security policies should be applied to everyone, but it's your typical slow-moving corporation, and the domestic and international communities are run by two separate groups, so it's difficult to get everyone on board at once. Everyone should be in sync by the end of the month. Right!