<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Auto Scaling Group on AWS McMillearn Blog</title><link>https://aws.mcmillearn.net/categories/auto-scaling-group/</link><description>Technical blog focused on AWS best practices and architecture patterns on AWS McMillearn Blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>John McMillan</managingEditor><webMaster>John McMillan</webMaster><lastBuildDate>Sun, 03 May 2026 09:00:00 +0000</lastBuildDate><atom:link href="https://aws.mcmillearn.net/categories/auto-scaling-group/index.xml" rel="self" type="application/rss+xml"/><category>AWS</category><category>Cloud Computing</category><category>DevOps</category><category>Architecture</category><item><title>Half Baked Images - Fully Cooked Update</title><link>https://aws.mcmillearn.net/posts/half-baked-images-update/</link><pubDate>Sun, 03 May 2026 09:00:00 +0000</pubDate><author>John McMillan</author><guid>https://aws.mcmillearn.net/posts/half-baked-images-update/</guid><description>AWS fixed the Distribution Settings - the Lambda workaround is no longer needed</description><content:encoded><![CDATA[<h1 id="tldr">TL;DR</h1>
<p>Remember that Lambda function I built to work around EC2 Image Builder&rsquo;s Distribution Settings wiping your Launch Template config? You can bin it. AWS have updated the Distribution Settings behaviour and it now preserves your existing Launch Template settings when it creates a new version. The custom Lambda updater is no longer required.</p>
<h1 id="a-quick-recap">A quick recap</h1>
<p>In <a href="https://aws.mcmillearn.net/posts/half-baked-images/">Half Baked Images</a> I walked through the pain of integrating EC2 Image Builder with Terraform-managed Auto Scaling Groups. The core issue was that when EC2 Image Builder distributed a new AMI and updated your Launch Template, it created a new version containing <em>only</em> the AMI ID and account ID. Everything else, i.e. instance profile, security groups, key pair, block device mappings was gone.</p>
<p>The result? Your ASG would scale up instances with no instance profile, no security groups, and none of the configuration you&rsquo;d carefully defined. Not ideal.</p>
<p>My solution was a two-parter:</p>
<ol>
<li>Use an SSM Parameter as the source of truth for the AMI ID, updated by Image Builder&rsquo;s Distribution Settings.</li>
<li>Deploy a custom Lambda function, triggered by EventBridge, that would read the latest AMI ID from SSM and create a <em>proper</em> new Launch Template version. One that actually included all the important bits.</li>
</ol>
<p>It worked. It was reliable. But it was also plumbing that shouldn&rsquo;t have been necessary.</p>
<h1 id="what-changed">What changed?</h1>
<p>AWS have updated the EC2 Image Builder Distribution Settings behaviour. The <a href="https://docs.aws.amazon.com/imagebuilder/latest/userguide/dist-using-launch-template.html">documentation</a> now states:</p>
<blockquote>
<p>When <code>launchTemplateConfigurations</code> are present during the distribution process, Image Builder creates a new version of the launch template that includes all of the original settings from the template, and the new AMI ID from the build.</p>
</blockquote>
<p>The important bit there:  <strong>All of the original settings from the template.</strong></p>
<p>That&rsquo;s the bit that was missing before. Previously, Image Builder would create a new Launch Template version with <em>only</em> the AMI ID, effectively a blank template with a picture attached. Now it carries forward your existing configuration and simply swaps in the new AMI ID.</p>
<p>This is exactly the behaviour I expected when I first configured the <code>launch_template_configuration</code> block in my distribution config. It&rsquo;s what most people would reasonably expect. And now it actually works that way.</p>
<h1 id="what-does-this-mean-practically">What does this mean practically?</h1>
<p>If you followed my original approach, you can simplify things significantly.</p>
<h3 id="what-you-can-remove">What you can remove</h3>
<ul>
<li>The <strong>Lambda function</strong> that created new Launch Template versions with the full configuration.</li>
<li>The <strong>EventBridge rule</strong> that triggered that Lambda on successful image builds.</li>
<li>The associated <strong>IAM roles and policies</strong> for the Lambda function.</li>
<li>Any <strong>CloudWatch log groups</strong> you set up for the Lambda.</li>
</ul>
<p>That&rsquo;s a decent chunk of infrastructure that existed purely to work around a limitation that no longer exists.</p>
<h3 id="what-you-should-keep">What you should keep</h3>
<ul>
<li>The <strong>SSM Parameter</strong> approach is still a good pattern. Having a single source of truth for your AMI ID that both Terraform and Image Builder can reference is clean and avoids the bootstrapping headaches I described in the original post.</li>
<li>The <strong>lifecycle ignore</strong> on the SSM Parameter value in Terraform, this is still needed so Terraform doesn&rsquo;t fight with Image Builder over who owns that value.</li>
</ul>
<h3 id="what-to-add-back">What to add back</h3>
<p>You can now use the <code>launch_template_configuration</code> block in your distribution config with confidence:</p>
<pre tabindex="0"><code>resource &#34;aws_imagebuilder_distribution_configuration&#34; &#34;dist&#34; {
  name = &#34;${var.project}-dist&#34;

  distribution {
    region = var.region

    ami_distribution_configuration {
      name        = &#34;${var.project}-{{imagebuilder:buildDate}}&#34;
      description = &#34;Custom built AL2023 image&#34;

      ami_tags = {
        OS       = &#34;AmazonLinux2023&#34;
        Hardened = &#34;true&#34;
        Name     = &#34;${var.project}-{{imagebuilder:buildDate}}&#34;
      }
    }

    launch_template_configuration {
      launch_template_id = aws_launch_template.my_lt.id
      account_id         = local.account_id
      set_default_version = true
    }
  }
}
</code></pre><p>Note the <code>set_default_version = true</code>&hellip; this tells Image Builder to set the newly created version as the default. Combined with the preserved settings, this means your ASG will pick up the new AMI on the next scale event without any Lambda glue in the middle.</p>
<h1 id="updated-lab">Updated lab</h1>
<p>I&rsquo;ve updated the <a href="https://github.com/jmcmillan1873/autoscaling-with-imagebuilder">autoscaling_with_imagebuilder</a> lab to reflect this. The Lambda updater and its associated EventBridge rule have been removed. The distribution config now handles the Launch Template update natively, as it always should have.</p>
<p>If you previously deployed the lab with the Lambda approach, you can safely remove those resources and update your distribution configuration. The SSM Parameter pattern remains, it&rsquo;s still useful for the Terraform bootstrapping needs.</p>
<h1 id="thoughts">Thoughts</h1>
<p>I&rsquo;ll be honest, this is a satisfying update to write. The original solution worked, and I do think it was a reasonable approach given the constraints at the time. But it always felt like I was compensating for something that should have been a native capability.</p>
<p>The fact that AWS have addressed this is a good sign. It suggests they listened to the feedback (and I suspect I wasn&rsquo;t the only one who ran into this). It also validates the frustration I had - if the fix was to make Distribution Settings preserve existing config, then the previous behaviour <em>was</em> the problem, not my expectations.</p>
<p>One less Lambda in the world. Now if AWS could just make their <a href="https://aws.mcmillearn.net/posts/half-baked-lifecycles/">Lifecycle Policies</a> stop nuking AMIs that are still in use, I might be able to retire the <em>other</em> Lambda too. A man can dream.</p>
<h2 id="additional-resources">Additional Resources</h2>
<ul>
<li><a href="https://aws.mcmillearn.net/posts/half-baked-images/">Half Baked Images - original blog post</a></li>
<li><a href="https://aws.mcmillearn.net/posts/half-baked-lifecycles/">Half Baked Lifecycles - follow up blog post</a></li>
<li><a href="https://github.com/jmcmillan1873/autoscaling-with-imagebuilder">Updated Auto Scaling Group with EC2 Image Builder Lab</a></li>
<li><a href="https://docs.aws.amazon.com/imagebuilder/latest/userguide/dist-using-launch-template.html">EC2 Image Builder - Configure AMI distribution with a launch template</a></li>
</ul>
<hr>
<p><em>This post is part of my AWS technical blog series. Found this helpful? Connect with me on <a href="https://www.linkedin.com/in/john-mcmillan-aaa4274/">LinkedIn</a> or check back here for more AWS content and discussions.</em></p>
]]></content:encoded><category>AWS</category><category>EC2 Image Builder</category><category>Auto Scaling Group</category><category>AWS</category><category>EC2</category><category>Auto Scaling Group</category><category>EC2 Image Builder</category><category>AWS EC2 Image Builder</category><category>AWS Auto Scaling Group</category><enclosure url="https://aws.mcmillearn.net/images/half-baked-update-title.png" type="image/png"/></item><item><title>Half baked Images?</title><link>https://aws.mcmillearn.net/posts/half-baked-images/</link><pubDate>Thu, 02 Oct 2025 09:00:00 +0000</pubDate><author>John McMillan</author><guid>https://aws.mcmillearn.net/posts/half-baked-images/</guid><description>Making ImageBuilder work with Terraform managed Auto scaling</description><content:encoded><![CDATA[<h1 id="tldr">TL;DR</h1>
<p>Using Terraform to deploy and manage an Auto Scaling Group that utilises custom AMIs built by EC2 Image Builder and dealing with the limitations of EC2 Image Builder&rsquo;s native capabilities for managing Launch Templates.</p>
<h1 id="what-were-we-asked">What were we asked?</h1>
<p>I&rsquo;ve recently worked on a project that required an Auto Scaling Group of EC2 instances that utilised custom-built AMIs produced by EC2 Image Builder. The requirement was to have an EC2 Image Builder pipeline produce weekly images based on Amazon Linux 2023, with various custom components applied.<br>
It&rsquo;s quite straightforward to do this in Terraform, so happy days!</p>
<p>If you&rsquo;ve used Terraform to manage Auto Scaling Groups in AWS, you&rsquo;ll know that this is relatively  straightforward too. There are lots of examples and use cases out there to draw from.<br>
So it should be super simple to marry those two concepts together into one simple, happy, solution you&rsquo;d think.<br>
Yeah right!</p>
<p>If you want to skip the dramatic retelling of my pain and just get the answer, skip to <a href="#so-what-was-the-solution">the solution</a></p>
<h2 id="whats-the-problem-go">What&rsquo;s the problem? Go!</h2>
<p>The first two parts <em>are</em> straightforward - the ASG definition and EC2 Image Builder pipeline setup in Terraform. As is the rest of the Terraform really. Working with my buddy we quickly had IaC that would deploy the VPC that would host our Auto Scaling Group, the Launch Template that would complement that ASG, and the EC2 Image Builder components, recipe, pipeline, and distribution, etc. As well as all the trimmings, IAM policies, security groups, KMS keys, etc, etc.<br>
During the initial deploy attempt we encountered our first red flag. We had valid Terraform code, but it threw up this error:</p>
<pre tabindex="0"><code>Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: Your query returned no results. Please change your search criteria and try again.
│
│   with data.aws_ami.latest_custom,
│   on data.tf line 486, in data &#34;aws_ami&#34; &#34;latest_custom&#34;:
│  486: data &#34;aws_ami&#34; &#34;latest_custom&#34; {
│
</code></pre><p>I&rsquo;d used a data source to capture the ID of the latest image that EC2 Image Builder would produce, but of course that hadn&rsquo;t run yet.<br>
I thought I&rsquo;d been smart, I had foreseen the bootstrapping issue, and thought I&rsquo;d get around it by temporarily pointing the image_id at an AWS managed SSM parameter that stores the Amazon Linux 2023 AMI ID.</p>
<pre tabindex="0"><code>resource &#34;aws_launch_template&#34; &#34;my-cool-project-resource&#34; {
  name_prefix            = &#34;my-cool-project-name-lt-&#34;
  #image_id               = data.aws_ami.latest_custom.id
  image_id               = data.aws_ssm_parameter.al2023.id
