I work mostly on Apple Mac OS X. I’ve also been writing a lot of Windows automation, and that means PowerShell DSC. PSDSC doesn’t work on OS X yet, and even once it does I won’t be able to test Windows-only resources. To test my configurations, I use Vagrant boxes. It took a little fiddling to get this set up, so I’m posting the code here in case you’re in the same situation.
This assumes you already have Vagrant working. I use the VirtualBox provider and I install everything with homebrew.
Today, Vagrant doesn’t have a native PowerShell DSC provisioner. I use the shell provisioner to run my simple headless PSDSC script, which compiles and runs a configuration. Follow that link and save a copy as
headless_dsc.ps1 and its XML file as
Next, copy this
Vagrantfile into the same directory:
Vagrant.configure("2") do |config| config.vm.box = "StefanScherer/windows_2019" config.vm.provision "file" do |file| file.source = "input.xml" file.destination = "input.xml" end config.vm.provision "shell" do |shell| shell.path = "headless_dsc.ps1" shell.args = ["-InputXmlFile", "input.xml"] # https://github.com/hashicorp/vagrant/issues/9138#issuecomment-444408251 shell.privileged = false end end
To create the box and run PSDSC:
If you’ve made changes and you want to recomplie and rerun the configuration:
- If you need to connect to the instance and run commands, check out
vagrant rdp. You’ll need to install Remote Desktop from the App Store. There’s a funky bug: if Remote Desktop isn’t open, the first
rdpcommand will open the app but won’t start a session. Just run it again and you’ll get a session window.
- The provisioner uploads the script and XML file on every run even though the current directory is mounted as a synced folder on the box by default. I could have used an inline shell script to call the script directly via that sync, but sometimes I disable the sync for testing and it’s convenient for the provisioner to keep working.
shell.privileged = false, I got errors like this:
default: (10,8):UserId: default: At line:72 char:1 default: + $folder.RegisterTaskDefinition($task_name, $task, 6, $username, $pass ... default: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ default: + CategoryInfo : OperationStopped: (:) , ArgumentException default: + FullyQualifiedErrorId : System.ArgumentException default: The system cannot find the file specified. (Exception from HRESULT: 0x80070002) default: At line:74 char:1 default: + $registered_task = $folder.GetTask("\$task_name") default: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ default: + CategoryInfo : OperationStopped: (:) , FileNotFoundException default: + FullyQualifiedErrorId : System.IO.FileNotFoundException default: You cannot call a method on a null-valued expression. default: At line:75 char:1 default: + $registered_task.Run($null) | Out-Null default: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ default: + CategoryInfo : InvalidOperation: (:) , RuntimeException default: + FullyQualifiedErrorId : InvokeMethodOnNull
The whole process froze and I had to ctrl+c twice to close it. There’s a bug. I left a link in a comment that I recommend you keep in your code so this setting isn’t confusing later.
If this was helpful and you want to save time by getting “copy and paste” patterns for PowerShell and Cloud DevOps in your inbox, subscribe here.