29

I have encountered an issue when creating custom AMIs (images) on EC2 instances. If I start up a Windows default 2012 server instance with a custom bootstrap/user-data script such as;

<powershell>
PowerShell "(New-Object System.Net.WebClient).DownloadFile('http://download.microsoft.com/download/3/2/2/3224B87F-CFA0-4E70-BDA3-3DE650EFEBA5/vcredist_x64.exe','C:\vcredist_x64.exe')"
</powershell>

It will work as intended and go to the URL and download the file, and store it on the C: Drive.

But if I setup a Windows Server Instance, then create a image from it, and store it as a Custom AMI, then deploy it with the exact same custom user-data script it will not work. But if I go to the instance url (http://169.254.169.254/latest/user-data) it will show the script has imported successfully but has not been executed.

After checking the error logs I have noticed this on a regular occasion:

Failed to fetch instance metadata http://169.254.169.254/latest/user-data with exception The remote server returned an error: (404) Not Found.
Cœur
  • 37,241
  • 25
  • 195
  • 267
Joe Chatterton
  • 319
  • 1
  • 4
  • 10

5 Answers5

43

Update 4/15/2017: For EC2Launch and Windows Server 2016 AMIs

Per AWS documentation for EC2Launch, Windows Server 2016 users can continue using the persist tags introduced in EC2Config 2.1.10:

For EC2Config version 2.1.10 and later, or for EC2Launch, you can use true in the user data to enable the plug-in after user data execution.

User data example:

<powershell>
    insert script here 
</powershell> 
<persist>true</persist>

For subsequent boots:

Windows Server 2016 users must additionally enable configure and enable EC2Launch instead of EC2Config. EC2Config was deprecated on Windows Server 2016 AMIs in favor of EC2Launch.

Run the following powershell to schedule a Windows Task that will run the user data on next boot:

C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 –Schedule

By design, this task is disabled after it is run for the first time. However, using the persist tag causes Invoke-UserData to schedule a separate task via Register-FunctionScheduler, to persist your user data on subsequent boots. You can see this for yourself at C:\ProgramData\Amazon\EC2-Windows\Launch\Module\Scripts\Invoke-Userdata.ps1.

Further troubleshooting:

If you're having additional issues with your user data scripts, you can find the user data execution logs at C:\ProgramData\Amazon\EC2-Windows\Launch\Log\UserdataExecution.log for instances sourced from the WS 2016 base AMI.


Original Answer: For EC2Config and older versions of Windows Server

User data execution is automatically disabled after the initial boot. When you created your image, it is probable that execution had already been disabled. This is configurable manually within C:\Program Files\Amazon\Ec2ConfigService\Settings\Config.xml.

The documentation for "Configuring a Windows Instance Using the EC2Config Service" suggests several options:

  • Programmatically create a scheduled task to run at system start using schtasks.exe /Create, and point the scheduled task to the user data script (or another script) at C:\Program Files\Amazon\Ec2ConfigServer\Scripts\UserScript.ps1.

  • Programmatically enable the user data plug-in in Config.xml.

Example, from the documentation:

<powershell>
$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"
    }
}
$xml.Save($EC2SettingsFile)
</powershell>
  • Starting with EC2Config version 2.1.10, you can use <persist>true</persist> to enable the plug-in after user data execution.

Example, from the documentation:

<powershell>
    insert script here
</powershell>
<persist>true</persist>
Anthony Neace
  • 25,013
  • 7
  • 114
  • 129
  • I think this needs to be updated again, they want you to use EC2Launch for Windows2016 and EC2Config for previous OS versions: https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-user-data.html – Jeremy Thompson Jul 11 '18 at 04:37
  • @JeremyThompson Could you clarify what needs to be updated? This answer is already making that recommendation, as far as I can tell. – Anthony Neace Jul 11 '18 at 04:40
  • Sure, the bit that says: *To enable user data execution on Windows Server 2016 (EC2Launch)*, they explicitly state that *To enable user data execution on Windows Server 2012 R2 and earlier (EC2Config)*. You may be right that **EC2Config 2.1.10** works with Win2016 but the doco indicates to use EC2Launch for Win2016 and the other for prior OS. – Jeremy Thompson Jul 11 '18 at 04:48
  • 2
    @JeremyThompson Ah :) No no, that's not what that is saying. Allow me to clarify: I mentioned that version of EC2Config to draw out that EC2Launch continued supporting that same functionality -- to support this previous solution of persisting tags via the user-data script. You are correct that WS 2016 needs to be using EC2Launch -- and the answer does not disagree with that assertion -- EC2Config 2.1.10 was only mentioned to draw context to where this functionality originally came from, that the same solution also works for EC2Launch, and why it does. Hope that helps. – Anthony Neace Jul 11 '18 at 04:53