</code></pre><p>This didn&rsquo;t prevent the error above. To my shame (not really, I&rsquo;m actually quite pragmatic) I commented out the data source for my latest custom image and was able to successfully deploy my infrastructure. I immediately reversed the commented out sections. <code>data.aws_ami.latest_custom</code> was reinstated, and the Launch Template was reverted back to <code>image_id = data.aws_ami.latest_custom.id</code> and all worked well. Sorted.</p>
<h3 id="5-influence-points-if-you-see-where-this-is-going">+5 influence points if you see where this is going</h3>
<p>It wasn&rsquo;t sorted. Not completely.<br>
I could now successfully deploy the infrastructure reliably, I&rsquo;d hacked my way past the bootstrapping issue, but comforted by my solemn vow to myself that I&rsquo;d go back and fix that. Scout&rsquo;s honour.<br>
<img alt="IdontBelieveYou" loading="lazy" src="/posts/half-baked-images/dontbelieveyou.gif"></p>
<p>The situation was better but not quite what we needed. We had an EC2 Image Builder pipeline that ran to a schedule and worked as expected. Our Launch Template looked good but it wasn&rsquo;t using the most recent image. It was using the most recent -1.  Consider this sequence of events:</p>
<ol>
<li>Terraform deploys IaC, creating ASG, Launch Template, and EC2 Image builder pipeline.</li>
<li>EC2 Image Builder was triggered manually to confirm it was working.</li>
<li>EC2 Image Builder produces a new version of the AMI.</li>
<li>Launch Template is still running the TF deployed AMI version and doesn&rsquo;t know about the one EC2 Image Builder produced.</li>
</ol>
<p>This surfaced what might have been obvious to you dear reader, that the Launch Template <strong>must</strong> be built before we&rsquo;ve built any images.<br>
Also that our Pipeline would run independently of our Terraform code - and so our Launch Template would never know about the latest image until we next ran Terraform to update the Launch Template. That&rsquo;s not ideal as the intention was to set up a CI/CD pipeline to run the Terraform code upon merges to the main branch.</p>
<p>I was opposed to the idea of scheduling the Terraform code to run <em>after</em> the pipeline schedule. That wouldn&rsquo;t be pragmatic, that would be messy.</p>
<h3 id="why-dont-you-just-update-the-lt-natively-from-ec2-image-builder">Why don&rsquo;t you just update the LT natively from EC2 Image Builder?</h3>
<p>Reasons!<br>
Mostly that I didn&rsquo;t know that was a thing at this point. I started looking at how other people have solved this, because surely using custom-built AMIs in Auto Scaling Groups is super common? It kinda is, but so is this challenge.</p>
<p>In actual fact, the distribution settings within EC2 Image Builder allow for updating a Launch Template with the latest AMI&hellip; directly. It&rsquo;s explained nicely <a href="https://docs.aws.amazon.com/imagebuilder/latest/userguide/dist-using-launch-template.html">here</a>.</p>
<p>So that resulted in me adding this <code>launch_template_configuration</code> block to my distribution config:</p>
<pre tabindex="0"><code>resource &#34;aws_imagebuilder_distribution_configuration&#34; &#34;dist&#34; {
  name = &#34;${var.project}-dist&#34;

  distribution {
    region = var.region

    ami_distribution_configuration {
      name        = &#34;${var.project}-{{imagebuilder:buildDate}}&#34;
      description = &#34;A descriptive description describing things&#34;

      ami_tags = {
        OS       = &#34;AmazonLinux2023&#34;
        Hardened = &#34;true&#34;
        Name     = &#34;${var.project}-{{imagebuilder:buildDate}}&#34;
      }
    }
    launch_template_configuration {
      launch_template_id = aws_launch_template.my-cool-project-resource.id
      account_id         = local.account_id
    }
  }
}
</code></pre><p>My distribution would now update the Launch Templates with details of the new AMI ID.<br>
And it did.</p>
<h3 id="half-baked-images">Half Baked Images!</h3>
<p>This was a step forward, if you&rsquo;d first pivoted 180 degrees.<br>
While it did solve the problem of the Launch Template not having the latest AMI - it absolutely <em>did</em> have the latest AMI now - it came at a price.</p>
<p>So, for some reason the documentation gods appear not to have fully explained (please tell me on Linkedin if I&rsquo;m wrong) EC2 Image Builder Distribution Configuration can only configure <em>some</em> elements of the Launch Template, not all!</p>
<p>And it doesn&rsquo;t update the Launch Template in the sense that it retains previous config and simply updates the AMI ID. It updates in the sense that it creates a <strong>new</strong> version with the details you&rsquo;ve told it to. In my case the Account ID and the Launch Template to update with the AMI ID. That makes sense when you step back and think about it, but it also felt unintuitive.</p>
<h3 id="why-is-this-bad">Why is this bad?</h3>
<p>Because the updated version of the Launch Template no longer contains several key details about the rest of the required configuration, Instance profile, Security groups, key pair, etc.</p>
<p>I&rsquo;ll save you the suspense - I checked with Amazon Q to point me to the explanation, and it had this to say:</p>
<blockquote>
<p><em>&ldquo;Unfortunately, EC2 Image Builder cannot preserve existing Launch Template settings when updating the AMI. When EC2 Image Builder creates a new Launch Template version through its distribution settings, it only copies the AMI ID to the new version - it doesn&rsquo;t inherit or preserve other configuration settings like instance profiles, security groups, or other parameters from your existing Launch Template&hellip;<br>
&hellip;the limitations are not explicitly stated in the official AWS docs. The documentation focuses on how to configure EC2 Image Builder to update Launch Templates, but it doesn&rsquo;t clearly specify what happens to existing Launch Template settings when it creates a new version.&rdquo;</em></p>
</blockquote>
<p>The result of this was that my ASG was now spinning up instances with no instance profile, which wasn&rsquo;t the only misconfiguration introduced, but this alone was a show stopper.</p>
<h2 id="so-what-was-the-solution">So what was the solution?</h2>
<p>The solution I settled on was a two-parter, but let&rsquo;s start by recapping what we want to happen:</p>
<ol>
<li>Using Terraform: deploy &amp; manage infrastructure that will create an ASG, Launch Templates, and an EC2 Image Builder pipeline that will update images frequently.</li>
<li>Shared custody of the latest AMI ID &amp; Launch Template configuration between EC2 Image Builder and Terraform. By that I mean:<br>
a. Terraform creates the infrastructure that holds the AMI details and doesn&rsquo;t complain when&hellip;<br>
b. The Launch Template is always referring to the latest AMI ID.</li>
<li>The Launch Template is updated as soon as an updated image is produced, without waiting for Terraform.</li>
<li>Terraform won&rsquo;t freak out when it realises the Launch Template is using a newer AMI ID than it was expecting.</li>
</ol>
<p>So practically how do we do this?</p>
<h3 id="step-1---get-the-iac-correct">Step 1 - Get the IaC correct</h3>
<p>First of all, Terraform needs to create an SSM Parameter as shown in this example:</p>
<pre tabindex="0"><code>
data &#34;aws_ssm_parameter&#34; &#34;al2023&#34; {
  name = &#34;/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64&#34;
}

