OS Deployments useing WinPE + PowerShell and WindowsImageTools
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Jones, David
When you don't have expensive deployment tools PowerShell can be used as a down and dirty way to automating OS deployments. We will explore creating a PowerShell enabled WinPE image and the ins and outs of the WindowsImageTools module.
 I'm David Jones. I'm going to be going over some PowerShell with WinPE and does anyone know what Polaris is? Yeah, I didn't expect you guys to do the PowerShell team. Polaris is a fairly lightweight PowerShell-based, how is it, REST method, ability to create those. So I have that in here because I ran into a problem I wasn't expecting while trying to get this initial setup. So the problem that initiated this whole thing was, you know, we have, for our server deployments, we didn't have MDT. The system they did have for deploying did not support UEFI natively. And so we couldn't use it for imaging. And so for the longest time, everyone's still been using, you know, stick the CD in the machine and load the windows. So I also wanted to make sure I had a PE image where I did not have to re-image it with the process changed. I didn't want to just create the ISO and be done with it. So the goal here for this presentation is to take a, while this was intended for both virtual or something where you can't lay down the OS from outside the machine, kind of like VMware, I'm still going to be doing it all with Hyper-V VMs. But the goal here was to build a Hyper-V server that's got a boot drive and a data drive, join it to the domain, and have it kick off a bootstrap. So it's going to actually, if we get that far, it'll actually go ahead and build another VM. So, oops, that was not right. Next one. So here's the setup I've got. All right. So I've got a domain controller, certificate authority, a program server where I can upload my scripts and modules. That's running under IIS. And so I'm using the certificate authority, so that's going to be HTTPS. So I've got to have my PE be able to trust the certificate. And then I've got my Polaris server, and it is running. It's there because PE, while it has the module in place for PowerShellGet, it doesn't actually have everything that's needed to make it work. So I needed a way to be able to find the location of my build script and the modules that I need without having to hard code that in my PE boot script. I also didn't want to have to, at one point I was actually using a redirector off of an IIS spot, say, okay, you hit this, redirect, send a point to it. But every time I versioned a thing, I had to keep updating it. So I ended up creating a small Polaris thing to handle that. So let's go ahead and get started. So first, I needed to build the WinPE. So I'm going to create, I knew that was going to happen. And I always work when you're first getting set up. Okay, is that readable? So it needs to be bigger. Okay. Okay, so... Now, normally, most people, they're going to go through and they're going to highlight and run a line. And because of some certain issues here, it actually works more often when you do it that way, but there's a certain kevat I wanted to cover. So I'm going to run this as a separate script. So we're going to go through some of the code here. So basically, I'm going to create this temp PE folder. I'm going to be copying in the files here from the assessment and deployment kit that are needed for the WinPE. And we're going to get the boot whim. Now, if you're not familiar with the PE, the boot whim is basically the Windows image that glows into the RAM drive that you're actually running under. That comes up as your X drive. And I also, I'm not too fond of having to press a key to get it to boot from the media. So I'm going to be using the no prompt, the ea-fices. Okay. From there, we're just going to do a mount on the boot. And then we're going to add WMI.net, scripting host, PowerShell. And since we're going to be dealing with images, we're going to add a dism and some storage one. I'm not too sure if those bottom two are actually required for this. All right. Then to actually, to get it so the WinPE image will trust the certificate when it goes to talk to the web server, I have to include the certificate into the image. So here I've got this huge byte array and the thumbprint of it. And I'm going to mount the registry off of the, out of the mounted whim. And then I'm going to add these two registry keys. Now, this is where I got stuck. This worked perfectly for dismounting and saving out the whim after, and I ran it one line by line. But as soon as I ran it as a script, it blew up every time and left me with an orphaned mount point. It turns out that inside of a script, the new item when dealing with the registry PS drive doesn't close its file handle properly until the script ends. So I had to run the forcefully call that the close method and then run the garbage collector before I could unload the registry. And then part of WinPE is it runs a batch file called start net. So I'm going to add an additional line in there to start up PowerShell. And to run my PS1 file here, it has a couple of functions. I got the right color here just because I want to highlight certain commands because the technician running it might not always know, or might not just read everything, but... All right, so here I've got the git build script. We'll come back to that one. That's going to actually download and talk to Polaris and download the file. So I'm going to ask it... So I need to make sure my path includes the script's location. So if you've ever done the install script, usually the first time on a machine is going to ask you to make sure it updates the path. So I'm going to handle that and then I'm going to get this new branch server script off of my internal gallery. And as long as that works from there, I'm just going to... So if you notice, instead of using a here string, I see a lot of people do that in the ScriptsayPost online. I used a script block for the function I'm going to write to a file because it's that content, thanks to the way they are always changing how files work, it'll take a script block for the content. And it gives me the highlighting and whatnot. That doesn't run. So I'm going to go ahead and unmount it and then we'll create an image. So right now, I do not have that folder here at all. Go ahead and... This takes a little bit. So while that's running... Okay, so get build script. What I'm going to do with this one is I need to be able to put together the request I'm going to send to the REST API. And I probably could have done it with invokeREST method, but the URI I'm familiar with, so that's what I did it with. So basically, I'm just going to take the output... I've got the output path here from the script name. And if they don't supply the path, I'm going to put that one together. And then the default URI is just going to be this get package download, the script name. And if it's repository is supplied, then I'll add that into it. So if you're familiar with the packages, the new get packages are just a zip file with a certain file structure. So I'm going to unzip that, put it where it needs to go, and then we'll be able to run that. Let's see how this is coming along. Yeah. Thought net always takes the longest install. I sure hope it didn't hang. Yeah, there she goes. Okay. So all the packages are the... all the cab files added here, and then certificate done. It's got a mountain saving. And we're done. So end result, I've got this folder and I've got an ISO. So let's actually attach that. And then we're going to run on. This is probably going to fail. It's realized I didn't set it to start the Polaris yet. Thought I'd save some time by going on. So there, download failed because Polaris service wasn't actually running. So let's get to that. Come on, type. Start over. Come on. All right. So here's my script for setting up Polaris. So it's going to... well, transcript is running. I'm going to log so I can help with debugging there. But we got to register the gallery that I'm going to be using. You know, galleries are per user configuration. So the service is going to be running under, needs to have that preset up. So I'm going to say that the path is get package download like we saw before. And I'm going to set my default responses of nothing found and then grab these different query parameters. So I've got the repository name. Throw an error if you're trying to give it both a script and a module because you can't do both at the same time. Then I'm going to take from a repository configuration based on what repository name was handled to it and get it's either the script source or just the source location for my URL and then I'm going to do a find module or script and grab the version number and from there I'm able to do a status code 303 and give it a location that is just the source location slash package slash package name and version. And then below that it's going to create a scheduled task that runs the system at the start up of the system. So anytime this server gets rebooted it will just automatically restart the Polariser. Now I had to add just because I'm on a laptop, you'll have on battery is that totally screwed up my thing as soon as I unplugged the power everything broke. So there is our task. So we're better. So this time it worked tells you how to start the new branch server. Actually before I do that I should just show you what I got here. I've got two disks. They're online. The only volume is the disk I'm on. They're empty. So it goes up and it's going to go get some additional functions and then it's going to ask me some stuff. I'm going to get this kicked off and then we're going to go back and look at the code on it. So I've got a thing in here for standardizing the computer names. So they have to put in some information. This is kind of made up so this would be the sales at branch 120. It's the local admin password and password to a network account. You can join it to the domain. And off she goes. All right. So here's my new branch server script. I've got to get required modules. It looks a lot like the one for function is just going to query Polaris. It gets the redirect downloads and extracts it to the correct location. I'm going to import those. That's where I'm querying the data. It checked the drive. You know, is the boot disk empty? If so, we're going to need this information collected. And it's going to go in and is the data drive empty. So it looks to the largest disk. It says, you know, there's nothing on it. So doing it, this is where the Windows image tools comes in. This module is something I built a couple of years ago. It was intended really for handling the fact that Server 2012 core didn't have most of the content in it. And if you applied any patches, you then couldn't use the ISO or the other WIM to reload any of the different features into the system. So that's kind of what got me started on messing with images. And so I ended up rewriting it to not just work with the WIMs and virtual drives to actually work with physical drives here. So it's going to initialize the disk partition. I'm telling it I want a data layout, which just means one big partition. And I'm going to format it into FS. And now that I've got some place I can write files, I'm going to grab off of the Polaris server using the credentials I had previously given it. I'm going to grab my WIM file. This is just copied off of the CD at this point. I could just as easily use a custom image. And then again, it's going to look, is the boot disk empty? Then I'm going to create an unattended. So for that, I need the local parameters, some basic stuff, the account to join where I want to put it. And here's where the interesting part is. The first boot script is during when Windows first goes and starts up that first time, it's checking all the hardware, figuring out what drivers and stuff it needs to do. You can run commands at that point. I think they call them synchronous commands in the XML. And from that, I'm going to put in a simple script that is going to create a scheduled task to run every time the machine boots up. And that's what I'm going to use to bootstrap. So that script can then check the status of each thing, restart it if necessary, start back up, go back down as checklist. And when it's done, remove the scheduled task. So from there, I've created, just for kicks, I'm kicking out a couple of different unattends. And then here I've got the parameters I'm going to use to actually populate the disk. So I've got my source disk, the disk number I'm targeting, which will be the boot disk of the drive. And then using the UEFI layout, because if you're familiar with disk imaging, there's the master boot record and the GPT partition. And when you're booting with UEFI, you've got to have some funky different partitions and stuff on it. So I'm going to have it automatically add these features. So I've got Hyper-V, the PowerShell Management, and SNMP, and remove the SMB1 protocol, which I'm not entirely sure is in the image that I'm using by default, but just want to make sure it's gone. And because those features are all present on the image I'm pulling, it's going to, I'm not going to give it a feature source to add anything else. I would have to do that if I was trying to add.NET 3.5 from the beginning. And because I didn't specify, it's going to grab index number one out of the WIM file. So I've got the, I've also got a function here, because in our environment, if we were doing this on a physical machine, we might be using a lifecycle controller that provides a virtual disk that has all the drivers needed. So this is going to find those, and if they're there, it's going to include them as part of the installation. So and then it splits all that to the install from WIM. And that is, that is the huge center of a lot of, of the Windows image tools. So we'll see some of the verbose output of that in a minute. And then I create the file structure. I'm going to copy a few files. I've got my first boot script and the boot scrap that I'm going to inject in those. And I've got this other script that's actually going to create them. So that first boot script, all it's really doing is, because it's during that initial boot, the specialization phase, none of the services have started up. So I've got to start up the scheduling service. And then again do the fact, I'm on a laptop. I had to do this with XML just so that I could turn on the, the battery function. So the thing would actually start. And then because PowerShell functions don't work at this point in Windows lifespan, the, I'm just going to use the sketch task to load that. I'm also doing a quick disk part script to take the disk online. Because the data disk, because it was built by another operating system, and then when I'm just laying down, it's going to come up as a foreign disk and be offline. Oops. What did I just do? All right. So. Then it's going to do another one where it gets to build scripts. So I'm going to go back out to Polaris and then over to the NuGet server and download that bootstrap file. So let's see where this is at. It's like it's done. Oh, come on. There we go. Here's where the Windows image tools loaded. It detected it's on a PE and that the VHD commandments weren't available. But it's going to use a.NET class to handle some functionality. We're not really using the VHDs at this point, so that doesn't really matter. So it's going to create, here's the unattended I created for this OS. And then I threw a couple other ones out there for the file and print server and a client. And so here's it showing the parameters that were passed in to set this partition up. Everything we're going to put in there. And so it's going to initialize it as a GPT, create the EFI partition. It's Fat32. That's where the boot from file goes. The Microsoft reserved. And then of course the Windows partition with all the space. Now is there anything missing from that list? It's recognized. A lot of Microsoft's documentation for formatting the drives includes like recovery tools and recovery image, those partitions. They're not required. And my function, this function could do that if I specifically told it I wanted the recovery image done on my physical disk so I can at least get to that. Half the time if the machine's that poached you're starting over anyway. So I've chosen not to include those on this. So it's going to show me here's my, I got an eDrive, a system and fDrive with basic. That's the Windows. And this is going to go ahead and lay it all down with the, applying the image here. All right. It's going to skip the source pass because I told it none. No source. Here's where it's removing the feature. And then it's going to apply PCD boot and give me a, that's going to set up the UEFI boot space. So just so I don't mess with drive letters here, I'm going to go ahead and remove this. Now in our environment, if we're doing this with a physical machine, we don't have a physical CD in it anymore. So the virtual thing that's mounted through like the lights out of the iDRAC is going to be disabled before we reboot. So I just don't want to confuse any of my scripts here. Any questions so far? I don't know. There's little bits and pieces I have to, problems I've ran into before. Like I know Microsoft has their script for converting a WIM into VHD that they'd put out there for Nano and stuff, which is all, it was all in a script. And I was like, this would work a lot better as a module. Well, it turns out in the module scope, if you change drive letters or do anything like that, it's not visible to anything that, outside of it, unless you run a couple of things. It's actually a bug that's been, it was reported on Connect and then on to User Voice. I don't know if there's one out there yet on GitHub, but I don't know if it's a design that way or what, but that's one problem I ran into. So once this VM comes up, it is going to run this bootstrap. The first thing I'm going to do is I'm going to make sure that I've got NuGet installed. So I'm going to look for the actual DLL because if I try running the provider function, it's going to try to go out to the internet and reach for it. So I want to avoid that. So I'm going to check for the actual file it creates from that register NuGet. And of course, the gallery for this domain. And update the scripts path. And so it's going to go through a lot of the same things here. It's going to look at, now first we've got to take that disk while the other thing should have brought it online. It didn't flag it for writable. So I have to make sure that that gets handled too so that the foreign disk now becomes writable here. Normally I would also have it do Windows updates and also it could do a driver package there if the drivers haven't been done. So it's also going to check, do I have the WIM file on my data driver? So I don't have that. These other, a lot of these functions on here are part of another module I had put together just for our environment. So most of it's been sanitized so that I can publish it. So actually all the source code here I will get uploaded as soon as I've made sure I've removed anything that shouldn't be in there so that you guys can take a look. I'm going to set some basic hyper-v settings and create a script and from there hopefully create a VHD and another VM. So this thing is definitely restarted. Okay, so where is that? See where it's at. Yeah, whichever is here I want to use, And here's the other band. So let's see where it's at. A lot of the same stuff going through clearing the disk, setting the partitions, and it looks like it is still on applying the web image for that VM. Now this is a little different because it's applying it into a VHD. So some of the sub-functions from what we're called, they build the VHD, set its partitions, and then lay down the OS based on that. So we're waiting on that. Alright. Is everyone familiar with the end-of-ten? Yeah. XML is not fun. So, yeah, one of the Windows Image Tools function is a new on-a-ten XML. And that will, it doesn't do everything because there's a million things you can do in the end-of-ten, but it takes care of a lot of the basics. In this particular case, we wanted to join the domain, so it leaves my super secret password out there. Not always the best, but I haven't had much experience or luck with the whole creating an offline join and using that, which probably would be a little more secure in this, but it also requires you to have other stuff done in advance that maybe the technician doing it doesn't. But in our environment, if an on-a-ten is found on a machine, it's immediately deleted by group policy anyway. So, I've seen a lot of people with their examples for on-a-ten. They say, like, take this and then do a fine and replace if you're 32-bit or 64-bit. Well, it turns out they can live side by side. So, in this case, it's a complete duplicate. One is the X86 and the other one's the 64. And the OS will just run whichever one it needs, except when you're dealing with tasks. So, if I'm going to run a synchronous command, it will, the 32-bit will run the 32-bit section. The 64-bit will run both the 32 and the 64-bit section. Everything will run twice. So, since it doesn't actually trigger a 32 versus 64-bit process, it just runs whatever command it is, I only have to include those once. So, here's where that first boot script is handled. I've, since I only asked for the path for that particular function, I've pre-populated that the rest of this with the starting up PowerShell. And I also, there's some also parameters to set your own stuff if you wanted to create anything specific. It's different. You know, it's just mostly right now, it's just the basics, input locale, and the parameters to make it so that it doesn't ask you for anything, keep it pretty clean. Here's where it set the password for the admin. So, and that's still going. Well, anyway, so I'm hoping some of these things are, the little gems in here that you guys might find useful for stuff that you might do with either imaging or just handling that first boot. I mean, I, being able to have something bootstrap and kick off right as the OS is coming up, I found fairly good. Now, I know there are ways to bootstrap DSC. I never had very much luck with it. A script usually works better. So this is a great way to have that bootstrap kick off a DSC config or some other automated process that you guys want to just, you know, not have to have someone do manually. So, I, suckers just turning along, isn't it? Doesn't look very busy. All right, well, does anyone got any questions or anything else? Yes? I don't remember the name of the website, but just a few words of the website that helped you create the on-app end ectomo. That's slacking and all those things. Oh, yeah. I'm going to just spit it out for you and then I'll use it for the whole story. Yeah, the reason for the PowerShell one was just so that I could script the process. You know, you can't script a website as easily. If they've got a nice REST method, that's great. But, yeah, if you're trying to do something really complicated, something like that's probably covered every possible option. I don't see this going there. This is primarily just for getting the basics up and then handling a lot of the other settings can be done after the fact. So, yeah? So, after this image is aligned, you're currently on the rules matter, so I'm going to bring it up a little bit more. I think it would be excellent for anything like that. The bootstrap could kick off anything from that. So, after that scheduled task I'm building during the initial specialization phase, that could start anything. That scheduled task then kicks off the PowerShell script and anything I need to do beyond that. I mean, this is a demo, so I was just doing something simple. I mean, it could do all sorts of stuff from software installations to, you know, I don't know, configuration, registering it with your config stuff, getting its certificate or anything like that. So, you know, it's PowerShell. Anything PowerShell can touch. You know, we can manage it somehow. And while I did this with Hyper-V, I could do the same thing with VMware. With Hyper-V, I would normally just, I'd use what I'm doing here inside this host. I would just go ahead and write out the VHD directly. Yes? What do you use when you're briefing your blog lawyers and you have their own protection? Almost every script has a start transcript at the top. And in this case, just to be standardized, the, I've created this folder under the program data. And inside it, I've got one where I store my scripts and where I store my logs. So I just have a standardized way which we have. These are where we're going to write, put any scripts that aren't grabbed off of our internal gallery. Or here's a spot for the logs. I was actually looking at having certain steps in this is going to kick off an email over to our Teams channel. So that's working in that direction to have some sort of notification. But we don't really have a lot of sophistication in certain parts of our environment. So, you know, it would be possible to put whatever you want to have it check in, let you know how things are going. I could just as easily do that with a Polaris thing where I have it write to a log, just go ahead and hit that endpoint. Have it either shoot out an email, update it, hit database, something, so that I can see the status of what things are at, how far they are, you know. We write our logs instead of going to the local or the local. The first thing we do is we create a map to a central share and we actually write the logs to our field and we send our shares slowly. Well, once this is on a domain, you know, group policy is going to set off on that first boot up and it's going to set our transcript log location and our log forwarding and everything. So we capture all the stuff that I'm pretty sure Lee talked about this morning. I didn't see his talk but I've seen plenty of his other ones, so yeah, we're trying to capture everything we can. Oh, the scheduled tasks during the WinPE portion, something's not there. I can start the scheduled service but the new scheduled tasks, PowerShell functions just don't work at that point. But that's just because we're in that initialization phase. Who knows what's available. The scheduled task walking was probably not available during the state route that shipped through the outbreak system. Yeah, I mean a lot of things are missing in PE but this is at the point, it's the full OS, it's booting up but it's doing the specialization phase. Most of the services are not running. So I know the scheduled task was needed. I don't know why the other part is. It's probably the top of my mind might not be running. I don't know. Do you know what version of the power shelling is running? I don't know. This is the one I downloaded last month. So I suppose we can do that. We'll just bail this. Oh, come on. Of course. That way it'll actually have newer stuff. At least having something is a lot better because otherwise you're stuck with whatever a vendor of your imaging tool or whatever you can manage to do with batch files. So that's the version. I don't know. I don't know. You start the ADK and then inside that it's going to download the WIM and everything else and you just go with whatever's there. And honestly, I'm not entirely sure how to tell it to refresh and get the new one. I just end up ripping the thing out and resetting it up. So I mean, most of the time it works. I mean, like the Windows update function I had in there, it was working great until November and then something changed. It was using the same code you used for like a nano and those don't work anymore. They do not work in Windows 10 and 2016. It's like, why did they have to break that? So I got to get a lot more creative with Windows update. Anyway, that's all the time I've got. So thank you guys for coming.
 I'm David Jones. I'm going to be going over some PowerShell with WinPE and does anyone  know what Polaris is? Yeah, I didn't expect you guys to do the PowerShell team. Polaris  is a fairly lightweight PowerShell-based, how is it, REST method, ability to create  those. So I have that in here because I ran into a problem I wasn't expecting while trying  to get this initial setup. So the problem that initiated this whole thing was, you know,  we have, for our server deployments, we didn't have MDT. The system they did have for deploying  did not support UEFI natively. And so we couldn't use it for imaging. And so for the longest  time, everyone's still been using, you know, stick the CD in the machine and load the windows.  So I also wanted to make sure I had a PE image where I did not have to re-image it with the  process changed. I didn't want to just create the ISO and be done with it. So the goal here  for this presentation is to take a, while this was intended for both virtual or something  where you can't lay down the OS from outside the machine, kind of like VMware, I'm still  going to be doing it all with Hyper-V VMs. But the goal here was to build a Hyper-V server  that's got a boot drive and a data drive, join it to the domain, and have it kick off  a bootstrap. So it's going to actually, if we get that far, it'll actually go ahead  and build another VM. So, oops, that was not right. Next one. So here's the setup I've  got. All right. So I've got a domain controller, certificate authority, a program server where  I can upload my scripts and modules. That's running under IIS. And so I'm using the certificate  authority, so that's going to be HTTPS. So I've got to have my PE be able to trust the  certificate. And then I've got my Polaris server, and it is running. It's there because  PE, while it has the module in place for PowerShellGet, it doesn't actually have everything that's  needed to make it work. So I needed a way to be able to find the location of my build  script and the modules that I need without having to hard code that in my PE boot script.  I also didn't want to have to, at one point I was actually using a redirector off of an  IIS spot, say, okay, you hit this, redirect, send a point to it. But every time I versioned  a thing, I had to keep updating it. So I ended up creating a small Polaris thing to handle  that. So let's go ahead and get started. So first, I needed to build the WinPE. So I'm  going to create, I knew that was going to happen. And I always work when you're first  getting set up. Okay, is that readable? So it needs to be bigger. Okay. Okay, so... Now,  normally, most people, they're going to go through and they're going to highlight and  run a line. And because of some certain issues here, it actually works more often when you  do it that way, but there's a certain kevat I wanted to cover. So I'm going to run this  as a separate script. So we're going to go through some of the code here. So basically,  I'm going to create this temp PE folder. I'm going to be copying in the files here from  the assessment and deployment kit that are needed for the WinPE. And we're going to get  the boot whim. Now, if you're not familiar with the PE, the boot whim is basically the  Windows image that glows into the RAM drive that you're actually running under. That comes  up as your X drive. And I also, I'm not too fond of having to press a key to get it to  boot from the media. So I'm going to be using the no prompt, the ea-fices. Okay. From there,  we're just going to do a mount on the boot. And then we're going to add WMI.net, scripting  host, PowerShell. And since we're going to be dealing with images, we're going to add  a dism and some storage one. I'm not too sure if those bottom two are actually required  for this. All right. Then to actually, to get it so the WinPE image will trust the certificate  when it goes to talk to the web server, I have to include the certificate into the image.  So here I've got this huge byte array and the thumbprint of it. And I'm going to mount  the registry off of the, out of the mounted whim. And then I'm going to add these two  registry keys. Now, this is where I got stuck. This worked perfectly for dismounting and saving  out the whim after, and I ran it one line by line. But as soon as I ran it as a script,  it blew up every time and left me with an orphaned mount point. It turns out that inside  of a script, the new item when dealing with the registry PS drive doesn't close its file  handle properly until the script ends. So I had to run the forcefully call that the close  method and then run the garbage collector before I could unload the registry. And then  part of WinPE is it runs a batch file called start net. So I'm going to add an additional  line in there to start up PowerShell. And to run my PS1 file here, it has a couple of  functions. I got the right color here just because I want to highlight certain commands  because the technician running it might not always know, or might not just read everything,  but... All right, so here I've got the git build script. We'll come back to that one.  That's going to actually download and talk to Polaris and download the file. So I'm  going to ask it... So I need to make sure my path includes the script's location. So  if you've ever done the install script, usually the first time on a machine is going to ask  you to make sure it updates the path. So I'm going to handle that and then I'm going to  get this new branch server script off of my internal gallery. And as long as that works  from there, I'm just going to... So if you notice, instead of using a here string, I  see a lot of people do that in the ScriptsayPost online. I used a script block for the function  I'm going to write to a file because it's that content, thanks to the way they are always  changing how files work, it'll take a script block for the content. And it gives me the  highlighting and whatnot. That doesn't run. So I'm going to go ahead and unmount it and  then we'll create an image. So right now, I do not have that folder here at all. Go  ahead and... This takes a little bit. So while that's running...  Okay, so get build script. What I'm going to do with this one is I need to be able to  put together the request I'm going to send to the REST API. And I probably could have  done it with invokeREST method, but the URI I'm familiar with, so that's what I did it  with. So basically, I'm just going to take the output... I've got the output path here  from the script name. And if they don't supply the path, I'm going to put that one together.  And then the default URI is just going to be this get package download, the script name.  And if it's repository is supplied, then I'll add that into it. So if you're familiar with  the packages, the new get packages are just a zip file with a certain file structure.  So I'm going to unzip that, put it where it needs to go, and then we'll be able to run  that. Let's see how this is coming along. Yeah. Thought net always takes the longest  install.  I sure hope it didn't hang. Yeah, there she goes.  Okay.  So all the packages are the... all the cab files added here, and then certificate done.  It's got a mountain saving.  And we're done.  So end result, I've got this folder and I've got an ISO. So let's actually attach that.  And then we're going to run on.  This is probably going to fail. It's realized I didn't set it to start the Polaris yet.  Thought I'd save some time by going on. So there, download failed because Polaris service  wasn't actually running. So let's get to that. Come on, type.  Start over.  Come on.  All right. So here's my script for setting up Polaris. So it's going to... well, transcript  is running. I'm going to log so I can help with debugging there. But we got to register  the gallery that I'm going to be using. You know, galleries are per user configuration.  So the service is going to be running under, needs to have that preset up. So I'm going  to say that the path is get package download like we saw before. And I'm going to set my  default responses of nothing found and then grab these different query parameters. So  I've got the repository name. Throw an error if you're trying to give it both a script  and a module because you can't do both at the same time. Then I'm going to take from  a repository configuration based on what repository name was handled to it and get it's either  the script source or just the source location for my URL and then I'm going to do a find  module or script and grab the version number and from there I'm able to do a status code  303 and give it a location that is just the source location slash package slash package  name and version.  And then below that it's going to create a scheduled task that runs the system at the  start up of the system.  So anytime this server gets rebooted it will just automatically restart the Polariser.  Now I had to add just because I'm on a laptop, you'll have on battery is that totally screwed  up my thing as soon as I unplugged the power everything broke.  So there is our task.  So we're better.  So this time it worked tells you how to start the new branch server.  Actually before I do that I should just show you what I got here.  I've got two disks.  They're online.  The only volume is the disk I'm on.  They're empty.  So it goes up and it's going to go get some additional functions and then it's going to  ask me some stuff.  I'm going to get this kicked off and then we're going to go back and look at the code  on it.  So I've got a thing in here for standardizing the computer names.  So they have to put in some information.  This is kind of made up so this would be the sales at branch 120.  It's the local admin password and password to a network account.  You can join it to the domain.  And off she goes.  All right.  So here's my new branch server script.  I've got to get required modules.  It looks a lot like the one for function is just going to query Polaris.  It gets the redirect downloads and extracts it to the correct location.  I'm going to import those.  That's where I'm querying the data.  It checked the drive.  You know, is the boot disk empty?  If so, we're going to need this information collected.  And it's going to go in and is the data drive empty.  So it looks to the largest disk.  It says, you know, there's nothing on it.  So doing it, this is where the Windows image tools comes in.  This module is something I built a couple of years ago.  It was intended really for handling the fact that Server 2012 core didn't have most of  the content in it.  And if you applied any patches, you then couldn't use the ISO or the other WIM to reload any  of the different features into the system.  So that's kind of what got me started on messing with images.  And so I ended up rewriting it to not just work with the WIMs and virtual drives to actually  work with physical drives here.  So it's going to initialize the disk partition.  I'm telling it I want a data layout, which just means one big partition.  And I'm going to format it into FS.  And now that I've got some place I can write files, I'm going to grab off of the Polaris  server using the credentials I had previously given it.  I'm going to grab my WIM file.  This is just copied off of the CD at this point.  I could just as easily use a custom image.  And then again, it's going to look, is the boot disk empty?  Then I'm going to create an unattended.  So for that, I need the local parameters, some basic stuff, the account to join where  I want to put it.  And here's where the interesting part is.  The first boot script is during when Windows first goes and starts up that first time,  it's checking all the hardware, figuring out what drivers and stuff it needs to do.  You can run commands at that point.  I think they call them synchronous commands in the XML.  And from that, I'm going to put in a simple script that is going to create a scheduled  task to run every time the machine boots up.  And that's what I'm going to use to bootstrap.  So that script can then check the status of each thing, restart it if necessary, start  back up, go back down as checklist.  And when it's done, remove the scheduled task.  So from there, I've created, just for kicks, I'm kicking out a couple of different unattends.  And then here I've got the parameters I'm going to use to actually populate the disk.  So I've got my source disk, the disk number I'm targeting, which will be the boot disk  of the drive.  And then using the UEFI layout, because if you're familiar with disk imaging, there's  the master boot record and the GPT partition.  And when you're booting with UEFI, you've got to have some funky different partitions  and stuff on it.  So I'm going to have it automatically add these features.  So I've got Hyper-V, the PowerShell Management, and SNMP, and remove the SMB1 protocol, which  I'm not entirely sure is in the image that I'm using by default, but just want to make  sure it's gone.  And because those features are all present on the image I'm pulling, it's going to,  I'm not going to give it a feature source to add anything else.  I would have to do that if I was trying to add.NET 3.5 from the beginning.  And because I didn't specify, it's going to grab index number one out of the WIM file.  So I've got the, I've also got a function here, because in our environment, if we were  doing this on a physical machine, we might be using a lifecycle controller that provides  a virtual disk that has all the drivers needed.  So this is going to find those, and if they're there, it's going to include them as part  of the installation.  So and then it splits all that to the install from WIM.  And that is, that is the huge center of a lot of, of the Windows image tools.  So we'll see some of the verbose output of that in a minute.  And then I create the file structure.  I'm going to copy a few files.  I've got my first boot script and the boot scrap that I'm going to inject in those.  And I've got this other script that's actually going to create them.  So that first boot script, all it's really doing is, because it's during that initial  boot, the specialization phase, none of the services have started up.  So I've got to start up the scheduling service.  And then again do the fact, I'm on a laptop.  I had to do this with XML just so that I could turn on the, the battery function.  So the thing would actually start.  And then because PowerShell functions don't work at this point in Windows lifespan, the,  I'm just going to use the sketch task to load that.  I'm also doing a quick disk part script to take the disk online.  Because the data disk, because it was built by another operating system, and then when  I'm just laying down, it's going to come up as a foreign disk and be offline.  Oops.  What did I just do?  All right.  So.  Then it's going to do another one where it gets to build scripts.  So I'm going to go back out to Polaris and then over to the NuGet server and download  that bootstrap file.  So let's see where this is at.  It's like it's done.  Oh, come on.  There we go.  Here's where the Windows image tools loaded.  It detected it's on a PE and that the VHD commandments weren't available.  But it's going to use a.NET class to handle some functionality.  We're not really using the VHDs at this point, so that doesn't really matter.  So it's going to create, here's the unattended I created for this OS.  And then I threw a couple other ones out there for the file and print server and a client.  And so here's it showing the parameters that were passed in to set this partition up.  Everything we're going to put in there.  And so it's going to initialize it as a GPT, create the EFI partition.  It's Fat32.  That's where the boot from file goes.  The Microsoft reserved.  And then of course the Windows partition with all the space.  Now is there anything missing from that list?  It's recognized.  A lot of Microsoft's documentation for formatting the drives includes like recovery tools and  recovery image, those partitions.  They're not required.  And my function, this function could do that if I specifically told it I wanted the recovery  image done on my physical disk so I can at least get to that.  Half the time if the machine's that poached you're starting over anyway.  So I've chosen not to include those on this.  So it's going to show me here's my, I got an eDrive, a system and fDrive with basic.  That's the Windows.  And this is going to go ahead and lay it all down with the, applying the image here.  All right.  It's going to skip the source pass because I told it none.  No source.  Here's where it's removing the feature.  And then it's going to apply PCD boot and give me a, that's going to set up the UEFI  boot space.  So just so I don't mess with drive letters here, I'm going to go ahead and remove this.  Now in our environment, if we're doing this with a physical machine, we don't have a physical  CD in it anymore.  So the virtual thing that's mounted through like the lights out of the iDRAC is going to  be disabled before we reboot.  So I just don't want to confuse any of my scripts here.  Any questions so far?  I don't know.  There's little bits and pieces I have to, problems I've ran into before.  Like I know Microsoft has their script for converting a WIM into VHD that they'd put  out there for Nano and stuff, which is all, it was all in a script.  And I was like, this would work a lot better as a module.  Well, it turns out in the module scope, if you change drive letters or do anything like  that, it's not visible to anything that, outside of it, unless you run a couple of things.  It's actually a bug that's been, it was reported on Connect and then on to User Voice.  I don't know if there's one out there yet on GitHub, but I don't know if it's a design  that way or what, but that's one problem I ran into.  So once this VM comes up, it is going to run this bootstrap.  The first thing I'm going to do is I'm going to make sure that I've got NuGet installed.  So I'm going to look for the actual DLL because if I try running the provider function, it's  going to try to go out to the internet and reach for it.  So I want to avoid that.  So I'm going to check for the actual file it creates from that register NuGet.  And of course, the gallery for this domain.  And update the scripts path.  And so it's going to go through a lot of the same things here.  It's going to look at, now first we've got to take that disk while the other thing should  have brought it online.  It didn't flag it for writable.  So I have to make sure that that gets handled too so that the foreign disk now becomes writable  here.  Normally I would also have it do Windows updates and also it could do a driver package there  if the drivers haven't been done.  So it's also going to check, do I have the WIM file on my data driver?  So I don't have that.  These other, a lot of these functions on here are part of another module I had put together  just for our environment.  So most of it's been sanitized so that I can publish it.  So actually all the source code here I will get uploaded as soon as I've made sure I've  removed anything that shouldn't be in there so that you guys can take a look.  I'm going to set some basic hyper-v settings and create a script and from there hopefully  create a VHD and another VM.  So this thing is definitely restarted.  Okay, so where is that?  See where it's at.  Yeah, whichever is here I want to use,  And here's the other band.  So let's see where it's at.  A lot of the same stuff going through clearing the disk, setting the partitions,  and it looks like it is still on applying the web image for that VM.  Now this is a little different because it's applying it into a VHD.  So some of the sub-functions from what we're called, they build the VHD,  set its partitions, and then lay down the OS based on that.  So we're waiting on that.  Alright. Is everyone familiar with the end-of-ten?  Yeah.  XML is not fun.  So, yeah, one of the Windows Image Tools function is a new on-a-ten XML.  And that will, it doesn't do everything because there's a million things you can do in the end-of-ten,  but it takes care of a lot of the basics.  In this particular case, we wanted to join the domain, so it leaves my super secret password out there.  Not always the best, but I haven't had much experience or luck with the whole creating an offline join and using that,  which probably would be a little more secure in this,  but it also requires you to have other stuff done in advance that maybe the technician doing it doesn't.  But in our environment, if an on-a-ten is found on a machine, it's immediately deleted by group policy anyway.  So, I've seen a lot of people with their examples for on-a-ten.  They say, like, take this and then do a fine and replace if you're 32-bit or 64-bit.  Well, it turns out they can live side by side.  So, in this case, it's a complete duplicate.  One is the X86 and the other one's the 64.  And the OS will just run whichever one it needs,  except when you're dealing with tasks.  So, if I'm going to run a synchronous command, it will, the 32-bit will run the 32-bit section.  The 64-bit will run both the 32 and the 64-bit section.  Everything will run twice.  So, since it doesn't actually trigger a 32 versus 64-bit process, it just runs whatever command it is,  I only have to include those once.  So, here's where that first boot script is handled.  I've, since I only asked for the path for that particular function,  I've pre-populated that the rest of this with the starting up PowerShell.  And I also, there's some also parameters to set your own stuff if you wanted to create anything specific.  It's different.  You know, it's just mostly right now, it's just the basics, input locale,  and the parameters to make it so that it doesn't ask you for anything,  keep it pretty clean.  Here's where it set the password for the admin.  So, and that's still going.  Well, anyway, so I'm hoping some of these things are,  the little gems in here that you guys might find useful for stuff that you might do with either imaging  or just handling that first boot.  I mean, I, being able to have something bootstrap and kick off right as the OS is coming up,  I found fairly good.  Now, I know there are ways to bootstrap DSC.  I never had very much luck with it.  A script usually works better.  So this is a great way to have that bootstrap kick off a DSC config  or some other automated process that you guys want to just, you know, not have to have someone do manually.  So, I, suckers just turning along, isn't it?  Doesn't look very busy.  All right, well, does anyone got any questions or anything else?  Yes?  I don't remember the name of the website, but just a few words of the website that helped you create the on-app end ectomo.  That's slacking and all those things.  Oh, yeah.  I'm going to just spit it out for you and then I'll use it for the whole story.  Yeah, the reason for the PowerShell one was just so that I could script the process.  You know, you can't script a website as easily.  If they've got a nice REST method, that's great.  But, yeah, if you're trying to do something really complicated, something like that's probably covered every possible option.  I don't see this going there.  This is primarily just for getting the basics up and then handling a lot of the other settings can be done after the fact.  So, yeah?  So, after this image is aligned, you're currently on the rules matter, so I'm going to bring it up a little bit more.  I think it would be excellent for anything like that.  The bootstrap could kick off anything from that.  So, after that scheduled task I'm building during the initial specialization phase, that could start anything.  That scheduled task then kicks off the PowerShell script and anything I need to do beyond that.  I mean, this is a demo, so I was just doing something simple.  I mean, it could do all sorts of stuff from software installations to, you know, I don't know, configuration, registering it with your config stuff,  getting its certificate or anything like that.  So, you know, it's PowerShell.  Anything PowerShell can touch.  You know, we can manage it somehow.  And while I did this with Hyper-V, I could do the same thing with VMware.  With Hyper-V, I would normally just, I'd use what I'm doing here inside this host.  I would just go ahead and write out the VHD directly.  Yes?  What do you use when you're briefing your blog lawyers and you have their own protection?  Almost every script has a start transcript at the top.  And in this case, just to be standardized, the, I've created this folder under the program data.  And inside it, I've got one where I store my scripts and where I store my logs.  So I just have a standardized way which we have.  These are where we're going to write, put any scripts that aren't grabbed off of our internal gallery.  Or here's a spot for the logs.  I was actually looking at having certain steps in this is going to kick off an email over to our Teams channel.  So that's working in that direction to have some sort of notification.  But we don't really have a lot of sophistication in certain parts of our environment.  So, you know, it would be possible to put whatever you want to have it check in, let you know how things are going.  I could just as easily do that with a Polaris thing where I have it write to a log, just go ahead and hit that endpoint.  Have it either shoot out an email, update it, hit database, something, so that I can see the status of what things are at, how far they are, you know.  We write our logs instead of going to the local or the local.  The first thing we do is we create a map to a central share and we actually write the logs to our field and we send our shares slowly.  Well, once this is on a domain, you know, group policy is going to set off on that first boot up and it's going to set our transcript log location and our log forwarding and everything.  So we capture all the stuff that I'm pretty sure Lee talked about this morning.  I didn't see his talk but I've seen plenty of his other ones, so yeah, we're trying to capture everything we can.  Oh, the scheduled tasks during the WinPE portion, something's not there.  I can start the scheduled service but the new scheduled tasks, PowerShell functions just don't work at that point.  But that's just because we're in that initialization phase.  Who knows what's available.  The scheduled task walking was probably not available during the state route that shipped through the outbreak system.  Yeah, I mean a lot of things are missing in PE but this is at the point, it's the full OS, it's booting up but it's doing the specialization phase.  Most of the services are not running.  So I know the scheduled task was needed.  I don't know why the other part is.  It's probably the top of my mind might not be running.  I don't know.  Do you know what version of the power shelling is running?  I don't know.  This is the one I downloaded last month.  So I suppose we can do that.  We'll just bail this.  Oh, come on.  Of course.  That way it'll actually have newer stuff.  At least having something is a lot better because otherwise you're stuck with whatever a vendor of your imaging tool or whatever you can manage to do with batch files.  So that's the version.  I don't know.  I don't know.  You start the ADK and then inside that it's going to download the WIM and everything else and you just go with whatever's there.  And honestly, I'm not entirely sure how to tell it to refresh and get the new one.  I just end up ripping the thing out and resetting it up.  So I mean, most of the time it works.  I mean, like the Windows update function I had in there, it was working great until November and then something changed.  It was using the same code you used for like a nano and those don't work anymore.  They do not work in Windows 10 and 2016.  It's like, why did they have to break that?  So I got to get a lot more creative with Windows update.  Anyway, that's all the time I've got.  So thank you guys for coming.