11

Another solution that worked for me is to run Sysprep with EC2Launch.

The issue is that AWS doesn't reestablish the route to the profile service (169.254.169.254) in your custom AMI. See response by SanjitPatel in this post. So when I tried to use my custom AMI to create spot requests, my new instances were failing to find user data.

Shutting down with Sysprep, essentially forces AWS re-do all setup work on the instance, as if it were run for the first time. So when you create your instance, shut it down with Sysprep and then create your custom AMI, AWS will setup the profile service route correctly for the new instances and execute your user data. This also avoids manually changing Windows Tasks and executing user data on subsequent boots, as persist tag does.

Here is a quick step-by-step:

  1. Create an instance using one of the AWS Windows AMIs (Windows Server 2016 Nano Server doesn't support Sysprep) and passing your desired user data (this may be optional, but good to make sure AWS wires setup scripts correctly to handle user data).
  2. Customize your instance as needed.
  3. Shut down your instance with Sysprep. Just open EC2LaunchSettings application and click "Shutdown with Sysprep". Full instructions here.
  4. Create your custom AMI from the instance you just shut down.
  5. Use your custom AMI to create other instances, passing user data on instance creation. User data will be executed on instance launch. In my case, I used Spot Request screen, which had a User Data text box.

Hope this helps!

  • Thanks for this! It's exactly what I needed. I had a custom AMI to launch spot instances off of using Lambda functions, but the user data wouldn't run. Logs said it was empty. – rgoliveira Nov 23 '21 at 17:54
4

At the end of initial bootstrap (UserData) script, just append persist tag as shown below. Works perfectly.

<powershell>
    insert script here
</powershell>
<persist>true</persist>
dspatil
  • 327
  • 2
  • 8
1

For those people that got here from Google and are running a Server 2016 instance, it seems that this is no longer possible.

Server2016 doesn't have ec2config service and so you can't use the persist flag.

<persist>true</persist>

Described in Anthony Neace's post.

Server 2016 uses EC2Launch and I haven't yet seen how it's possible to run a script at every boot. You can run a script on the first boot, but subsequent boots will not run it.

Lou O.
  • 599
  • 4
  • 17
  • 1
    You can do it by enabling the scheduled task "Amazon Ec2 Launch - Instance Initialization". It will run only once on the next startup and then become disabled again. – SkyFx Jan 15 '17 at 13:37
  • @SkyFx - is there a way to run a script every time an instance boots? – Lou O. Jan 16 '17 at 22:40
  • 1
    Yes, this task just runs the `C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1` Powershell script. This script first disables the task and then invokes the user data. So you can just reenable the task in the user data [using Powershell](https://technet.microsoft.com/en-us/library/jj649819.aspx) – SkyFx Jan 17 '17 at 14:22
  • 1
    Or, come to think of it, just comment out the task disabling in InitializeInstance.ps1 – SkyFx Jan 17 '17 at 14:25
  • 1
    @LouO. Please see my updated answer; EC2Launch was designed to continue supporting the persist tags. – Anthony Neace Apr 15 '17 at 14:24
0

I added below powershell script to run during the AMI bake process which helped me fix this issue. This was Windows server 2019.

$EC2LaunchInitInstance = "C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1"
$EC2LaunchSysprep = "C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\SysprepInstance.ps1"
Invoke-Expression -Command "$EC2LaunchInitInstance -Schedule"
Invoke-Expression -Command "$EC2LaunchSysprep -NoShutdown"
Pranav
  • 1