resource &#34;aws_ssm_parameter&#34; &#34;custom_built_image_id&#34; {
  name        = &#34;/imagebuilder/${var.project}/custom_id&#34;
  description = &#34;SSM Parameter for storing the AMI ID of the image built from EC2 Image Builder&#34;
  type        = &#34;String&#34;
  data_type   = &#34;aws:ec2:image&#34;
  value       = data.aws_ssm_parameter.al2023.value

  lifecycle {
    ignore_changes = [
      value
    ]
  }
}
</code></pre><p>So what did we accomplish with the SSM parameter definition in Terraform?<br>
This is our source of truth for our AMI image.</p>
<ul>
<li>The Terraform definition for the Launch Template should retrieve the value stored here. Hence why it needs to be seeded with a real AMI ID to start with.</li>
<li>Something (no spoilers yet) needs to update the Launch Template with the latest custom built AMI ID. Hence why we have the lifecycle policy ignoring changes to the value.</li>
<li><strong>Important 1</strong> - The managed service role for EC2 Image Builder only has permissions to publish to items in Parameter Store that begin with the prefix <code>/imagebuilder/</code> - see <a href="https://docs.aws.amazon.com/imagebuilder/latest/userguide/tutorial-ssm-parameters-recipe.html">AWS docs on that point</a>. i.e. You <em>must</em> use a <code>name</code> similar to the one in the example above.</li>
<li><strong>Important 2</strong> - <code> data_type   = &quot;aws:ec2:image&quot;</code> <strong>must</strong> be set otherwise the Distribution Configuration in EC2 Image Builder will fail to update the parameter. (Ask me how I know this!)</li>
<li><strong>Important 3</strong> - Don&rsquo;t forget to ignore changes to the <code>value</code> as shown above!</li>
</ul>
<p>Now that we have somewhere to store the AMI ID, we need to make sure its referred to in our code, specifically in the Launch Template and the EC2 Image Builder distribution configs:</p>
<pre tabindex="0"><code>resource &#34;aws_launch_template&#34; &#34;my_lt&#34; {
  name_prefix            = &#34;my-lt-&#34;
  image_id               = aws_ssm_parameter.custom_built_image_id.value
  instance_type          = var.instance_type
  vpc_security_group_ids = [aws_security_group.private_sg.id]

  iam_instance_profile {
    name = aws_iam_instance_profile.custom_permissions.name
  }

  block_device_mappings {
    device_name = &#34;/dev/xvda&#34;
    ebs {
      volume_size = 20
      volume_type = &#34;gp3&#34;
      encrypted   = true
    }
  }

  tag_specifications {
    resource_type = &#34;instance&#34;
    tags = {
      Name    = &#34;NamingIsHard&#34;
      Project = var.project
    }
  }
}