OS_Deployments_useing_WinPE_0_47.png OS_Deployments_useing_WinPE_48_95.png OS_Deployments_useing_WinPE_96_143.png OS_Deployments_useing_WinPE_144_251.png OS_Deployments_useing_WinPE_252_3383.png OS_Deployments_useing_WinPE_3384_3443.png OS_Deployments_useing_WinPE_3444_3491.png OS_Deployments_useing_WinPE_3492_5555.png OS_Deployments_useing_WinPE_5556_6191.png OS_Deployments_useing_WinPE_6192_6287.png OS_Deployments_useing_WinPE_6288_8687.png OS_Deployments_useing_WinPE_8688_11447.png OS_Deployments_useing_WinPE_11448_13871.png OS_Deployments_useing_WinPE_13872_14207.png OS_Deployments_useing_WinPE_14208_14627.png OS_Deployments_useing_WinPE_14628_15167.png OS_Deployments_useing_WinPE_15168_17843.png OS_Deployments_useing_WinPE_17844_18203.png OS_Deployments_useing_WinPE_18204_18431.png OS_Deployments_useing_WinPE_18432_18551.png OS_Deployments_useing_WinPE_18552_20003.png OS_Deployments_useing_WinPE_20004_20303.png OS_Deployments_useing_WinPE_20304_20351.png OS_Deployments_useing_WinPE_20352_20819.png OS_Deployments_useing_WinPE_20820_21791.png OS_Deployments_useing_WinPE_21792_23387.png OS_Deployments_useing_WinPE_23388_25919.png OS_Deployments_useing_WinPE_25920_26963.png OS_Deployments_useing_WinPE_26964_27203.png OS_Deployments_useing_WinPE_27204_29327.png OS_Deployments_useing_WinPE_29328_39911.png OS_Deployments_useing_WinPE_39912_45083.png OS_Deployments_useing_WinPE_45084_48179.png OS_Deployments_useing_WinPE_48180_49703.png OS_Deployments_useing_WinPE_49704_49895.png OS_Deployments_useing_WinPE_49896_49955.png OS_Deployments_useing_WinPE_49956_50147.png OS_Deployments_useing_WinPE_50148_50339.png OS_Deployments_useing_WinPE_50340_50627.png OS_Deployments_useing_WinPE_50628_50855.png OS_Deployments_useing_WinPE_50856_51635.png OS_Deployments_useing_WinPE_51636_51707.png OS_Deployments_useing_WinPE_51708_51839.png OS_Deployments_useing_WinPE_51840_52043.png OS_Deployments_useing_WinPE_52044_52211.png OS_Deployments_useing_WinPE_52212_52427.png OS_Deployments_useing_WinPE_52428_52487.png OS_Deployments_useing_WinPE_52488_52919.png OS_Deployments_useing_WinPE_52920_52967.png OS_Deployments_useing_WinPE_52968_53027.png OS_Deployments_useing_WinPE_53028_54347.png OS_Deployments_useing_WinPE_54348_54419.png OS_Deployments_useing_WinPE_54420_54851.png OS_Deployments_useing_WinPE_54852_54995.png OS_Deployments_useing_WinPE_54996_55607.png OS_Deployments_useing_WinPE_55608_56123.png OS_Deployments_useing_WinPE_56124_56195.png OS_Deployments_useing_WinPE_56196_56303.png OS_Deployments_useing_WinPE_56304_56423.png OS_Deployments_useing_WinPE_56424_57479.png OS_Deployments_useing_WinPE_57480_57563.png OS_Deployments_useing_WinPE_57564_59111.png OS_Deployments_useing_WinPE_59112_66503.png OS_Deployments_useing_WinPE_66504_66791.png OS_Deployments_useing_WinPE_66792_67343.png OS_Deployments_useing_WinPE_67344_69743.png OS_Deployments_useing_WinPE_69744_69899.png OS_Deployments_useing_WinPE_69900_69931.png