Musings of Jason Jones

Tag Archives: powershell

I recently became involved in a project to get SCCM working on all our endpoints in various DMZ’s. These servers are all in workgroups in DMZ’s so can’t use the standard method of using the Certificate MMC Snap-in to enroll in certificates (needed to encrypt the SCCM communication).

In case you aren’t aware, to do a Computer-based certificate request you need to do the following:

create an INF file that includes all the information for your certificate template

create the .cer request based off of the INF

submit it to your PKI server

download the completed cert and complete the request

export the request to a pfx file

In the case of our scenario where your DMZ box needs a certificate issued by your internal PKI you need to create and submit the request from a domain joined machine by an account with access to enroll in the certificate. Then you have to export the certificate and import it into the DMZ server directly. You also have to import your certificate chain into the DMZ machine, but that’s relatively easy and I’m not going to cover it in this article.

The rest of the commands are pretty obvious how to read from the code, but essentially it throws out a bunch of certreq commands to create and submit the certificate request and then export it to the pfx we need to ultimately copy to the new DMZ box. And then it cleans up after itself and deletes the working files.

To import our trusted root certificates into the DMZ box, run the following commands:

There’s really not a lot to this script, but with what I was trying to do I thought this was actually kind of a cool trick.

What I was trying to do was a get a list of all computers in AD with desktop operating systems. Yes, this is part of the migration series we’ve been doing because as it turns out random people have random other desktops sitting under their desk. 🙂

In addition to just pulling the computer objects I also wanted a list of the OS installed and the IP address they had registered in DNS. As you know, AD does not store the IP by itself, so since I wanted this all saved to just one array it required a little bit of trickery.

The first thing we want to do is the actual get-qdcomputer from AD, which is fairly straightforward. Then we want to pipe that to a where clause and filter on the operating system we want. Then save that to an array

This next line is where the actual “trick” comes in. I was tickled pink by the ease of doing this after all the issues I was having with adding IP to the results above. The key was to set a new array (or the same one) and just select the attributes I want. In this case, name, operatingsystem, and lastlogontimestamp. I wanted the timestamp so that I could see the last time the machine had booted on the network. Then the real key is I also told it to select the attribute ipaddress. This attribute doesn’t actually exist in the above array, but because I’m selecting it here, it actually creates that row in the new array.

$b=$b|select name,operatingsystem,lastlogontimestamp,ipaddress

Then in the next section I’m executing a flushdns at the OS level and creating a new array. The new array just keeps track of the machines that we can’t ping. For the purposes of this script it’s really not used. So then we go thru each item in $b, reset some variables, then do a wmi call out to DNS to do an nslookup on the computer name. If it gets a results it adds that IP address back into the original array. Then we just output the array.

In our scenario we had to keep creating new hires in the legacy domain so that we could get sidHistory, until we can say that we’re done and all things have been migrated.

I did it all in one script, but it’s a little big. I like it cause I did some new (to me) stuff like menus and consolidation. It’s what I wanted to do with the rest of the migration scripts, but just didn’t quite work right. If you’re migrating 100 people at once, you want to verify that everything in step 1 has worked correctly before going on to step 2.

Below is the script. I’ll try to explain as we go, but most of it is just re-doing of things we’ve done before.

This script also assumes that you still have your daily sync scripts going from source to target domain and that you’re waiting at least a day between new hire creation in legacy and migration to target. If not, you’ll need to run the prepare-mailboxmove script manually to create the MEU

I’ve now given you all my scripts I used for my AD migrations. They represent a huge amount of work on my part for writing, testing, compiling and refining them. Please use them wisely and if they help you out feel free to write a comment and let me know. I like to know I’m not just writing for myself.

Also keep in mind that you are not done. Not remotely. Now you have to go back and fix everything else you didn’t touch: DNS zones, DHCP, file share permissions, file servers, applications servers, applications, GPO’s, contacts, Sharepoint, etc, etc. Try to think of everything a user does on a daily basis and figure out if and how it needs to be migrated. You probably band-aided everything to get it working in the interim, but you still need to go back and FIX it.

To that end, what about your new hires? Hopefully you enfolded them in the migration process or at the end you’ll find out you have another 20-100 people who still need to be migrated. All because you didn’t define that process in the beginning. I know, because we ran out of time to do it on ours and had to do them all over again.

What about your remote users? Are you making them come into the office, mail their equipment in and be offline for days, or what? We did some hodge-podge process of creating a new local user account on remote PC’s and handholding them thru logging in with that, VPN in, and then migrate their computer and have them do it all over again so we could get the IP and finish the process. By the end it worked great, but 1 person could really only handle a couple of these remote users at a time.

And what are you going to do until all of the above is done? Do new hires need to be onboarded into their legacy domain or can they go directly into the new domain? Likely you’ve still got applications tied to the old domain that require sidHistory, group access or whatever, so your new users will need to come into the old and then be migrated into the new before they even start. Hopefully your onboarding process has that flexibility. (I’ll cover that powershell script in another post.)