resource &#34;aws_imagebuilder_distribution_configuration&#34; &#34;dist&#34; {
  name = &#34;${var.project}-dist&#34;

  distribution {
    region = var.region

    ami_distribution_configuration {
      name        = &#34;${var.project}-{{imagebuilder:buildDate}}&#34;
      description = &#34;Custom built AL2023 image&#34;

      ami_tags = {
        OS       = &#34;AmazonLinux2023&#34;
        Hardened = &#34;true&#34;
        Name     = &#34;${var.project}-{{imagebuilder:buildDate}}&#34;
      }
    }

    ssm_parameter_configuration {
      parameter_name = aws_ssm_parameter.custom_built_image_id.name
      ami_account_id = local.account_id
    }
  }
}
</code></pre><p>So now the Launch Template is happy, because even at boot time we&rsquo;ve given it a valid AMI ID.<br>
EC2 Image Builder is no longer sabotaging our Launch Template and is instead updating the SSM Parameter Store.</p>
<h3 id="step-2---join-the-dots">Step 2 - Join the dots</h3>
<p>So, thus far we&rsquo;ve completed our deployment, but we still need something to handle the Launch Template updates. Cue <strong>EventBridge</strong> &amp; <strong>Lambda</strong>.<br>
Whereas the native option in EC2 Image Builder would allow us to create a new version of the Launch Template with the new AMI ID, it didn&rsquo;t allow us to specify the other important requirements like security group, profile etc.<br>
A custom Lambda function allows us to do exactly that.</p>
<p>e.g. We can take this function, deploy it using Terraform&hellip;</p>
<pre tabindex="0"><code>import boto3
import os

