I’ve been prepping for a lot of different speaking engagements coming up in the next few months and a very hot topic these days is the use of PowerShell and automation, when it comes to Hyper-V. With this in mind, I’ve prepped the below script for some of these upcoming discussions, and wanted to share it with the community so that it’ll be of help to some people.

The Story

Awhile back, I spoke at a VMware VMUG event where I talked about advanced VM provisioning using PowerCLI and templates. In that talk, I taught the group how to provision a virtual machine using the shell, and then reach into the VM and execute a number of commands against the guest OS to achieve even greater amounts of automation and followed up by posting the script HERE.

Well, I wanted to take the goal from that script and apply it to a Hyper-V environment using PowerShell Direct. For those that aren’t aware, PowerShell Direct is a new feature in the Windows Server 2016 feature set, that allows us to inject PowerShell commands into the guest OS of a running VM, without the network for networking. Without getting too into the specifics, it does this via the VMbus on the Hyper-V host, and it works VERY well.

NOTE: You must have either Windows Server 2016 TP5 or newer and/or Windows 10 running as BOTH the host and the guest in order for PowerShell Direct to work.

Let’s cover the workflow and the goal of this script first.

Technical Goals of the Script

  1. Must be FULLY automated from the moment you hit the enter key to start the script.
  2. Must provide verbose information for the administrators viewing if desired.
  3. Must Deploy 2 VMs from a pre-existing Sysprepped VHDX with an embedded unattend.xml answer file.
  4. Must be able to define static IP addresses.
  5. Must configure AD on one of the new VMs and provision a new AD Forest.
  6. Must create a new custom administrative account within the domain.
  7. 2nd VM must be automatically joined to the newly defined domain.
  8. File Services must be installed on the second VM
  9. A new SMB share must be defined on the file server VM, once the file services role is present.

This is certainly a lot to get done with a single press of the enter key, but it is doable. Also, this is likely just the beginnings of a new environment, but the script could very easily be modified to deploy more than just the two VMs.

Community Goals of the Script

Just like the PowerCLI/VMware script, My communities goals with THIS script remain the same. My goal from a teaching aspect with this script was to be the follow:

  1. Well Documented and Heavily Commented
  2. VERY sequential ordering of the commands, as to be easy to follow for beginners
  3. Best Practices definition of variables at the top of the script.
  4. Well segmented so that certain sections can be copied and duplicated if desired

The Secret Sauce

So what are the important bits that make this script work? With every script there are key points that really stand out, and I always like to point them out.

Pre-Prepared VHDX – While not strictly script related, this certainly makes the job much easier. The script assumes you have a VHDX file that already has Server 2016 installed and patched. It also assumes that you’ve sysprepped that image, and you’ve used the Windows ADK toolset to create an answer file. This takes care of the bulk of the OS customization upon deployment of the new VMs.

While Loop – If you look at the script, I have a small chunk of code in there that is from Ben Armstrong on the Hyper-V product group team. It essentially attempts to write a string of text to the console of the guest VM using PowerShell Direct, until it’s successful. What this allows you to do in your script execution is have it wait until PowerShell direct is functional before moving on. Very useful for sequential operations.

Invoke-Command –  While not a new ground breaking cmdlet, this script wouldn’t be possible without it. The invoke-command cmdlet is the one cmdlet in this script that is doing the bulk of the work. You’ll see it used with the -VMName  parameter extensively throughout the script. When used with the -VMName parameter, it lets PowerShell know that it’s running the mentioned script block with PowerShell direct and NOT standard PowerShell Remoting. See the script for examples.

The Script

So here is the part you’ve been waiting for. Below is the script. Take it! Use it. Learn from it! Hopefully it will be of some use!

Again the script is heavily self-documented and commented, so you should be able to read through it and set the needed variables as needed. Note, all of the user-definable variables are at the top of the script.


There you have it! It is my hope that this will be of some use to you, whether that be helping you get a task done at your job, or helping you learn how to use PowerShell and automate.

If you have questions, feel free to use the comment form below, and I’m looking forward to seeing if this script was useful to you.