AWS Vendor-Written Generated Code

I remember thinking, Don't write about AWS too much once you only write here. Well, I'm little less hung up on that as much as I was previously. With that, here's another AWS-centric article. This one needs to be out there, however.

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, that 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){ 
}

Now that I look at it again, there is some indentation in the foreach. Anyway, this is not why we're here.

It's rare that I ever manually launch an AWS EC2 instance (a virtual sever). 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 with which 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 like it has previously. Okay, off to potentially find something to discuss and share that's not about AWS (outside of part b to another AWS article I started a few days ago).

≥ Tommy Maynard (Twitter: @thetommymaynard)

About Tommy Maynard

IT Pro. Passionate for #PowerShell, #AWS (certified x2), & all things automation. I'm not done learning. Author in #PSConfBook. Writes at https://powershell.org.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.