Email people via Office 365 from PowerShell when passwords about to expire

I have a demonstration environment where many users have accounts but they never logon to AD directly nor look at this demonstration email mailbox. They only use the environment via Azure AD where they logon at Azure AD via the replicated password hash. Because of this they don’t get password expiry notifications and continue to logon however if they try and access something that does hook into AD and not Azure AD the logon fails.

They wanted to be emailed of upcoming password expiry to their real-email. To accomplish this their real email was stored in extensionAttribute10. I didn’t use the proxyaddresses as this may have SIP information. This attribute could be easily set with:

I had a mailbox for a core process I use. Now that user has no other rights so I placed the password in the script but that’s not ideal at all. If this was Azure Automation I could have used a credential object, I could have at least made the password harder to read by creating an encrypted version of the password and then storing that in the file (but its still reversible, just slightly harder to glance at!), e.g.

However the account can’t do anything except email and access to the script location was highly restricted so I left it as text which was also easier to demonstrate below however in my environment I used the alternate approach above just to make it a little harder to get the password on glance :-). Replace this with your own email and password.

The script looks for any password expiring in less than 10 days and emails a simple message. Customize as you like! It has a basic HTML block with a placeholder (MESSAGEHOLDER) that is replaced by a custom string for the user.

Have fun!

Create AD sites through PowerShell

I recently needed to create an AD site for each MTC (an office), add the IP range assigned to that MTC (which was in a CSV file) and then associate the site with a site link for its region. This is so the Active Directory automatic site coverage feature will enable DCs to populate per-site DNS records for the MTCs ensuring authentication traffic uses the most optimal DC. The DCs are spread over four regional locations.

The CSV file simply had one or two second octet numbers for the /16 IP range associated with the MTC. The code therefore enumerates through each OU, checks to see if the MTC can be found in the CSV data for the IP ranges. Next if the site does not already exist it is created, added to its regional site link (based on the parent OU name and for NA if its East or West) and then the IP ranges for the MTC assigned.


Bulk created group policy objects with PowerShell

A lot of the work I do around Active Directory and Azure AD is for our environment used by our global Microsoft Technology Centers. It is built around a number of region-based organizational units which then have child OUs for each MTC.

The requirement was to create a number of GPOs for each MTC which could then be modified by the local administrator of the MTC. To do this I created two template GPOs with most of the basic settings which I then just needed to copy to a new, per-MTC GPO instance then link to the GPO. This was very easy with PowerShell and the GroupPolicy module.

I also had already created the GPOs for a couple of MTCs so wanted to skip creating the objects for them. In the PowerShell below you can see I have a variable for the top-level of the MTC and then an array of the top level regional OUs. From there I have the names of the GPO templates and an array of the MTCs to skip. At that point I just enumerate for OUs, copy the GPOs and link the new per-instance GPO to the OU.


Removing a non-existent domain

Recently lost a huge storage array and with that the DC for a demo child domain. I therefore had to clean up the now non-existent domain. I did this with ntdsutil. Below are the steps involved. Basically the domain controllers for the domain are removed, then the DNS naming context and finally the domain.

Note that if you receive an error that the domain cannot be remove because of a leaf object run the following to force replication.

repadmin /syncall /aped