ssm = boto3.client(&#34;ssm&#34;)
ec2 = boto3.client(&#34;ec2&#34;)

PARAM_NAME = os.environ[&#34;SSM_PARAM_NAME&#34;]
LT_ID = os.environ[&#34;LAUNCH_TEMPLATE_ID&#34;]
INSTANCE_TYPE = os.environ[&#34;INSTANCE_TYPE&#34;]
SECURITY_GROUP_ID = os.environ[&#34;SECURITY_GROUP_ID&#34;]
IAM_INSTANCE_PROFILE = os.environ[&#34;IAM_INSTANCE_PROFILE&#34;]

def handler(event, context):
    # Get latest AMI from SSM
    response = ssm.get_parameter(Name=PARAM_NAME)
    ami_id = response[&#34;Parameter&#34;][&#34;Value&#34;]

    # Create new Launch Template version
    ec2.create_launch_template_version(
        LaunchTemplateId=LT_ID,
        SourceVersion=&#34;$Default&#34;,
        LaunchTemplateData={
            &#34;ImageId&#34;: ami_id,
            &#34;InstanceType&#34;: INSTANCE_TYPE,
            &#34;SecurityGroupIds&#34;: [SECURITY_GROUP_ID],
            &#34;IamInstanceProfile&#34;: {&#34;Name&#34;: IAM_INSTANCE_PROFILE}
        }
    )

    # Set the new version as default
    latest = ec2.describe_launch_template_versions(
        LaunchTemplateId=LT_ID,
        Versions=[&#34;$Latest&#34;]
    )[&#34;LaunchTemplateVersions&#34;][0][&#34;VersionNumber&#34;]

    ec2.modify_launch_template(
        LaunchTemplateId=LT_ID,
        DefaultVersion=str(latest)
    )

    print(f&#34;Updated LT {LT_ID} to use {ami_id}&#34;)
    return {&#34;statusCode&#34;: 200, &#34;ami_id&#34;: ami_id}
</code></pre><h4 id="what-will-this-function-do">What will this function do?</h4>
<p>It takes five environment variables that are used to ensure all the required data for creating a <em>useful</em> new version of the Launch Template is included, but simply put it&rsquo;s doing three things:</p>
<ul>
<li>It obtains the latest AMI ID - that EC2 Image Builder&rsquo;s Distribution config applied to the SSM Parameter.</li>
<li>Create a new version of the Launch Template, passing in the required details, image ID, instance type, security group(s), &amp; Instance profile for permissions.</li>
<li>It updates the Launch Template to set the latest version it just created as the default version.</li>
</ul>
<h4 id="how-do-we-trigger-that-function">How do we trigger that function?</h4>
<p>Now that we have the function to handle the updates, we just need to trigger it appropriately. We do that using EventBridge, and we can also deploy this from Terraform using something like this:</p>
<pre tabindex="0"><code># This is used to trigger Lambda to update the ASG Launch Template when there&#39;s a new AMI available.

resource &#34;aws_cloudwatch_event_rule&#34; &#34;imagebuilder_completed&#34; {
  name          = &#34;${var.project}-imagebuilder-completed&#34;
  description   = &#34;Trigger on successful image builds&#34;
  event_pattern = &lt;&lt;EOF
{
  &#34;source&#34;: [&#34;aws.imagebuilder&#34;],
  &#34;detail-type&#34;: [&#34;EC2 Image Builder Image State Change&#34;],
  &#34;detail&#34;: {
    &#34;state&#34;: {
      &#34;status&#34;: [&#34;AVAILABLE&#34;]
    }
  }
}
EOF
}

resource &#34;aws_cloudwatch_event_target&#34; &#34;trigger_lambda&#34; {
  rule      = aws_cloudwatch_event_rule.imagebuilder_completed.name
  target_id = &#34;LaunchTemplateUpdater&#34;
  arn       = aws_lambda_function.update_launch_template.arn
}
</code></pre><p>This creates an EventBridge rule that is triggered every time your EC2 Image Builder pipeline successfully produces an AMI with a status of &lsquo;AVAILABLE&rsquo;.</p>
<p>That rule in turn triggers the Lambda function shown above.
So now we&rsquo;ll have this sequence:</p>
<ol>
<li>EC2 Image Builder runs and produces a new Image</li>
<li>It also updates the SSM Parameter store with the latest AMI ID</li>
<li>When the new AMI is updated to be &lsquo;AVAILABLE&rsquo;, our EventBridge Rule is triggered.</li>
<li>The EventBridge rule in turn runs the Lambda function to retrieve this new ID and create a new Launch Template version.</li>
</ol>
<p>The result is that when the ASG next scales up, it&rsquo;ll use the latest AMI ID.<br>
It also means that on the next Terraform run it&rsquo;ll pull the latest AMI ID from SSM and recognise that no change is required.</p>
<h2 id="conclusion">Conclusion</h2>
<p>If you want to use EC2 Image Builder to manage AMIs for an ASG, all but the most simple use cases will require some extra plumbing when it comes to handling the Launch Template configuration.<br>
In those more advanced scenarios a method as highlighted here will be effective in handling the synchronisation between Terraform&rsquo;s view of the world and ensuring the Launch Template is always up to date (with a useful Instance Profile.)</p>
<h2 id="additional-resources">Additional Resources</h2>
<ul>
<li><a href="https://github.com/jmcmillan1873/autoscaling-with-imagebuilder">Complete Auto Scaling Group with EC2 Image Builder Example</a></li>
<li><a href="https://docs.aws.amazon.com/imagebuilder/latest/userguide/tutorial-ssm-parameters-recipe.html">EC2 Image Builder &amp; SSM</a></li>
</ul>
<hr>
<p><em>This post is part of my AWS technical blog series. Found this helpful? Connect with me on <a href="https://www.linkedin.com/in/john-mcmillan-aaa4274/">LinkedIn</a> or check back here for more AWS content and discussions.</em></p>
]]></content:encoded><category>AWS</category><category>EC2 Image Builder</category><category>Auto Scaling Group</category><category>AWS</category><category>EC2</category><category>Auto Scaling Group</category><category>EC2 Image Builder</category><category>AWS EC2 Image Builder</category><category>AWS Auto Scaling Group</category><category>AWS EventBridge</category><category>AWS Lambda</category><enclosure url="https://aws.mcmillearn.net/images/half-baked-title.png" type="image/png"/></item></channel></rss>