So, here's a handy URL to bookmark:
https://azure.microsoft.com/en-us/documentation/articles/active-directory-aadconnectsync-feature-prevent-accidental-deletes/
This is the page that explains how to get Azure AD Connect moving again if it gets stuck due to a large number of on-premises account deletions.
Being in the retail sector, our company hires, and subsequently terminates, a large number of seasonal associates, and this year was no different. This morning, I found several messages in my inbox from the "MSOnlineServicesTeam", stating that...
"the Identity
synchronization service detected that the number of deletions exceeded the
configured deletion threshold for [company name]. A total of 16402
objects were sent for deletion in this Identity synchronization run. This met
or exceeded the configured deletion threshold value of 500
objects.
We need you to
provide confirmation that these deletions should be processed before we will
proceed."
The accompanying link leads to a site with instructions for disabling the limit for DirSync, but also has the above link for similar instructions for Azure AD Connect users.
First, verify that your pending deletes are not accidental. I checked with our HR department to confirm that they terminated those 16000+ associates yesterday, and then ran the following powershell command from the Azure AD Connect server:
Disable-ADSyncExportDeletionThreshold
Next, either wait for the scheduled synchronization task to run, or kick it off manually. Either way, it's going to take much longer than normal to push all those deletes up to Office 365.
Finally, be sure to re-enable the delete threshold, just to be safe.
Enable-ADSyncExportDeletionThreshold
Thanks to Andreas Kjellman and his team for all the great work they've done on Azure AD Connect.
Friday, January 15, 2016
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") {
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
}
}
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
} else {
write-host "K2 not found for $($k2user.userprincipalname)..." -foreground darkgray
}
}
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.
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");
}
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!
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!
Thursday, January 8, 2015
Ironport Best Practice articles
A couple of brief blog articles related to Cisco Ironport appliance best practices.
http://emtunc.org/blog/06/2014/cisco-ironport-e-mail-security-appliance-best-practices-part-1/
http://emtunc.org/blog/12/2014/cisco-ironport-e-mail-security-appliance-best-practices-part-2/
http://emtunc.org/blog/06/2014/cisco-ironport-e-mail-security-appliance-best-practices-part-1/
http://emtunc.org/blog/12/2014/cisco-ironport-e-mail-security-appliance-best-practices-part-2/
Sunday, December 15, 2013
Subscribe to:
Posts (Atom)