Notice: The following post was originally published on another website. As the post is no longer accessible, it is being republished here on tommymaynard.com. The post was originally published on September 13, 2019.
Sometimes you read an error message, or in this case, come across some vendor-written code that you can’t find anywhere else on the Internet. It’s been years, but once PowerShell generated an error I had never seen. I couldn’t find a hit for it online anywhere either. I felt that once I had figured out the problem behind that error message, it was my duty to write about it—to help get that error message picked up by search engines, as well as my experience. I feel nearly the same way about the below code to which I was recently introduced, written by AWS, or Amazon Web Services. I’ll share it now. Just a note. This is exactly how it was found. There were no indentations. I’m not too concerned about the lack of indentations—I don’t have to stare at it each day. Perhaps it’s generated by something that may not be preserving the spacing/tabs. That’s just a guess.
try { Get-Service Ec2Config $EC2SettingsFile='C:\Program Files\Amazon\Ec2ConfigService\Settings\Config.xml' $xml = [xml](get-content $EC2SettingsFile) $xmlElement = $xml.get_DocumentElement() $xmlElementToModify = $xmlElement.Plugins foreach ($element in $xmlElementToModify.Plugin){ if ($element.name -eq 'Ec2SetPassword') {$element.State='Enabled'} elseif ($element.name -eq 'Ec2HandleUserData') {$element.State='Enabled'} elseif ($element.name -eq 'Ec2DynamicBootVolumeSize') {$element.State='Enabled'} } $xml.Save($EC2SettingsFile) } catch { C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File 'C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1' -Schedule } finally { New-Item -Path HKLM:\Software\Amazon -Name WarmBoot Invoke-Expression -Command:'shutdown.exe /s /t 0' } while ($true){ }
It’s rare that I ever manually launch an AWS EC2 instance (a virtual server). Well, I was doing that recently for some quick testing and my UserData PowerShell script was not landing in the C:\Program Files\Amazon\Ec2ConfigService\Scripts\UserScript.ps1
file on my Windows Server 2012 R2 instance, as it should have been. I was doing something wrong and it wasn’t clear to me what, soon enough. Before we get to that, let’s discuss what we have here. We have a try-catch
language construct. I know from my AWS experience that most of what’s going on in the try
block was done for Windows Server 2012 R2 and newer. I also know that what’s in the catch
block is how we ensure UserData is enabled in Windows Server 2016 and later. AWS couldn’t take my UserData and drop it on this instance. Instead, I got this code in its place. Ugh.
This code also includes the finally
block. That code is run regardless of whether the try
or catch
block fired. The code creates a value in the Windows Registry and then restarts the computer using Invoke-Expression
—interesting choice. It’s always fun to see vendor code. It closes with an empty While
language construct. While $true
is $true
, this While
loop will run—and do absolutely nothing. It will do that successfully, however.
Again, my UserData PowerShell wasn’t getting into this UserScript file. It was, however, available by “navigating” to 169.254.169.254 on the EC2 instance.
Invoke-RestMethod -Uri http://169.254.169.254/latest/user-data # >> Add function to memory. Function Set-SystemForNextRun { ... Set-SystemForNextRun -CodeSectionComplete 2 } # End If-ElseIf.
The problem was that my code didn’t have the begin
and end
PowerShell tags. In order for the UserScript.ps1
file to be populated with my code and not this code from Amazon, I needed to ensure I was including everything required by me. It seems like something in the AWS Management Console (the web front end) could’ve notified me after looking at my code, but before moving to the next step in manually building my instance. Or, be even less helpful, and additionally write something else in the UserScript.ps1
file. They could’ve just started their code with a comment to tell me I didn’t follow the directions. I’ve used UserData in CloudFormation; I know these tags are required.
<# This doesn't look right, does it? Did you remember to use script or powershell start and end tags? #>
Anyway, once I enclosed my PowerShell in the proper tags, it worked, and I moved on. And by moved on I mean, I found another problem to consume me—as is typical in this industry. It should’ve look liked this from the start. Ugh.
Invoke-RestMethod -Uri http://169.254.169.254/latest/user-data <powershell> # >> Add function to memory. Function Set-SystemForNextRun { ... Set-SystemForNextRun -CodeSectionComplete 2 } # End If-ElseIf. </powershell>
Things changed between Windows Server 2012 R2 and 2016, 2019. I’m not exactly sure where the UserData code ends up in the newer OS if it even does end up in a file on disk as it has previously.