If you’ve been following along with the process you’ll know I already covered mailbox moves. So you’re probably wondering what this post is about.

“Well, Jason,” you’re probably saying, “your process is nice enough and really saved me from having to write a lot of code, but what do I do for this users with huge mailboxes? Like, I have a few execs with 10GB mailboxes. I know I was never supposed to allow that, but, you know, they’re execs. And I have a tiny migration window.”

I’m glad you asked.

I found a nifty little switch on the mailbox move called “suspendwhenreadytocomplete”. Essentially what it does is kick off a mailbox move of a user into a temporary staging area (i.e you still have mailflow” and then when it gets to 95% it just stops. You could conceivably leave it there forever. Or days. Either one. The idea here is that that last 5% is the remaining mail items (and the changes) and then it flips the switch from MEU to mailbox and puts the mail attributes on the account in the target domain. Since I saw an average of about 20 minutes per 1GB of a mailbox during moves, I could work backward to see how much time it would take to move that mailbox and was able to kick it off early.

A couple huge caveats with this:

1. Only works from Exchange 2007 or 2010 to Exchange 2010.
2. You canNOT have Unified Messaging turned on for the user in the source domain when you start. Same as for a mailbox move. What this means is that the users won’t have voicemail until they’re actually migrated. There are ways around this using alternate names on your UM policy, but that didn’t work for us based on how we were changing things up.

We had to do a few random clean up items to get machines to work correctly post-migration. Set a few registry keys, copy some files, enable Bitlocker keys, etc.

This first one copies a couple BAT files over to the client machine and then sets an auto run registry key. Since we were completely changing out the Lync environment and using a whole new SIP we had to force the client to re-do autodiscovery

Bitlocker was a fun one. No easy Powershell way to do this, so had to run some commands, scrape the output, then run some other commands.

#setting a variable so we don't get prompted for creds in our params file. None of these need credentials.
$Creds="NO"
#setting the variables for bitlocker. I tried to build a script to poll the client for actual HDD's but it didn't work consistently without enabling WinRM on all the machines, so had to hardcode these in there. Made the script a mess, but ran out of time for a clean way to do it.
$app="manage-bde.exe"
$DriveLetters = @("C","D","E")
##call include file
. .\params.ps1
##set up an array
$bad=@()
$import=import-csv "d:\migration\admtincludes\bitlockercompincludes1.csv"
## go thru each item in the import file, then go thru each drive in the drive array
foreach ($item in $import){
$computer=$item.computer
foreach ($drive in $driveletters){
#set up our variables. Re-null out some and configure drives
$a=$null
$b=$null
$c=$null
$key=$null
$getparams=$null
$putparams=$null
$share=$drive+"$"
$Bdrive=$drive+":"
## test the drive to see if it's good. most computers only have C$. If they have D$ or E$ this will run the commands against those drives
if ($(Test-path "\\$computer\$share")){
## Set up our param list for the manage-bde command
## i.e. manage-bde.exe -cn MYcomp -protectors -delete C: -type recoverypassword
## deletes the protectors on mycomp's c:
$Parameters =@("-cn","$computer","-protectors","-delete","$Bdrive","-type","recoverypassword")
## Run the command. Since we'red doing an executable we must do it this way
& $App $Parameters
##basically the same as above, but we now want to add the recovery key back into the new AD
## i.e. manage-bde.exe -cn mycomp -protectors -add C: -recoverypassword
$Parameters = @("-cn","$computer","-protectors","-add","$Bdrive","-recoverypassword")
& $App $Parameters
##sometimes the above command doesn't work, so as a backup we want to tell it backup the recovery key to AD
##this requires getting the current key first
##i.e. manage-bde.exe -cn mycomp -protectors -get C:
$GetParams=@("-cn",$Computer,"-protectors","-get",$Bdrive)
##store the results into a variable
$Result=& $app $GetParams
##browse the results for the line we're looking for
##We'll get a couple lines back one is the DRA and one is the actual ID for the drive that we need
$a=$result|foreach-object {if($_ -match "ID") {$_}}
if ($a){
##let's manipulate the data
##get the last line in the data that has ID in it
$b=$a[-1].trim()
##convert it to a string and then split each space, creating an array of the results. I.e. "ID:" is [0] and the GUID is the [1]. "Password" is [2] and the password is [3]
$c=$b.ToString().split(' ')
##set the key to the last item (i.e. the password)
$Key=$c[-1]
##rewrite the params so that we can backup the key
##ie. manage-bde.exe -cn mycomp -protectors -adbackup C: -ID 11111-1111-....
$PutParams=@("-cn",$Computer,"-protectors","-adbackup",$Bdrive,"-ID",$key)
& $app $putparams
}
}
##write out computers we couldn't connect to
ELSE {$BAD+=$Computer}
}
}
$bad|sort|unique|out-file "d:\migration\admtincludes\BLOutput.txt"

This next one is pretty straightforward. We’re just creating a remote session to Exchange and then enabling Unified Messaging with the appropriate settings. Again, this is where our import file is super important as it has to have all the relevant information in it.