New User Automation – Part 2
In Part 1 of New User Automation we looked at creating the user account in Active Directory, copying all of the properties we needed and adding the new user to all of the proper groups. Now we need to create the home directory, set the permissions and finally update the Spiceworks ticket with the information we created.
Home Directory Creation
It’s funny, before Windows 2008 the home directory creation was perfect. It would create the new directory, set the domain admins group and the user with full control permissions and everything worked great. For some reason Microsoft changed this in 2008 and allowed inheritance to work. Normally, this isn’t a big deal and can be safely ignored. But in my environment we ran into a bit of an issue with this default behavior and it’s all Apple’s fault.
A little over a year ago we introduced Apple iPad’s into our environment for our sales people, and it soon became apparent that they needed access to their files on the file server. This is pretty easily accomplished using WebDav on the file server, and I even wrote up a How-To at Spiceworks on how to do it. One of the things I did was create a WebDav share on my DFS tree at a very high level, which allows my iPad users access to pretty much anything they have rights to on the file server, including the “User” folder. The nice thing about Windows 2008 is it now has the ability to only show you folders you have access to, but this does require at a minimum “LIST” permissions to the user folder itself and then you’ll only see the user folders you have access to.
Which brings us back to home directory creation in Windows 2008. Since inheritance is now turned on on the user folder, every new user folder created using ADUC has a minimum of LIST rights inherited from the Users folder. Which means my iPad users can see every file in those user’s folders. Security is still good in that they can’t even read those files (list permissions does not grant read permissions) so it’s not a show stopping error but it sure is annoying. I ended up writing a script that goes through and resets the permissions–I can post that if anyone’s interested.
This means one of my primary goals of the New User Automation script was to not duplicate this problem but to set the permissions properly so that my WebDav users only see what they need to. Now, if you’ve ever tried to deal with NTFS permissions in vbScript you know what a nightmare it is. Luckily Powershell has gone a long way towards making this easier. I won’t say that it’s exactly easy but it’s definitely better then vbScript ever managed!
First thing we need to do is determine where to create the user folders. I have three sites each with their own file server, so my script needed to determine which file server to create it on. There would be a few ways you could do this, such as looking at the distinguishedName of the user, then using Switch to define the file server based on a keyword in there. The problem is this requires a lot of fixed code that if you add or remove a site would require you to go into the code and update it–something I’d like to avoid if possible. What I decided to do is look at the home directory of my “like” user and copy that, all we have to do is extract the user name out of it.
$HomePath = ($LikeUser.HomeDirectory).SubString(0,($LikeUser.HomeDirectory).IndexOf("\$($LikeUser.SamAccountName)"))
It looks a little ugly, but the idea is we want to use SubString at the beginning of the string (position 0) and then go forward to the point where we see “\username”. We can use IndexOf again, and get the “like” users username using $LikeUser.SamAccountName. Since it’s inside a double quote, we have to surround that with another $() to make sure Powershell parses it the way we want.
Next part is pretty easy, we just want to create the new home directory and then set the new user account with that information, as well as assign the drive letter. In our environment we use the U: drive.
New-Item -Name $UserName -ItemType Directory -Path $HomePath | Out-Null Set-ADUser $UserName -HomeDirectory "$HomePath\$UserName" -HomeDrive U:
As a funny side note, I helped someone out over at PowerGUI’s Community with an issue with assigning the home drive. He was using the Quest AD tools, but they are very similar to the Microsoft ones I tend to use, but the problem he was having was the home drive path was being set properly but the home drive assignment was not. Turns out he was using -HomeDrive U and it wasn’t working. I told him to try -HomeDrive U: just for grins and it worked! Turns out the same is true for the Microsoft tools.
Now we need to work with permissions. First thing we need to load the ACL settings into a object variable so we can work with it. We also need to turn off inheritance so we don’t run into the same problem I described above.
$ACL = Get-Acl "$HomePath\$UserName" $ACL.SetAccessRuleProtection($true, $false)
The Get-ACL cmdlet will load our variable with all of the ACL object settings for the directory we specify. We can then use that object and the SetAccessRuleProtection method. Here’s a link to what those positional settings are for. The one we’re really concerned about is the second position, $false. This turns off inheritance.
Just to make sure, I loop through all of the ACL settings and remove them. This is not strictly necessary since we have a new directory and have turned off inhertiance, but I like to do it just to make sure nothing extra is left in there. Now we want to use the AddAccessRule method to add our new security settings and this is where it gets a little complicated. This method doesn’t accept a simple code in string format, but actually requires a special object as input. The object in question is the System.Security.AccessControl.FileSystemAccessRule object. This does accept string input so we define the group/user to get access, what level of access it gets and then some container settings. Here’s another link, not quite a straight forward as the last one but it at least explains what the different settings are.
Add the two rules, one for Domain Admins and the other for the user, then use the Set-ACL cmdlet to save the changes.
$ACL.Access | ForEach { [Void]$ACL.RemoveAccessRule($_) } $ACL.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("$NTDomain\Domain Admins","FullControl", "ContainerInherit, ObjectInherit", "None", "Allow"))) $ACL.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("$NTDomain\$UserName","Modify", "ContainerInherit, ObjectInherit", "None", "Allow"))) Set-Acl "$HomePath\$UserName" $ACL
I love how the FileSystemAccessRule object allows the use of normal english for the permissions level and doesn’t go with those crazy security identifiers that vbScript forced us into using. Those identifiers are still in there, so you have the same level of granular control as always, but there are several identifiers that have been named for the commonly used permission levels. So nice!
Activate the Exchange Mailbox
There’s really two parts to this, one making sure the Exchange cmdlet’s are loaded and the other the actual creation of the mailbox. Turns out the creation of the mailbox is a single, simple line. The loading of the cmdlets are a little more difficult but not horribly so. There are a few ways of doing this, one is to load the Exchange server tools onto the PC/server you are running the script on–but this is quite a pain in the butt to be honest. The second method is to use Windows Remoting and actually run the commands on the Exchange server. And since I’m being honest this is the best way to do this. But I like method number 3, just because I think it’s cool. Probably not the best selection criteria, but I’m quirky like that!
What’s the 3rd method? Importing the commands from the Exchange server. This is also Windows Remoting, but what we’re doing is “importing” the commands from the Exchange server into the PC. Then when we run an imported cmdlet Powershell will automatically route them to the remote server. With an Exchange 2010 server here’s how you do it:
$ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$ExchangeServer/PowerShell/ Import-PSSession $ExchangeSession
First we create a remote session with the Exchange server using the New-PSSession cmdlet, we then load the object into a variable $ExchangeSession. Then use the Import-PSSession on our variable and voila! All of the Powershell cmdlet’s that the Exchange server has that our workstation doesn’t have are imported in and magically available to us. Very cool stuff.
I also use an Archive database and I wanted to make sure that was activated as well, but only if the “like” user has it too–to be truthful all my users will have it so I didn’t have to code it this way but I wanted to keep the flexibility of having either or. And as you can see it didn’t require much more code.
One interesting note is you can’t activate the mailbox and the Archive with a one liner. I needed to break it up into two different ones. What I did is simply check if the “like” user has Archive turned on, and if they do activate it on our new user.
$LikeMB = Get-Mailbox $LikeUser.SamAccountName Enable-Mailbox -Identity $UserName -Database $LikeMB.Database | Out-Null If ($LikeMB.ArchiveDatabase) { Enable-Mailbox -Identity $UserName -Archive -ArchiveDatabase $LikeMB.ArchiveDatabase | Out-Null }
Update the Spiceworks Ticket
Next step is actually a lot easier then it might sound. When you update the status of a ticket, Spiceworks sends out an email to everyone on the ticket and those people have the ability to reply to that email and have their response added to the ticket too. Spiceworks does this by looking at the email subject line for the text “ticket #xxxxx” where xxxxx is the ticket number. So we’re just going to send an email to the help desk with that text in the subject line and Spiceworks will helpfully pick it up and add it to our ticket.
$Body = "Account created.`n" $Body += "`nFirst Name:`t$FirstName`n" $Body += "Last Name:`t$LastName`n" $Body += "User Name:`t$UserName`n" $Body += "Email:`t$($UserName)@$EmailDomain" Send-MailMessage -To $SWEmail -From $From -Subject "RE: [Ticket #$Ticket]" -Body $Body -SmtpServer $SMTPServer
Just create a string with the information we want in it, then use the Send-MailMessage cmdlet to send the email.
Full Code
The obvious question now is, why haven’t I posted the full code? The answer is simply every environment is different and I’m not sure how useful my full source code would be to anyone because it’s so specific to my environment. I’ve posted all the snippets for the interesting parts and you should be able to modify those to work for you–maybe you have different NTFS permission requirements, maybe you have different Exchange requirements, maybe you have a different way of calculating the password–you might have noticed I didn’t include that here! But using these different parts will definitely stand as a good foundation for your own automation tasks.
I’m considering making future updates to this script to directly edit the Sharepoint custom list that HR starts too. In that list I have checklists for IT showing which steps have been completed. It’d be pretty cool to have the script automatically update those checklists as well as fill in the informational fields.
One problem I’m still having is an odd one. The very first time I run the script, the Exchange activation fails because the user object isn’t enabled. I added code for the script to wait until the user object is enabled but that doesn’t really work. But if I run the script a second time it works fine. So there’s some kind of timing thing going on there. I hate to add a generic Start-Sleep -seconds 25 but maybe that’s what will be needed.
Hope you enjoyed my little venture into New User Automation. I also have some stuff put together for User Termination so expect those posts soon.
[…] So stay tuned for Part 2! […]
[…] written about New User Automation here and here, it only made sense to start working on User Termination Automation. Read on to see how I […]