[{"title":"Half Baked Images - Fully Cooked Update","url":"https://aws.mcmillearn.net/posts/half-baked-images-update/","content":"TL;DR Remember that Lambda function I built to work around EC2 Image Builder\u0026rsquo;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.\nA quick recap In Half Baked Images 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 only the AMI ID and account ID. Everything else, i.e. instance profile, security groups, key pair, block device mappings was gone.\nThe result? Your ASG would scale up instances with no instance profile, no security groups, and none of the configuration you\u0026rsquo;d carefully defined. Not ideal.\nMy solution was a two-parter:\nUse an SSM Parameter as the source of truth for the AMI ID, updated by Image Builder\u0026rsquo;s Distribution Settings. Deploy a custom Lambda function, triggered by EventBridge, that would read the latest AMI ID from SSM and create a proper new Launch Template version. One that actually included all the important bits. It worked. It was reliable. But it was also plumbing that shouldn\u0026rsquo;t have been necessary.\nWhat changed? AWS have updated the EC2 Image Builder Distribution Settings behaviour. The documentation now states:\nWhen launchTemplateConfigurations 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.\nThe important bit there: All of the original settings from the template.\nThat\u0026rsquo;s the bit that was missing before. Previously, Image Builder would create a new Launch Template version with only 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.\nThis is exactly the behaviour I expected when I first configured the launch_template_configuration block in my distribution config. It\u0026rsquo;s what most people would reasonably expect. And now it actually works that way.\nWhat does this mean practically? If you followed my original approach, you can simplify things significantly.\nWhat you can remove The Lambda function that created new Launch Template versions with the full configuration. The EventBridge rule that triggered that Lambda on successful image builds. The associated IAM roles and policies for the Lambda function. Any CloudWatch log groups you set up for the Lambda. That\u0026rsquo;s a decent chunk of infrastructure that existed purely to work around a limitation that no longer exists.\nWhat you should keep The SSM Parameter 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. The lifecycle ignore on the SSM Parameter value in Terraform, this is still needed so Terraform doesn\u0026rsquo;t fight with Image Builder over who owns that value. What to add back You can now use the launch_template_configuration block in your distribution config with confidence:\nresource \u0026#34;aws_imagebuilder_distribution_configuration\u0026#34; \u0026#34;dist\u0026#34; { name = \u0026#34;${var.project}-dist\u0026#34; distribution { region = var.region ami_distribution_configuration { name = \u0026#34;${var.project}-{{imagebuilder:buildDate}}\u0026#34; description = \u0026#34;Custom built AL2023 image\u0026#34; ami_tags = { OS = \u0026#34;AmazonLinux2023\u0026#34; Hardened = \u0026#34;true\u0026#34; Name = \u0026#34;${var.project}-{{imagebuilder:buildDate}}\u0026#34; } } launch_template_configuration { launch_template_id = aws_launch_template.my_lt.id account_id = local.account_id set_default_version = true } } } Note the set_default_version = true\u0026hellip; 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.\nUpdated lab I\u0026rsquo;ve updated the autoscaling_with_imagebuilder 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.\nIf 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\u0026rsquo;s still useful for the Terraform bootstrapping needs.\nThoughts I\u0026rsquo;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.\nThe fact that AWS have addressed this is a good sign. It suggests they listened to the feedback (and I suspect I wasn\u0026rsquo;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 was the problem, not my expectations.\nOne less Lambda in the world. Now if AWS could just make their Lifecycle Policies stop nuking AMIs that are still in use, I might be able to retire the other Lambda too. A man can dream.\nAdditional Resources Half Baked Images - original blog post Half Baked Lifecycles - follow up blog post Updated Auto Scaling Group with EC2 Image Builder Lab EC2 Image Builder - Configure AMI distribution with a launch template This post is part of my AWS technical blog series. Found this helpful? Connect with me on LinkedIn or check back here for more AWS content and discussions.\n","summary":"TL;DR Remember that Lambda function I built to work around EC2 Image Builder\u0026rsquo;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.\nA quick recap In Half Baked Images 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 only the AMI ID and account ID. Everything else, i.e. instance profile, security groups, key pair, block device mappings was gone.\n","date":"2026-05-03","categories":["AWS","EC2 Image Builder","Auto Scaling Group"],"tags":["AWS","EC2","Auto Scaling Group","EC2 Image Builder"],"aws_services":["EC2 Image Builder","Auto Scaling Group"],"difficulty":"intermediate","estimated_time":null,"cost_estimate":"$5-15","featured_image":"/images/half-baked-update-title.png"},{"title":"Why Cloud sovereignty suddenly matters.","url":"https://aws.mcmillearn.net/posts/aws-eu-sov-cloud/","content":"Disclaimer AKA: Are you sure you want to read this?\nOkay - so this isn\u0026rsquo;t my typical technical approach to blogging. This topic is regulatory heavy - unavoidably so if you\u0026rsquo;re to understand and navigate it. You have been warned :).\nIntro You might have noticed the world feels slightly less keen on being stable and predictable. Geopolitics, trade, and national interest are clashing more openly. Cloud computing is an area where we see some of the downstream consequences as a result. The \u0026lsquo;Cloud\u0026rsquo; has a truly global presence, and so perhaps it\u0026rsquo;s unsurprising that it isn\u0026rsquo;t immune from politics. Companies like AWS made it possible for organisations of any size to start small and grow rapidly to a huge global scale - \u0026ldquo;reach your clients wherever they are\u0026rdquo; - that wasn\u0026rsquo;t by magic. It was because the cloud went global.\nThe cloud, being global by design, blurred many traditional boundaries. In doing so it made possible faster innovation and global reach to anyone with a good idea and the will to act on it. It also quietly blurred some legal and jurisdictional boundaries.\nOver the past 10 years, laws like the CLOUD Act and before that, the PATRIOT Act, have forced business leaders and their legal teams to contemplate some uncomfortable, but legitimate, questions. e.g. \u0026ldquo;Who can access my data, and under what circumstances?\u0026rdquo;.\nSome of these laws create the possibility that their corporate data, user data, or intellectual property held by a US based service provider, could be subject to a lawful access request from US legal authorities. Potentially without the client knowing.\nCompanies like AWS have offered strong assurances that they\u0026rsquo;ll challenge overreach and advise any clients when this happens if they\u0026rsquo;re legally permitted to. And they provide strong mechanisms to protect your data from unauthorised access, e.g. Nitro, which we\u0026rsquo;ll take about later. See \u0026ldquo;How does AWS handle Law enforcement requests?\u0026rdquo; in their CLOUD Act FAQs.\nBut AWS don\u0026rsquo;t need me to fight their corner. They have a few more resources than I have to throw at that.\nEither way, Sovereign Cloud is here. It\u0026rsquo;s up to us as IT professionals to understand what that means for us, our employers, clients, and their users.\nIn this blog I\u0026rsquo;ll look at what Sovereignty is, what it\u0026rsquo;s not - and how to avoid common missteps, e.g. that data residency equates to digital sovereignty. It doesn\u0026rsquo;t.\nSovereignty The Oxford English dictionary defines Sovereignty as\u0026hellip;. no, that would be awful. :)\nDigital Sovereignty is about a few things.\nControl - which outsiders can impact you, derail your business, or access your data? Jurisdiction - which legal systems have authority over your data, applications, and services? Auditability - who\u0026rsquo;s done what, where, and when - and can I prove that to auditors? Security - how do I protect my data and access to my systems? Accountability - who am I answerable to? Users, regulators, shareholders, etc Residency - where is my data located? Operations - who manages, operates, and maintains my systems? You might be thinking, some of those overlap. Yup. The factors that contribute to Sovereignty do overlap somewhat.\nSo think about it this way:\nIf your important data is classified correctly, you know where it is and it\u0026rsquo;s confined to where you want it to be, you understand how to control access to it, you can audit who\u0026rsquo;s had access, you know who you\u0026rsquo;re subject to in terms of regulatory bodies \u0026amp; compliance frameworks, who needs access to your systems, who has access to your systems, then you\u0026rsquo;re in a good position.\nIf you answered positively to those then you\u0026rsquo;re well on the way to understanding what Sovereignty is, and which aspects apply to you.\nWhat Sovereignty isn\u0026rsquo;t Data residency (location) is not Digital Sovereignty. It\u0026rsquo;s likely that some clients will ask for Sovereign solutions when in actual fact, they\u0026rsquo;re really concerned about data residency and security. Helping those clients understand the difference between where data can be stored and secured (Residency) and who can legally compel access (legal Jurisdiction) is a difference to be understood.\nMake my solution fully Sovereign - retrospectively\nIf a client states that they want to keep their existing solution but \u0026ldquo;make it Sovereign\u0026rdquo;\u0026hellip; that\u0026rsquo;s not necessarily impossible, but it\u0026rsquo;s tricky. Sovereignty is a design time decision. To achieve the best possible sovereign posture for your solution, the design needs to factor in the Sovereignty objectives and address each where possible. (good luck with Supply Chain Sovereignty).\nI want to increase my Sovereignty without compromise\nFor some clients, the appeal of being sovereign and independent from foreign owned companies will be appealing.\nEU Sentiment I won\u0026rsquo;t spend time talking about the Schrems II ruling, but I\u0026rsquo;d recommend having a look into that for some context - it gets to the heart of some of the trust issues that are at the centre of the CLOUD Act, conflicting national interests, and the EU position on the subject.\nSuffice it to say, the EU have been concerned for some time about their belief that a lack of equivalence exists in data protection laws between the EU and US, and this creates risk for EU based organisations using US based cloud providers.\nOn the point of trust — it’s an important distinction to make, but the European Commission created the EU Sovereign Cloud framework because the EU considers foreign legal reach to be a risk that could undermine things like GDPR, not because it distrusts cloud technology.\nOrganisations that are subject to foreign legal obligations, such as the US CLOUD Act, may be viewed through a sovereignty lens as presenting a potential avenue for legal overreach.\nEnter the EU Cloud Sovereign Framework It\u0026rsquo;s no surprise then that the EU decided to produce a framework that helps organizations assess their current sovereignty posture and to provide some clear guidance and metrics in the form of the EU Cloud Sovereignty Framework.\nWhat does the framework define It\u0026rsquo;s definitely worth reading the framework, it\u0026rsquo;s actually very concise considering what it is, but in the meantime, I\u0026rsquo;ll summarise here:\nSEAL scores The document explains the concept of SEAL scores - Sovereignty Effectiveness Assurance Level.\nThere are eight contributing \u0026lsquo;Sovereignty Objectives\u0026rsquo;, and a related formula that accounts for different weightings, that provide the inputs for calculating the SEAL score of a platform or solution.\nSovereign Objectives (the factors) Strategic Sovereignty - This assesses the supplier\u0026rsquo;s ownership stability, governance influence, \u0026amp; alignment with EU Strategic priorities. Legal \u0026amp; Jurisdictional Sovereignty - Evaluates the legal environment, exposure to foreign authority, \u0026amp; enforceability of rights. Data \u0026amp; AI Sovereignty - Focuses on how data and AI services are secured, protected, controlled, where they\u0026rsquo;re located, and where they\u0026rsquo;re processed from. Operation Sovereignty - Assess the ability of EU based organizations to support, patch, maintain, service, and evolve a technology or solution independent of non-EU control. Supply Chain Sovereignty - Evaluates the geographic origin, transparency, and resilience of the supply chain, with a specific focus on the extent critical components are or aren\u0026rsquo;t controlled by the EU. Technology Sovereignty - Considers the degree of transparency and independence in the technology stack. Can EU organizations, operate the technology, audit it, and evolve it without lock-in to foreign owned systems. Security \u0026amp; Compliance Sovereignty - Assesses the extent to which security operations, compliance obligations, and resilience are controlled within the EU, without reliance on foreign jurisdictions. Environmental Sustainability - This assesses the autonomy \u0026amp; resilience of cloud services over the long term in relation to energy usage, dependency, and raw material security. (aka - can you be Sovereign if you\u0026rsquo;re not energy independent and someone else can switch off the lights?) The framework goes into more detail about some of the specific examples and criteria they look for and assess in each of those factors. At this stage I just wanted to give you some relevant context to frame the Hyperscaler response, where it works and where it doesn\u0026rsquo;t.\nHyperscaler response I\u0026rsquo;ve stated outright that I personally operate in the AWS arena - other hyperscalers are taking this seriously too - but I\u0026rsquo;ll talk about how AWS are now facing up to the sovereignty challenge in the EU.\nEnter EU AWS Sovereign Cloud. AWS first announced plans for their EU Sovereign Cloud business in October 2023. In January 2026, following €8 billion investment, AWS formally announced general availability of its EU Sovereign Cloud located in Germany.\n\u0026ldquo;The AWS European Sovereign Cloud is a new, independent cloud for Europe entirely located within the European Union (EU), designed to help customers meet their evolving sovereignty requirements.\u0026rdquo;\nAWS conceived and built the EU Sovereign Cloud to be \u0026ldquo;Sovereign-by-design\u0026rdquo;. AWS have worked with various EU organizations to gather the needs of clients who are interested in Sovereign Cloud. In that sense it\u0026rsquo;s a genuine attempt at a purpose built Sovereign Cloud. You can see the thought that\u0026rsquo;s gone into that when you look at service availability. For example, at the time I write this there are only two Foundational Models available in eusc-de-east-1 under Amazon Bedrock - \u0026lsquo;Nova Lite\u0026rsquo; and \u0026lsquo;Nova Pro\u0026rsquo;.\nIs that just taking time to roll out services and features, or is it regulation making vendors think carefully and seriously about risk, compliance, \u0026amp; governance?\nIf you\u0026rsquo;re already familiar with AWS, then EU Sovereign Cloud won\u0026rsquo;t initially look any different. In many respects it looks and behaves like any other AWS Region. As with AWS Regions, there\u0026rsquo;s variation in number of Availability Zones (eusc-de-east-1 has two), variation in service/feature availability, and pricing differences.\nThe sole purpose of AWS\u0026rsquo;s investment in the EU Sovereign Cloud is to offer, as close as is achievable, a truly sovereign platform for the EU. In their words, from the\nDoes it manage that? Let\u0026rsquo;s look first at how AWS\u0026rsquo; EU Sovereign cloud measures up to the framework:\nStrategic Sovereignty\nAWS EU Sovereign Cloud is owned by \u0026ldquo;AWS European Sovereign Cloud GmbH\u0026rdquo;, a German registered company - who are themselves 100% owned the US based Amazon. Although, the AWS EU board is entirely based in the EU though and comprised of EU Citizens. This means they\u0026rsquo;ll be able to get involved in EU initiatives, and they\u0026rsquo;ll be able to sustain the service in the EU even if the US government did something unthinkable like instructing Amazon to stop offering services in the EU. AWS have made some pretty firm and public commitments here, saying:\n\u0026ldquo;AWS established an independent advisory board for the AWS European Sovereign Cloud, legally obligated to act in the best interest of the AWS European Sovereign Cloud. Reinforcing the sovereign control of the AWS European Sovereign Cloud, the advisory board will consists of five members, all EU citizens residing in the EU, including at least two independent board members who are not affiliated with Amazon. The advisory board will act as a source of expertise and provide accountability on sovereignty-related aspects of the AWS European Sovereign Cloud operations, including strong security and access controls and the ability to operate independently in the event of disruption.\u0026rdquo;\nTherefore, compared to AWS Public Cloud, AWS EU Sovereign Cloud offers a significant improvement in this regard, but it wouldn\u0026rsquo;t score as highlight as truly owned EU organization with no ties or ownership outside of the EU.\nLegal \u0026amp; Jurisdictional Sovereignty\nAWS have done a good job of keeping the \u0026ldquo;AWS European Sovereign Cloud GmbH\u0026rdquo; entity at arm\u0026rsquo;s length. Physically and logically isolated from wider AWS. All staff are EU residents, and the board are entirely EU citizens. AWS state in their Sovereign Cloud overview that:\nAs part of the technical design, access to the AWS European Sovereign Cloud physical infrastructure and logical system is managed by Qualified AWS European Sovereign Cloud Staff and can only be granted to Qualified AWS European Sovereign Cloud Staff located in the EU. AWS European Sovereign Cloud restricted data will not be accessible, including to AWS employees, from outside the EU.\nSo only AWS staff employed in the EU have any sort of access.\nIs that enough to say there isn\u0026rsquo;t a loophole that means the FBI can\u0026rsquo;t petition AWS in the US to get at data? It appears that would require compelling EU employees to brake local law, which doesn\u0026rsquo;t seem likely.\nTruthfully - I think we\u0026rsquo;ll need to see this tested in the courts to know whether this is as robust as we hope or not, but we might be waiting a while\u0026hellip;\nThe CLOUD Act has resulted in zero disclosures of AWS enterprise or government customer content stored outside the U.S. to the U.S. government, since we started reporting the statistic in 2020.\nData \u0026amp; AI\nAWS EU Sovereign Cloud offer a lot to assure clients in this respect, whether on AWS EU Sovereign Cloud or AWS Public Cloud. There are strong controls in places to allow clients to secure access and auditing to their data to a high degree. Factor in things like AWS Nitro which provides strong isolation between tenants. In fact, Nitro Isolation prevents anyone other than the client accessing instances - even AWS Staff have no access to customer Instances. Nitro offloads networking, storage, and management functions to dedicated hardware, it significantly reduced the attack surface of the Hypervisor, and all admin access is prohibited.\nAWS EU Sovereign Cloud is independently audited and verified to various compliance standards - including attestations for Nitro.\nOperational Sovereignty\nAWS EU Cloud staff is entirely comprised of EU residents, and AWS have committed that this will be EU citizens located in the EU shortly. No non-EU residents will have access to the EU Sovereign Infrastructure.\nWho clients choose to support the solutions they deploy on EU Sovereign Cloud is entirely in their control - but AWS have closed the door on this one.\nSupply Chain Sovereignty\nOkay. This is a difficult one to check and satisfy. The EU framework refers to a few aspects of supply chain including where key components are manufactured. But it doesn\u0026rsquo;t clarify what it means by key.\nThink of all the devices involved in a typical solution (regardless of where it is) - cpu, PSU, motherboards, network cards, storage devices, adapters, cables, switches, firewalls, gpus, etc - and how many chips and electronics in each of those?\nI couldn\u0026rsquo;t find much clarity in AWS published material on this point. AWS does publish Supply Chain Standards which includes relevant information - but it\u0026rsquo;s quiet on where components come from.\nMy personal belief is that this is a complicated topic even for behemoth organizations like AWS - and the reality is the growing demand for full digital sovereignty shines on a light on an interconnected, complex, global supply chain - which means this is currently an extremely difficult lens to be viewed as Sovereign.\nOrganizations will have to make individual decisions about how far they progress in this direction, or except their partners and suppliers to.\nTechnology Sovereignty\nTransparency, auditability, and avoiding proprietary lock in is the objective here. i.e. Are you using software that can be transparently audited? Can you influence the direction of the code. Could someone else prevent you using that code?\nMany AWS services are based on Open Source software, or software that has functional Open Source equivalents, and lock-in is something to be managed anyway when deploying to a cloud - but there are ways to manage that so you\u0026rsquo;re not entirely dependent on a cloud platform - especially for containerised workloads.\nBut AWS in the USA are still responsible for the upstream code. So even though the EU is independent in some ways, it\u0026rsquo;s still seen to be reliant on the US entity for that upstream code.\nAgain, Organizations will have to decide on the right balance of convenience and compliance in terms of transparency, and whether the improved independence of the AWS EU Sovereign cloud is sufficient.\nSecurity \u0026amp; Compliance Sovereignty\nAWS have traditionally done well in this space. The AWS EU cloud is no exception. It has the same security and permissions models as the AWS Public Cloud. e.g. IAM, Organizations, SCPs, etc - but is regionally scoped, implemented, and operated.\nServices like Amazon GuardDuty \u0026amp; Security Hub are partially available - some features are on tap today; others planned for the EU Cloud. Check Builder Centre for service/feature availability in eusc-de-east-1.\nAmazon Nitro enables no operator access - coupled with no access to anyone outside the EU - this is a strong position for AWS EU Sovereign Cloud.\nEnvironmental Sustainability\nAWS do well here also. AWS have a strong message on circular economy, energy efficient data centre, and sustainability. They\u0026rsquo;re on track to meet targets to be water positive by 2030 and net-zero carbon by 2040.\nThis is an area that\u0026rsquo;s weighted lowly in the EU framework though - at only 5%.\nAWS EU Sovereign Cloud vs AWS Public Cloud How do the two platform compare then?\nAs already pointed out, where a service exists on EU Sovereign Cloud, it\u0026rsquo;s\nFeature / usage comparison At a high level some of the differences appear quite quickly:\nAWS Public Cloud AWS EU Sovereign Cloud What it\u0026rsquo;s optimised for Speed, Scale, Global Reach Legal \u0026amp; operational Sovereignty (EU) Strong Points Rapid innovation, Full AWS Service catalogue, Huge elastic scale, Low cost to entry EU Only operations, Reduced foreign legal exposure, AWS Native experience Limitations Jurisdictional ambiguity, cost predictability requires focus, easy to over engineer Smaller \u0026amp; slower moving service set, narrow Geographic scope, High cost than AWS Public Regions Use cases Digital native apps, SaaS platforms, Data analytics \u0026amp; AI/ML, Global customer solutions, Strong governance \u0026amp; compliance requirements Public Sector \u0026amp; Critical National Infrastructure, Strong Sovereignty Requirements, FIS, National Data platforms, High assurance workloads Bad fits Strong EU Sovereignty Requirements, Data platforms subject to strict national control Non Sovereign workloads with tight budgets, Rapid global expansion Sovereignty Effectiveness Assurance Level The formula to calculate SEAL Scores is explained in the EU Sovereign Compliance Framework - refer to that for details. To my mind scoring is subjective to a point You can review the framework, review the platform capabilities, and decide how close you feel there are or aren\u0026rsquo;t and score accordingly.\nI won\u0026rsquo;t show my working here for various mandated commercial reasons - but I\u0026rsquo;ll share the outcomes. My personal opinion after consideration resulted in me viewing the platforms to be:\nAWS Public Cloud - SEAL 2 AWS EU Sovereign Cloud - SEAL 3 Final thoughts The AWS EU Sovereign Cloud is a strong option for clients with strong Sovereignty requirements, but it isn\u0026rsquo;t the right choice for everyone. It\u0026rsquo;s not a one-size-fits-all solution.\nGiven AWS prices usually fluctuate by region, it\u0026rsquo;s difficult to accurately compare the cost difference with EU cloud. I deploy in eu-west-1 \u0026amp; eu-west-2 a lot, so I compared with my costs there and found the price was in the region of 15-20% more in eusc-de-east-1. That\u0026rsquo;s enough to be noticed and given the improved Sovereignty posture over AWS Public cloud, that feels reasonable to me. It also feels just high enough that it\u0026rsquo;ll help AWS clients decide if they want improved compliance, security, and data control (which they could likely do in their existing environments) or whether they really need a full sovereign platform.\nThe highest SEAL rating is \u0026lsquo;4\u0026rsquo;. I think as we see other platforms being assessed - 4 will be awarded rarely. The framework doesn\u0026rsquo;t easily allow for that in a global economy with global supply chains.\nBoth AWS Cloud and Sov Cloud lose points in \u0026lsquo;Strategic\u0026rsquo;, \u0026lsquo;Supply chain\u0026rsquo;, and \u0026lsquo;Technology\u0026rsquo; sovereignty objectives.\nHowever, AWS EU Sov cloud does significantly improve the position on \u0026lsquo;Legal \u0026amp; Jurisdictional\u0026rsquo;, \u0026lsquo;Operational\u0026rsquo;, \u0026amp; \u0026lsquo;Data \u0026amp; AI\u0026rsquo; sovereignty objectives.\nThe small caveat is that the scoring of these is somewhat subjective (hence I stressed above my personal opinion) and clients will decide themselves what they need. It\u0026rsquo;s our job to understand the differences, articulate those, and design \u0026amp; build accordingly.\nAdditional Resources EU Cloud Sovereignty Framework Five facts about how the CLOUD Act actually works Overview of the AWS EU Sovereign Cloud This post is part of my AWS technical blog series. Found this helpful? Connect with me on LinkedIn or check back here for more AWS content and discussions.\n","summary":"Disclaimer AKA: Are you sure you want to read this?\nOkay - so this isn\u0026rsquo;t my typical technical approach to blogging. This topic is regulatory heavy - unavoidably so if you\u0026rsquo;re to understand and navigate it. You have been warned :).\nIntro You might have noticed the world feels slightly less keen on being stable and predictable. Geopolitics, trade, and national interest are clashing more openly. Cloud computing is an area where we see some of the downstream consequences as a result. The \u0026lsquo;Cloud\u0026rsquo; has a truly global presence, and so perhaps it\u0026rsquo;s unsurprising that it isn\u0026rsquo;t immune from politics. Companies like AWS made it possible for organisations of any size to start small and grow rapidly to a huge global scale - \u0026ldquo;reach your clients wherever they are\u0026rdquo; - that wasn\u0026rsquo;t by magic. It was because the cloud went global.\n","date":"2026-01-27","categories":["AWS","Sovereign Cloud","Compliance"],"tags":["AWS","EU Sov Cloud","EU Sovereign Cloud","Sovereign","Compliance"],"aws_services":["AWS EU Sovereign Cloud","AWS Nitro"],"difficulty":"intermediate","estimated_time":null,"cost_estimate":"$$$","featured_image":"/images/aws-eu-sov-banner.png"},{"title":"Half Baked Lifecycles","url":"https://aws.mcmillearn.net/posts/half-baked-lifecycles/","content":"Intro In my previous post, Half Baked Images I walked through building hardened AMIs using Amazon EC2 Image Builder, automating the pipeline, and wiring it into Launch Templates for Auto Scaling Groups. I touched on some aspects that felt almost, but not quite useful. In this post I\u0026rsquo;ll talk about my attempts to keep on top of the images produced and why (spoiler!) I ultimately chose Lambda over built-in Image Builder functionality.\nTask: Stop AMIs and snapshots from piling up AWS has an answer for that, of course: Image Builder lifecycle policies.\nThis should be easy (AKA Foreshadowing).\nWhen asked \u0026ldquo;What does Lifecycle management in Image Builder do?\u0026rdquo;, Amazon Q gave this answer:\n\u0026ldquo;The lifecycle manager implements automated rules to manage your image resources throughout their lifecycle, helping ensure image freshness while minimizing costs for underlying infrastructure like snapshot storage for AMIs or ECR repository storage for container images.\u0026rdquo;\nI understand all those words so this is going to be easy peasy.\nLifecycle management made\u0026hellip; confusing. What I didn’t anticipate was how surprisingly awkward it would be to implement what felt like a very simple solution.\nUnfortunately, the behaviour of those policies was less intuitive than the name suggested\u0026hellip; to me at least.\nThis post is about the assumptions I made, why they were wrong, what Image Builder lifecycle actually does, and why I ultimately replaced it with a small Lambda function that behaved better for my use case.\nInitial approach I started with a simple goal:\nRetain the last 5 AMIs produced by Image Builder.\nDelete anything older, including associated snapshots.\nImage Builder has a native lifecycle feature. Terraform exposes it cleanly. This felt so obvious that I didn’t question it. The console UI even said things like:\nSo I configured a lifecycle policy in terraform like this:\nresource \u0026#34;aws_imagebuilder_lifecycle_policy\u0026#34; \u0026#34;retain_5_images\u0026#34; { name = \u0026#34;${var.project}-lifecycle-policy\u0026#34; description = \u0026#34;Retain only the 5 most recent AMIs created by Image Builder\u0026#34; execution_role = aws_iam_role.imagebuilder_role.arn resource_type = \u0026#34;AMI_IMAGE\u0026#34; resource_selection { tag_map = { Project = var.project ManagedBy = \u0026#34;AWSImageBuilder\u0026#34; } } policy_detail { action { type = \u0026#34;DELETE\u0026#34; include_resources { amis = true containers = false snapshots = true } } filter { type = \u0026#34;COUNT\u0026#34; # or \u0026#34;COUNT\u0026#34; value = \u0026#34;5\u0026#34; } } } Note: This wasn\u0026rsquo;t my first attempt, this was the first that ran correctly.\nIt took me a week to get it to run. See Assumption #1.\nEven now, recovering from the savage AMI cull of \u0026lsquo;25, I still read that code and think the policy should:\nTake the DELETE action for resources type \u0026ldquo;AMI_IMAGE\u0026rdquo;\u0026hellip; also including resource types AMIs and snapshots, for those AMI_IMAGEs that matched\u0026hellip; tags Project \u0026amp; ManagedBy, whilst retaining\u0026hellip; Filter: COUNT = 5 Therefore I expected the outcome to be:\nFind Images were Project = var.project and ManagedBy = \u0026quot;AWSImageBuilder\u0026quot;\nIf there are ≤5 images that match that criteria → delete nothing\nIf there are 6 → delete the oldest 1\nIf there are 18 → delete the oldest 13\netc. Simple. Predictable. Safe.\nThat is not what happened.\nBut what went wrong?\nAssumption is the mother of all\u0026hellip; fowl ups. Assumption #1 My first assumption was that I would have some control over when the lifecycle management would run. Similar to controlling the pipeline schedule, I\u0026rsquo;d be able to control a lifecycle management schedule.\ne.g. I\u0026rsquo;d be able to define a cron schedule or trigger it to run immediately after a new image became AVAILABLE. Nope.\nImage Builder Lifecycle policies run on an internal AWS schedule, completely decoupled from the triggers or control I expected. There is also no \u0026ldquo;Run now\u0026rdquo; button to force an evaluation.\nThis had two consequences that significantly impacted my use case:\nTesting is non-linear.\nUnsurprisingly my lifecycle didn\u0026rsquo;t work on the first try. So, when I attempted to fix it, I had to wait 24 hours to see if that had worked.\nTesting, that could have taken a couple of hours, took days to work through as a result of this approach.\nTroubleshooting was complex and misleading\nI was working on a few aspects of the code simultaneously, so by the time my lifecycle ran each day I had usually made several changes.\nTags had been added or changed IAM permissions were tightened Images components were changing, meaning recipes had been updated. The combination of those two realities made it very difficult to figure out cause and effect: Was lifecycle management failing because I\u0026rsquo;d borked the config? Was it related to my tagging changes? or the new recipe?\nsigh.\nAssumption #2 My second assumption was that the lifecycle policy in Image Builder relates to the images built by Image Builder\u0026hellip; that is to say - the AMIs.\nSensible assumption? I thought so\u0026hellip; and if you did too\u0026hellip; join me in my pit of despair because, as we\u0026rsquo;ll get to in a moment, this was not the case.\nAssumption #3 My third, and what proved to be my most costly assumption, was that I\u0026rsquo;d provided enough details to safely identify the images to be deleted and that only those Images would be deleted. That this turned out to be incorrect was the real sore point - this one wasn\u0026rsquo;t easy to predict or see coming until it went wrong.\nSo what actually happened? When my lifecycle policy ran, it removed all AMIs. Everything.\nIncluding AMIs still in use by my Auto Scaling Group\u0026rsquo;s Launch Template.\nNothing useful remained. Zero AMIs, zero snapshots.\nThankfully this occurred in a lab environment so wasn\u0026rsquo;t too impactful\u0026hellip; but it did give me chills. I looked over the code, at the resources, and the lifecycle policy in the console, etc. It all looked correct. It seemed to make sense - but here I was with no useable images left.\nAnd at the moment of discovering this issue, even from that point, it took a good few hours to understand why it had gone so badly wrong.\nTo say I was a bit surprised at the outcome is putting it mildly.\nThis could not be allowed to happen in an environment that matters to me.\nWhere did the assumptions fall down? Not having control over the scheduling of the lifecycle policy was simultaneously a relatively minor issue and almost enough to be a deal breaker on its own. Who\u0026rsquo;s happy waiting 24 hours to test configuration changes in a lab? Fail fast isn\u0026rsquo;t an option, if you have to wait 24 hours to test each change.\nMy assumption that my lifecycle policy was targeting my AMI\u0026rsquo;s directly was incorrect. This one really mattered.\nEC2 Image Builder Lifecycle Policies target Image builder resources, e.g. things with arns like arn:aws:imagebuilder:eu-west-2:123456789012:image/custom-linux-build/1.0.6/3.\nThis is a subtle but important distinction to understand when you\u0026rsquo;re thinking about how Image Builder Lifecycle policies identify targets. You can, as I did in the code above, state that it should include amis = true \u0026amp; snapshots = true. But, again, the AMIs and snapshots are secondary in this.\nAssuming I\u0026rsquo;d provided the correct selection criteria was compounded by the previous assumption about what the lifecycle policies target.\nI mentioned that after my lifecycle policy ran I had zero AMI\u0026rsquo;s and zero snapshots.\nBut I did still have 5 \u0026ldquo;images\u0026rdquo;. I still had 5 Image Builder Image\u0026rsquo;s retained. Although the AMI\u0026rsquo;s that they pointed to were gone.\nYup, I swore inwardly at this point.\nBringing it home I asked Amazon Q why it would do this (to me), and in its reply it had this little unhelpful truth bomb:\n\u0026ldquo;Even if the Image Builder Image resources were retained, if your policy had:\u0026quot;includeResources\u0026quot;: { \u0026quot;amis\u0026quot;: true, \u0026quot;snapshots\u0026quot;: true } This would delete the underlying AMIs and snapshots while potentially keeping the Image Builder metadata.\u0026rdquo;\nI swore outwardly at this point. Loudly.\nThis ia an awful failure condition by any stretch. I accept that this was my configuration, and I am responsible for this, it still feels like this was a booby trap waiting to go off rather than something you could reasonably foresee \u0026amp; avoid.\nSo what did I do about it After a fair amount of frustration, confusion, and lost AMIs I decided to abandon the Image Builder lifecycle policy approach and turned to AWS Lambda - what can\u0026rsquo;t Lambda fix?\nI created a small function that I deployed using Terraform, as follows:\nimport os import json import boto3 import logging from datetime import datetime, timezone logger = logging.getLogger() logger.setLevel(logging.INFO) ec2 = boto3.client(\u0026#34;ec2\u0026#34;) RETAIN_COUNT = int(os.getenv(\u0026#34;RETAIN_COUNT\u0026#34;, \u0026#34;5\u0026#34;)) PROJECT_TAG_VALUE = os.getenv(\u0026#34;PROJECT_TAG_VALUE\u0026#34;, \u0026#34;labpoc\u0026#34;) MANAGED_BY_VALUE = os.getenv(\u0026#34;MANAGED_BY_VALUE\u0026#34;, \u0026#34;AWSImageBuilder\u0026#34;) LAUNCH_TEMPLATE_ID = os.getenv(\u0026#34;LAUNCH_TEMPLATE_ID\u0026#34;) # required for protection DRY_RUN = os.getenv(\u0026#34;DRY_RUN\u0026#34;, \u0026#34;true\u0026#34;).lower() == \u0026#34;true\u0026#34; def _parse_dt(s: str) -\u0026gt; datetime: return datetime.fromisoformat(s.replace(\u0026#34;Z\u0026#34;, \u0026#34;+00:00\u0026#34;)).astimezone(timezone.utc) def _list_matching_amis(): filters = [ {\u0026#34;Name\u0026#34;: \u0026#34;tag:Project\u0026#34;, \u0026#34;Values\u0026#34;: [PROJECT_TAG_VALUE]}, {\u0026#34;Name\u0026#34;: \u0026#34;tag:ManagedBy\u0026#34;, \u0026#34;Values\u0026#34;: [MANAGED_BY_VALUE]}, {\u0026#34;Name\u0026#34;: \u0026#34;state\u0026#34;, \u0026#34;Values\u0026#34;: [\u0026#34;available\u0026#34;]}, ] resp = ec2.describe_images(Owners=[\u0026#34;self\u0026#34;], Filters=filters) images = resp.get(\u0026#34;Images\u0026#34;, []) images.sort(key=lambda i: _parse_dt(i[\u0026#34;CreationDate\u0026#34;]), reverse=True) # newest first return images def _snapshots_for_ami(image: dict) -\u0026gt; list[str]: snaps = [] for bdm in image.get(\u0026#34;BlockDeviceMappings\u0026#34;, []): ebs = bdm.get(\u0026#34;Ebs\u0026#34;) if ebs and ebs.get(\u0026#34;SnapshotId\u0026#34;): snaps.append(ebs[\u0026#34;SnapshotId\u0026#34;]) return snaps def _summarise_images(images: list[dict], max_items: int = 50) -\u0026gt; list[dict]: out = [] for img in images[:max_items]: out.append({ \u0026#34;ImageId\u0026#34;: img.get(\u0026#34;ImageId\u0026#34;), \u0026#34;CreationDate\u0026#34;: img.get(\u0026#34;CreationDate\u0026#34;), \u0026#34;Name\u0026#34;: img.get(\u0026#34;Name\u0026#34;), }) if len(images) \u0026gt; max_items: out.append({\u0026#34;note\u0026#34;: f\u0026#34;truncated: showing {max_items} of {len(images)}\u0026#34;}) return out def _amis_referenced_by_latest_lt_versions(launch_template_id: str, protect_versions: int) -\u0026gt; set[str]: \u0026#34;\u0026#34;\u0026#34; Protect AMIs referenced by the *latest N* launch template versions. \u0026#34;\u0026#34;\u0026#34; protected = set() # Fetch versions (we\u0026#39;ll sort and take the latest N) versions = [] paginator = ec2.get_paginator(\u0026#34;describe_launch_template_versions\u0026#34;) for page in paginator.paginate(LaunchTemplateId=launch_template_id): versions.extend(page.get(\u0026#34;LaunchTemplateVersions\u0026#34;, [])) if not versions: return protected # Sort by VersionNumber descending and take latest N versions.sort(key=lambda v: v.get(\u0026#34;VersionNumber\u0026#34;, 0), reverse=True) latest = versions[:protect_versions] for v in latest: data = v.get(\u0026#34;LaunchTemplateData\u0026#34;, {}) image_id = data.get(\u0026#34;ImageId\u0026#34;) if image_id: protected.add(image_id) return protected def lambda_handler(event, context): logger.info( \u0026#34;Starting AMI retention. retain=%s project=%s managedby=%s dry_run=%s lt_id=%s\u0026#34;, RETAIN_COUNT, PROJECT_TAG_VALUE, MANAGED_BY_VALUE, DRY_RUN, LAUNCH_TEMPLATE_ID ) if RETAIN_COUNT \u0026lt; 1: raise ValueError(\u0026#34;RETAIN_COUNT must be \u0026gt;= 1\u0026#34;) images = _list_matching_amis() total = len(images) logger.info(\u0026#34;Found %s matching AMIs.\u0026#34;, total) logger.info(\u0026#34;Matched AMIs (newest-\u0026gt;oldest): %s\u0026#34;, json.dumps(_summarise_images(images))) if total \u0026lt;= RETAIN_COUNT: logger.info(\u0026#34;\u0026lt;= %s matching AMIs; deleting none.\u0026#34;, RETAIN_COUNT) return {\u0026#34;statusCode\u0026#34;: 200, \u0026#34;body\u0026#34;: json.dumps({\u0026#34;matching\u0026#34;: total, \u0026#34;deleted\u0026#34;: 0})} protected = set() if LAUNCH_TEMPLATE_ID: protected = _amis_referenced_by_latest_lt_versions(LAUNCH_TEMPLATE_ID, RETAIN_COUNT) logger.info(\u0026#34;Protected AMIs referenced by latest %s Launch Template versions: %s\u0026#34;, RETAIN_COUNT, list(sorted(protected))) else: logger.warning(\u0026#34;LAUNCH_TEMPLATE_ID not set; NOT protecting in-use AMIs (not recommended).\u0026#34;) # Keep newest RETAIN_COUNT (always) keep_newest = {img[\u0026#34;ImageId\u0026#34;] for img in images[:RETAIN_COUNT]} # Eligible = older than newest RETAIN_COUNT AND not protected by LT older = images[RETAIN_COUNT:] candidates = [] for img in older: ami_id = img[\u0026#34;ImageId\u0026#34;] if ami_id in protected: continue candidates.append(img) logger.info(\u0026#34;Candidates for deletion (older than newest %s, not referenced by LT): %s\u0026#34;, RETAIN_COUNT, len(candidates)) deleted = [] for img in candidates: ami_id = img[\u0026#34;ImageId\u0026#34;] snaps = _snapshots_for_ami(img) logger.info(\u0026#34;Deleting AMI %s (snapshots=%s)\u0026#34;, ami_id, snaps) if not DRY_RUN: ec2.deregister_image(ImageId=ami_id) for snap_id in snaps: ec2.delete_snapshot(SnapshotId=snap_id) deleted.append({\u0026#34;ami\u0026#34;: ami_id, \u0026#34;snapshots\u0026#34;: snaps}) logger.info(\u0026#34;Done. Deleted %s AMIs.\u0026#34;, len(deleted)) return {\u0026#34;statusCode\u0026#34;: 200, \u0026#34;body\u0026#34;: json.dumps({\u0026#34;matching\u0026#34;: total, \u0026#34;deleted\u0026#34;: len(deleted), \u0026#34;items\u0026#34;: deleted})} Note: the environment vars, you can set these in terraform when you specify your aws_lambda_function\nenvironment { variables = { RETAIN_COUNT = tostring(var.ami_retain_count) PROJECT_TAG_VALUE = var.project MANAGED_BY_VALUE = \u0026#34;AWSImageBuilder\u0026#34; DRY_RUN = \u0026#34;true\u0026#34; # flip to false when confident it\u0026#39;s working LAUNCH_TEMPLATE_ID = aws_launch_template.wafr_lt.id } } This Lambda function is, in many ways, much simpler than what I thought I’d been doing with the Image Builder Lifecycle Manager… This function:\nLists AMIs by tag (Project, ManagedBy) Sorts them by creation date Keeps the newest N (defined by Terraform variable ami_retain_count) Deregisters anything older Deletes the snapshots referenced by those AMIs Never deletes AMIs currently referenced by the latest N Launch Template (also defined by ami_retain_count.) This runs on a schedule I control. And understand!\nIt\u0026rsquo;s observable, repeatable, and it can be run in dry-run mode while testing.\nMost importantly, the behaviour can be explained in one sentence:\n“We keep the 5 newest AMIs for this project, and we never delete one that’s in use.”\nThat is the rule I thought I was configuring when I attempted to enable lifecycle retention — but now it’s actually true!\nWhy a custom solution won (\u0026amp; why I\u0026rsquo;m okay with that) After working through this in detail, I came to a conclusion that surprised me:\nImage Builder lifecycle policies are not a good fit for intuitive AMI retention.\nThey are:\nmetadata-centric recipe-scoped on their own schedule capable of deleting artifacts without leaving usable AMIs behind (talk about sharp edges!) This doesn’t mean lifecycle policies are broken — but it does make them dangerous if your mental model is “Keep the last N usable AMIs.”\nIn my experience this model doesn’t map cleanly to how Image Builder lifecycle works.\nBy contrast my Lambda function is:\nexplicit - I could set my own boundaries that did what I expected them to do. controlled - it runs when i want. able to add clear and simple guardrails - e.g. don\u0026rsquo;t delete any AMIs still in use, regardless of other filters. Auditable - see what\u0026rsquo;s going on easily in the execution logs. Test friendly. Flip \u0026ldquo;dry-run\u0026rdquo; to \u0026rsquo;true\u0026rsquo; to test what would be deleted before you take the training wheels off. The result was a solution that worked as I expected, trimmed my AMIs and Snapshots to the number I wanted, and stopped my bill from growing forever with the ghost of a million unused snapshots, allowed me to safely test changes when I wanted. And it only cost only a tiny amount in Lambda usage. Worth it.\nFinal thoughts EC2 Image Builder is excellent at building \u0026amp; customising images.\nEventBridge and Lambda are excellent at reacting to them.\nBut AMI retention is an operational concern, not a build concern — and once I treated it that way, the solution became simpler, safer, and easier to understand.\nIf you’re currently using Image Builder lifecycle today and it works for you, that’s great.\nIf, however, your mental model is “keep the last N usable AMIs”, it’s worth pausing and double-checking that the system is actually doing what you think it is.\nSometimes the most robust solution is the one you can explain easily — and without waiting 24 hours to see what it decided to do.\nAdditional Resources Complete EC2 Image Builder with Lifecycle Management Example Amazon EC2 Image Builder AWS Lambda Half Baked Images - previous blog post This post is part of my AWS technical blog series. Found this helpful? Connect with me on LinkedIn or check back here for more AWS content and discussions.\n","summary":"Intro In my previous post, Half Baked Images I walked through building hardened AMIs using Amazon EC2 Image Builder, automating the pipeline, and wiring it into Launch Templates for Auto Scaling Groups. I touched on some aspects that felt almost, but not quite useful. In this post I\u0026rsquo;ll talk about my attempts to keep on top of the images produced and why (spoiler!) I ultimately chose Lambda over built-in Image Builder functionality.\n","date":"2026-01-02","categories":["AWS","EC2 Image Builder","Lambda"],"tags":["AWS","EC2","Auto Scaling Group","EC2 Image Builder"],"aws_services":["EC2 Image Builder","Auto Scaling Group","EventBridge","Lambda"],"difficulty":"intermediate","estimated_time":null,"cost_estimate":"$5-15","featured_image":"/images/Half-baked-lifecycles.png"},{"title":"Half baked Images?","url":"https://aws.mcmillearn.net/posts/half-baked-images/","content":"TL;DR 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\u0026rsquo;s native capabilities for managing Launch Templates.\nWhat were we asked? I\u0026rsquo;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.\nIt\u0026rsquo;s quite straightforward to do this in Terraform, so happy days!\nIf you\u0026rsquo;ve used Terraform to manage Auto Scaling Groups in AWS, you\u0026rsquo;ll know that this is relatively straightforward too. There are lots of examples and use cases out there to draw from.\nSo it should be super simple to marry those two concepts together into one simple, happy, solution you\u0026rsquo;d think.\nYeah right!\nIf you want to skip the dramatic retelling of my pain and just get the answer, skip to the solution\nWhat\u0026rsquo;s the problem? Go! The first two parts are 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.\nDuring the initial deploy attempt we encountered our first red flag. We had valid Terraform code, but it threw up this error:\nPlanning 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 \u0026#34;aws_ami\u0026#34; \u0026#34;latest_custom\u0026#34;: │ 486: data \u0026#34;aws_ami\u0026#34; \u0026#34;latest_custom\u0026#34; { │ I\u0026rsquo;d used a data source to capture the ID of the latest image that EC2 Image Builder would produce, but of course that hadn\u0026rsquo;t run yet.\nI thought I\u0026rsquo;d been smart, I had foreseen the bootstrapping issue, and thought I\u0026rsquo;d get around it by temporarily pointing the image_id at an AWS managed SSM parameter that stores the Amazon Linux 2023 AMI ID.\nresource \u0026#34;aws_launch_template\u0026#34; \u0026#34;my-cool-project-resource\u0026#34; { name_prefix = \u0026#34;my-cool-project-name-lt-\u0026#34; #image_id = data.aws_ami.latest_custom.id image_id = data.aws_ssm_parameter.al2023.id This didn\u0026rsquo;t prevent the error above. To my shame (not really, I\u0026rsquo;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. data.aws_ami.latest_custom was reinstated, and the Launch Template was reverted back to image_id = data.aws_ami.latest_custom.id and all worked well. Sorted.\n+5 influence points if you see where this is going It wasn\u0026rsquo;t sorted. Not completely.\nI could now successfully deploy the infrastructure reliably, I\u0026rsquo;d hacked my way past the bootstrapping issue, but comforted by my solemn vow to myself that I\u0026rsquo;d go back and fix that. Scout\u0026rsquo;s honour.\nThe 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\u0026rsquo;t using the most recent image. It was using the most recent -1. Consider this sequence of events:\nTerraform deploys IaC, creating ASG, Launch Template, and EC2 Image builder pipeline. EC2 Image Builder was triggered manually to confirm it was working. EC2 Image Builder produces a new version of the AMI. Launch Template is still running the TF deployed AMI version and doesn\u0026rsquo;t know about the one EC2 Image Builder produced. This surfaced what might have been obvious to you dear reader, that the Launch Template must be built before we\u0026rsquo;ve built any images.\nAlso 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\u0026rsquo;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.\nI was opposed to the idea of scheduling the Terraform code to run after the pipeline schedule. That wouldn\u0026rsquo;t be pragmatic, that would be messy.\nWhy don\u0026rsquo;t you just update the LT natively from EC2 Image Builder? Reasons!\nMostly that I didn\u0026rsquo;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.\nIn actual fact, the distribution settings within EC2 Image Builder allow for updating a Launch Template with the latest AMI\u0026hellip; directly. It\u0026rsquo;s explained nicely here.\nSo that resulted in me adding this launch_template_configuration block to my distribution config:\nresource \u0026#34;aws_imagebuilder_distribution_configuration\u0026#34; \u0026#34;dist\u0026#34; { name = \u0026#34;${var.project}-dist\u0026#34; distribution { region = var.region ami_distribution_configuration { name = \u0026#34;${var.project}-{{imagebuilder:buildDate}}\u0026#34; description = \u0026#34;A descriptive description describing things\u0026#34; ami_tags = { OS = \u0026#34;AmazonLinux2023\u0026#34; Hardened = \u0026#34;true\u0026#34; Name = \u0026#34;${var.project}-{{imagebuilder:buildDate}}\u0026#34; } } launch_template_configuration { launch_template_id = aws_launch_template.my-cool-project-resource.id account_id = local.account_id } } } My distribution would now update the Launch Templates with details of the new AMI ID.\nAnd it did.\nHalf Baked Images! This was a step forward, if you\u0026rsquo;d first pivoted 180 degrees.\nWhile it did solve the problem of the Launch Template not having the latest AMI - it absolutely did have the latest AMI now - it came at a price.\nSo, for some reason the documentation gods appear not to have fully explained (please tell me on Linkedin if I\u0026rsquo;m wrong) EC2 Image Builder Distribution Configuration can only configure some elements of the Launch Template, not all!\nAnd it doesn\u0026rsquo;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 new version with the details you\u0026rsquo;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.\nWhy is this bad? 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.\nI\u0026rsquo;ll save you the suspense - I checked with Amazon Q to point me to the explanation, and it had this to say:\n\u0026ldquo;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\u0026rsquo;t inherit or preserve other configuration settings like instance profiles, security groups, or other parameters from your existing Launch Template\u0026hellip;\n\u0026hellip;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\u0026rsquo;t clearly specify what happens to existing Launch Template settings when it creates a new version.\u0026rdquo;\nThe result of this was that my ASG was now spinning up instances with no instance profile, which wasn\u0026rsquo;t the only misconfiguration introduced, but this alone was a show stopper.\nSo what was the solution? The solution I settled on was a two-parter, but let\u0026rsquo;s start by recapping what we want to happen:\nUsing Terraform: deploy \u0026amp; manage infrastructure that will create an ASG, Launch Templates, and an EC2 Image Builder pipeline that will update images frequently. Shared custody of the latest AMI ID \u0026amp; Launch Template configuration between EC2 Image Builder and Terraform. By that I mean:\na. Terraform creates the infrastructure that holds the AMI details and doesn\u0026rsquo;t complain when\u0026hellip;\nb. The Launch Template is always referring to the latest AMI ID. The Launch Template is updated as soon as an updated image is produced, without waiting for Terraform. Terraform won\u0026rsquo;t freak out when it realises the Launch Template is using a newer AMI ID than it was expecting. So practically how do we do this?\nStep 1 - Get the IaC correct First of all, Terraform needs to create an SSM Parameter as shown in this example:\ndata \u0026#34;aws_ssm_parameter\u0026#34; \u0026#34;al2023\u0026#34; { name = \u0026#34;/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64\u0026#34; } resource \u0026#34;aws_ssm_parameter\u0026#34; \u0026#34;custom_built_image_id\u0026#34; { name = \u0026#34;/imagebuilder/${var.project}/custom_id\u0026#34; description = \u0026#34;SSM Parameter for storing the AMI ID of the image built from EC2 Image Builder\u0026#34; type = \u0026#34;String\u0026#34; data_type = \u0026#34;aws:ec2:image\u0026#34; value = data.aws_ssm_parameter.al2023.value lifecycle { ignore_changes = [ value ] } } So what did we accomplish with the SSM parameter definition in Terraform?\nThis is our source of truth for our AMI image.\nThe 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. 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. Important 1 - The managed service role for EC2 Image Builder only has permissions to publish to items in Parameter Store that begin with the prefix /imagebuilder/ - see AWS docs on that point. i.e. You must use a name similar to the one in the example above. Important 2 - data_type = \u0026quot;aws:ec2:image\u0026quot; must be set otherwise the Distribution Configuration in EC2 Image Builder will fail to update the parameter. (Ask me how I know this!) Important 3 - Don\u0026rsquo;t forget to ignore changes to the value as shown above! 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:\nresource \u0026#34;aws_launch_template\u0026#34; \u0026#34;my_lt\u0026#34; { name_prefix = \u0026#34;my-lt-\u0026#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 = \u0026#34;/dev/xvda\u0026#34; ebs { volume_size = 20 volume_type = \u0026#34;gp3\u0026#34; encrypted = true } } tag_specifications { resource_type = \u0026#34;instance\u0026#34; tags = { Name = \u0026#34;NamingIsHard\u0026#34; Project = var.project } } } resource \u0026#34;aws_imagebuilder_distribution_configuration\u0026#34; \u0026#34;dist\u0026#34; { name = \u0026#34;${var.project}-dist\u0026#34; distribution { region = var.region ami_distribution_configuration { name = \u0026#34;${var.project}-{{imagebuilder:buildDate}}\u0026#34; description = \u0026#34;Custom built AL2023 image\u0026#34; ami_tags = { OS = \u0026#34;AmazonLinux2023\u0026#34; Hardened = \u0026#34;true\u0026#34; Name = \u0026#34;${var.project}-{{imagebuilder:buildDate}}\u0026#34; } } ssm_parameter_configuration { parameter_name = aws_ssm_parameter.custom_built_image_id.name ami_account_id = local.account_id } } } So now the Launch Template is happy, because even at boot time we\u0026rsquo;ve given it a valid AMI ID.\nEC2 Image Builder is no longer sabotaging our Launch Template and is instead updating the SSM Parameter Store.\nStep 2 - Join the dots So, thus far we\u0026rsquo;ve completed our deployment, but we still need something to handle the Launch Template updates. Cue EventBridge \u0026amp; Lambda.\nWhereas 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\u0026rsquo;t allow us to specify the other important requirements like security group, profile etc.\nA custom Lambda function allows us to do exactly that.\ne.g. We can take this function, deploy it using Terraform\u0026hellip;\nimport boto3 import os ssm = boto3.client(\u0026#34;ssm\u0026#34;) ec2 = boto3.client(\u0026#34;ec2\u0026#34;) PARAM_NAME = os.environ[\u0026#34;SSM_PARAM_NAME\u0026#34;] LT_ID = os.environ[\u0026#34;LAUNCH_TEMPLATE_ID\u0026#34;] INSTANCE_TYPE = os.environ[\u0026#34;INSTANCE_TYPE\u0026#34;] SECURITY_GROUP_ID = os.environ[\u0026#34;SECURITY_GROUP_ID\u0026#34;] IAM_INSTANCE_PROFILE = os.environ[\u0026#34;IAM_INSTANCE_PROFILE\u0026#34;] def handler(event, context): # Get latest AMI from SSM response = ssm.get_parameter(Name=PARAM_NAME) ami_id = response[\u0026#34;Parameter\u0026#34;][\u0026#34;Value\u0026#34;] # Create new Launch Template version ec2.create_launch_template_version( LaunchTemplateId=LT_ID, SourceVersion=\u0026#34;$Default\u0026#34;, LaunchTemplateData={ \u0026#34;ImageId\u0026#34;: ami_id, \u0026#34;InstanceType\u0026#34;: INSTANCE_TYPE, \u0026#34;SecurityGroupIds\u0026#34;: [SECURITY_GROUP_ID], \u0026#34;IamInstanceProfile\u0026#34;: {\u0026#34;Name\u0026#34;: IAM_INSTANCE_PROFILE} } ) # Set the new version as default latest = ec2.describe_launch_template_versions( LaunchTemplateId=LT_ID, Versions=[\u0026#34;$Latest\u0026#34;] )[\u0026#34;LaunchTemplateVersions\u0026#34;][0][\u0026#34;VersionNumber\u0026#34;] ec2.modify_launch_template( LaunchTemplateId=LT_ID, DefaultVersion=str(latest) ) print(f\u0026#34;Updated LT {LT_ID} to use {ami_id}\u0026#34;) return {\u0026#34;statusCode\u0026#34;: 200, \u0026#34;ami_id\u0026#34;: ami_id} What will this function do? It takes five environment variables that are used to ensure all the required data for creating a useful new version of the Launch Template is included, but simply put it\u0026rsquo;s doing three things:\nIt obtains the latest AMI ID - that EC2 Image Builder\u0026rsquo;s Distribution config applied to the SSM Parameter. Create a new version of the Launch Template, passing in the required details, image ID, instance type, security group(s), \u0026amp; Instance profile for permissions. It updates the Launch Template to set the latest version it just created as the default version. How do we trigger that function? 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:\n# This is used to trigger Lambda to update the ASG Launch Template when there\u0026#39;s a new AMI available. resource \u0026#34;aws_cloudwatch_event_rule\u0026#34; \u0026#34;imagebuilder_completed\u0026#34; { name = \u0026#34;${var.project}-imagebuilder-completed\u0026#34; description = \u0026#34;Trigger on successful image builds\u0026#34; event_pattern = \u0026lt;\u0026lt;EOF { \u0026#34;source\u0026#34;: [\u0026#34;aws.imagebuilder\u0026#34;], \u0026#34;detail-type\u0026#34;: [\u0026#34;EC2 Image Builder Image State Change\u0026#34;], \u0026#34;detail\u0026#34;: { \u0026#34;state\u0026#34;: { \u0026#34;status\u0026#34;: [\u0026#34;AVAILABLE\u0026#34;] } } } EOF } resource \u0026#34;aws_cloudwatch_event_target\u0026#34; \u0026#34;trigger_lambda\u0026#34; { rule = aws_cloudwatch_event_rule.imagebuilder_completed.name target_id = \u0026#34;LaunchTemplateUpdater\u0026#34; arn = aws_lambda_function.update_launch_template.arn } This creates an EventBridge rule that is triggered every time your EC2 Image Builder pipeline successfully produces an AMI with a status of \u0026lsquo;AVAILABLE\u0026rsquo;.\nThat rule in turn triggers the Lambda function shown above. So now we\u0026rsquo;ll have this sequence:\nEC2 Image Builder runs and produces a new Image It also updates the SSM Parameter store with the latest AMI ID When the new AMI is updated to be \u0026lsquo;AVAILABLE\u0026rsquo;, our EventBridge Rule is triggered. The EventBridge rule in turn runs the Lambda function to retrieve this new ID and create a new Launch Template version. The result is that when the ASG next scales up, it\u0026rsquo;ll use the latest AMI ID.\nIt also means that on the next Terraform run it\u0026rsquo;ll pull the latest AMI ID from SSM and recognise that no change is required.\nConclusion 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.\nIn those more advanced scenarios a method as highlighted here will be effective in handling the synchronisation between Terraform\u0026rsquo;s view of the world and ensuring the Launch Template is always up to date (with a useful Instance Profile.)\nAdditional Resources Complete Auto Scaling Group with EC2 Image Builder Example EC2 Image Builder \u0026amp; SSM This post is part of my AWS technical blog series. Found this helpful? Connect with me on LinkedIn or check back here for more AWS content and discussions.\n","summary":"TL;DR 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\u0026rsquo;s native capabilities for managing Launch Templates.\nWhat were we asked? I\u0026rsquo;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.\nIt\u0026rsquo;s quite straightforward to do this in Terraform, so happy days!\n","date":"2025-10-02","categories":["AWS","EC2 Image Builder","Auto Scaling Group"],"tags":["AWS","EC2","Auto Scaling Group","EC2 Image Builder"],"aws_services":["EC2 Image Builder","Auto Scaling Group","EventBridge","Lambda"],"difficulty":"intermediate","estimated_time":null,"cost_estimate":"$5-15","featured_image":"/images/half-baked-title.png"},{"title":"What is Kiro and Spec Driven Development?","url":"https://aws.mcmillearn.net/posts/kiro-and-sdd/","content":"From inspiration to action. With AI. Inspired by the talented Chris Bingham, an AWS Ambassador here at Fujitsu, I recently set myself a new target: to apply for the Ambassador program myself. Chris spoke positively about the program, and it struck me as the kind of nudge I needed to step outside my comfort zone. It also felt like an opportunity to help bring positive attention to the good AWS work being done at Fujitsu.\nHowever, there’s one glaring weakness with any application I might submit: I\u0026rsquo;ve never really shared knowledge publicly. So, I decided to start there. Build a habit of sharing knowledge regularly and then decide later whether I want to keep it up long-term and actually apply.\nThe next question to answer was, how would I share articles and opinions?\nEnter Kiro.\nWhat is Kiro? Kiro is a new tool from AWS that promotes Spec Driven Development. It\u0026rsquo;s a forked version of Visual Studio Code, so it\u0026rsquo;s familiar and supports the same extensions.\nThe Spec driven Development approach that it encourages guides the user to plan out a project, quickly but thoroughly, before generating code. That’s a little different from some of the other AI-assisted coding tools out there that focus purely on code generation.\nI managed to get onto the recent preview of Kiro — and so I wanted to use it in a way that would let me experience it fully.\nI decided to use Kiro to plan, design, build, and deploy a blogging site to host my articles - this website. Well three out of four isn\u0026rsquo;t bad. \u0026lsquo;Deploy\u0026rsquo; was achieved outside of Kiro, but I\u0026rsquo;ll explain that later.\nWhat is Spec Driven development? Spec Driven development (SDD) is a methodology that encourages the developer to use a coding assistant in the \u0026lsquo;right\u0026rsquo; way. You start with a \u0026lsquo;specifcation\u0026rsquo;. Define what you want to achieve, inputs, outputs, etc.\nSDD doesn\u0026rsquo;t immediately launch into generating code. It uses your prompt to build the spec. There are three main phases to the process that we\u0026rsquo;ll look at:\nCreate requirements - generate a well written project plan Create the design - generate a design capable of meeting the plan Create the tasks - generate the tasks capable of delivering your project Start with a prompt: The entirety of my prompt to Kiro was:\nCreate the requirements As you can see, Kiro was happy to oblige and generated requirements.md, a list of high level requirements for my blog, written in the form of user stories with acceptance criteria.\nFor example:\n1 2 3 4 5 6 7 8 9 10 ### Requirement 1 **User Story:** As an aspiring AWS Ambassador, I want to host my own blogging platform on AWS, so that I can publish technical articles and demonstrate my AWS expertise. #### Acceptance Criteria 1. WHEN I access the blogging platform THEN the system SHALL be hosted entirely on AWS infrastructure 2. WHEN I create a blog post THEN the system SHALL support rich text editing with code syntax highlighting 3. WHEN I publish an article THEN the system SHALL make it publicly accessible with SEO optimization 4. WHEN visitors access my blog THEN the system SHALL provide fast loading times and high availability In total, 5 requirements were created from my prompt. I changed only one. A line in one of the acceptance criteria from \u0026ldquo;generate CloudFormation or Terraform IAC\u0026rdquo;, to \u0026ldquo;generate Terraform IAC\u0026rdquo;.\nNow, have a quick look at my original prompt again and consider this:\nI received five, well written, comprehensive requirements from that prompt. There’s real value in going from a rough idea to a clear, and easily edited, list of requirements – something that gives ‘shape’ to the idea. It really accelerates the process of going from idea -\u0026gt; to POC -\u0026gt; to market.\nCreate the design Kiro waited patiently for me to review the requirements and make any amendments I wanted. When I was happy with the requirements, Kiro then produced design.md. This is a proposed solution, including a summary of that solution, high level archtitecture, list of components, and other relevant details to my plan. Here\u0026rsquo;s a snippet of my design:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ## Overview The AWS Blogging Platform is designed as a cost-effective, Hugo-based static site solution that demonstrates AWS best practices while providing excellent performance and SEO capabilities. The platform leverages Hugo static site generation with AWS hosting services to create an extremely affordable, fast, and secure blogging solution suitable for technical content creation and AWS expertise demonstration for Ambassador applications. ## Architecture ### Primary Architecture: Hugo Static Site Generator **Selected Architecture**: Hugo-based static site with AWS hosting - **Frontend**: Hugo-generated static site on S3 + CloudFront - **Content**: Markdown files in Git repository - **CMS**: Git-based workflow with GitHub repository - **CI/CD**: GitHub Actions for automated builds and deployment - **Cost**: ~$1-5/month for moderate traffic - **Pros**: Extremely fast, cheapest option, Git-based content management, excellent SEO, perfect for technical blogging - **Cons**: No dynamic features, requires Git knowledge, limited admin interface In actual fact Kiro created three high level proposals:\nServers Static site using React/Node.js hosted on S3 + CloudFront, and using DynamoDB. Container based solution - React app on ECS Fargate + RDS PostgreSQL A Hybrid approach which didn\u0026rsquo;t fully make sense using API Gateway, Lambda, ECS, and RDS. After some cherry picking on my part, I chose a design close to option 1 but dropped React/Node.js and DyanmoDB, and instead used Hugo to convert markdown to static HTML hosted on S3 + CloudFront.\nDuring the review of design.md I spotted the only misunderstanding I encountered with Kiro. My prompt included: “I’d like to review architectural diagrams and assess their suitability” .\nKiro interpreted this as a request for my blog site to also have functionality and capability to receive architectural diagrams. I realised that when I spotted this in the original design.md:\n### Architecture Review System ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ Architecture │────│ Diagram │────│ Cost │ │ Generator │ │ Renderer │ │ Calculator │ └─────────────────┘ └──────────────────┘ └─────────────────┘ │ │ │ │ ┌──────────────────┐ ┌─────────────────┐ └─────────────│ Template │────│ Validation │ │ Library │ │ Engine │ └──────────────────┘ └─────────────────┘ That’s a lesson for me as a user of the tool about being clear and unambiguous. It’s also a reminder that you do actually need to review the requirements, designs, and tasks that it produces.\nPretty cool though! – I’ve added the idea to my backlog.\nCreate the tasks As before, Kiro will wait for you to review, amend, and finalise the design before proceeding to the next step. That could be by either manually updating design.md yourself, or interacting with Kiro to describe the changes you want, and Kiro will update the design.\nOnce you\u0026rsquo;ve settled on the requirements, then the design, Kiro will generate tasks.md. This is a list of tasks that are all directly a result of, and therefore, linked back to, the requirements, for example:\nThese tasks are \u0026lsquo;clickable\u0026rsquo;. Where you now see Task completed, it orginially read start task. Clicking that was all that was required to complete the task(s). I had the choice of clicking 2. create aws infrastructure with Terraform to complete all of the tasks in step 2, or I could execute each one individually and review as I went.\nI was moving quickly but also had short pauses to review what had been achieved in bite size chunks.\nIt felt fast and considered. What do I mean by that?\nHaving reviewed tasks.md and about to generate the code - I was less than 2 hours into the process. i.e. 2 hours prior I hadn’t submitted my request to Kiro.\nNow I was generating complex code that was tuned to specific requirements and validated tasks – that’s undeniably fast. However, in that time I’d still had an opportunity to review the requirements and the subsequent designs, and to make changes. Therefore, the code I was about to create had been carefully considered.\nDoes SDD differ from Vibe coding? Yes.\nVibe coding is fun. When done right, it’s a great way to experiment or knock out a quick fix. But it doesn’t ask you to stop and think about what your requirements actually are.\nIf you get distracted, you can easily lose your place. The “plan” (if you had one) lives only in your head, which makes it hard to share with others, human or AI. And if you don’t have a plan, how exactly do you measure progress, or build in testing?\nSpec Driven Development (SDD) flips that around.\nThe small upfront investment of writing down your requirements pays off quickly. You’re forced to clarify your goals and capture them in a way that’s easy to edit later. Kiro can then update the design and tasks automatically. Your spec becomes a single source of truth that’s easy to share with colleagues, clients, or AI helpers. Testing stops being an afterthought, and becomes super simple to embed into the process from the start because your objectives are clear.\nWhy does that matter? Bluntly - it offers significant time savings. Using Kiro and SDD I estimate that I saved at least 3 weeks on my project.\nConsider this scenario. An account manager receives an opportunity to create a small static marketing website for a client wishing to reach customers with updates, announcements, and new offerings. The subsequent timeline might look like this:\nThe account manager schedules a call with the project team (A project manager, solutions architect, and a couple of cloud engineers). Availability of the team means it\u0026rsquo;s two days before the call happens. (+2 days) After the call, the solution architect is tasked with documenting the requirements and circulating for review, correction, and agreement. (+1 day) The solution architect takes a day to generate a high level design (+1 day) Feedback from the team results in iteration over the next couple of days (+2 days) Once finalised, one of the cloud engineers works with the PM to create user stories in Jira. (+1 day) The cloud engineers begin the process of writing code in terrform to define the infrastructure hosting the environment. (+5 to 8 days) Deploying the infrastructure reveals some bugs and improvement opportunities (+2 days) The cloud engineers switch to generate the CICD pipelines in Github actions to test, build, and deploy the site content. (+5 days) Oops - that thing we forgot to account for (+ 1 day) Does that sound familiar?\nThats roughly 18-21 working days involving 4-5 people. That\u0026rsquo;s approximately four working weeks. And of course in the real world that team won\u0026rsquo;t get four weeks uninterrupted to focus on that project - holidays, sickness, other priorities jumping the queue, etc. There will be additional delays, and the longer something runs, the more it\u0026rsquo;s affected by these sorts of delays and interruptions.\nOutcomes and timeline Those steps I described above are equivalent to what I undertook to create this blogging site, but it took me just 4 days to get the site up and running.\nApproximately 3 days of that was spent fine tuning, tweaking, corrections, etc - but it took just 3 hours to achieve the following in Kiro:\nConfirm the project (my) requirements - requirements.md Plan the project, outline the steps involved Generate and propose four high level solutions with comparative pros and cons on cost and complexity Select one of the four options, iterate that design to get to a solution I was happy with Produce the high level design architecture for the chosen solution - design.md Define all of the steps needed to make the project a reality - tasks.md Generate the CICD pipeline workflows in GitHub Actions, to validate PR\u0026rsquo;s, perform build and integration tests, and deploy content to the solution. Generate the first draft of Terraform IAC including, S3, Cloudfront, OAC, SSL certs via ACM, IAM Roles + least privilege policies, CloudWatch, Budgets, Route 53, Notification integrations with Slack, etc. S3 bucket for content and state file backups. Create 2 repositories, one for the IAC, one for blogging content. Both with specific security scanning, pre commit hooks etc. 3 hours effort to do that? - It blew my mind.\nNow as I\u0026rsquo;ve already said, I wasn\u0026rsquo;t finished in 3 hours, it was more like 4 days.\nHaving got to the first draft of Terraform code, I wanted to make some changes, validate what had been produced, customise to suit my needs, etc. I also had some corrections and customisations to make on the GitHub actions workflows.\nThat process took me a further 3 working days (approx. 20hrs effort) to deploy, fix, tweak, and iterate to get the platform working as you see it now.\nSo less than 5 days, for a solo experienced cloud architect/engineer to complete something that was ready for deploying and acceptance testing.\nRealistically that traditionally might take a team 3-4 weeks.\nWhat did I learn from this? AI powered coding tools can accelerate individual tasks. e.g. writing scripts, or a Terraform module. These tasks could be chained to accelerate aspects of a project delivery, but that on it\u0026rsquo;s own lacks a cohesive approach in terms of project planning, and working to an agreed plan.\nSDD accelerates the whole project. It does this by shaping requirements and design first. So you have a clear plan that you can act on and your code is generated in line with that.\nI also learned to have a backup plan.\nHaving travelled at the speed of developer light for the first few hours I ran into this issue:\nThere was 10 days before I\u0026rsquo;d get new credits and having generated, but not yet deployed and tested, all of the code\u0026hellip; I couldn\u0026rsquo;t sit around and wait for that.\nMost of the heavy lifting was done though - I was at the stage of deploying, reviewing, correcting, until it deployed cleanly and my site looked sensible and stable. But at this juncture I switched to ChatGPT Codex, integrating that with my repos in GitHub to take me down the home straight.\nConclusion As is hopefully evident, I found Kiro to be incredibly powerful and easy to use. It promoted good practice and made planning quick and easy, but also to the appropriate level, neither under nor over cooked. Although it was rapid - it never felt like I was cutting corners. Quite the opposite in fact, Kiro steers you to think about what you’re doing and helps you to do it quickly.\nWorking with Kiro is closer to working with people than other code generating tools. I was able to hand off tasks (literally tasks.md) to Kiro while I joined calls or responded to other requests. In that regard, Kiro was like having a team to hand things off to so I could focus on something else, then check in later and course correct.\nKiro\u0026rsquo;s SDD approach promotes a single source of truth that works for an individual or for a project team. Integrating new project requests would be simplified too - just update the spec and the plan adapts then generates updates for the code automatically.\nTeams using Kiro\u0026rsquo;s SDD could be much more competitive in delivering solutions for clients, then can be faster and cheaper.\nBusiness can benefit too. How often is innovation a challenge for organisations because of a lack of available resources? Kiro could allow teams to deliver much more quickly, freeing up time to focus on innovation, or to take on additional projects.\nPricing will be interesting. At the time I write this, pricing has only been known for a few days and it\u0026rsquo;s higher than expected. Already there are op-ed articles highlighting how the costs could potentially rise quickly.\nI\u0026rsquo;m not massively put off by that yet - I feel like I accidentally stumbled across a viable solution in my own use case: i.e. Use Kiro to go as far as you can, at least completing the project plan, and getting to the generated code ready to test. Then if you\u0026rsquo;re out of credits, switch to another (cheaper) coding assistant that can focus just on generating good quality code.\nI would have loved to have completed my blog journey exclusively in Kiro - but I\u0026rsquo;d seen enough in my use case to recognise that there is genuine game changing potential in Kiro\u0026rsquo;s approach.\nRight now I\u0026rsquo;m in the early process of learning about two potential projects coming my way. Whether it\u0026rsquo;s one of those or something else, price permitting, I\u0026rsquo;d be very keen to use Kiro to move quickly and accurately.\nAdditional Resources Claude-4 from Anthropic Kiro Tiers and Pricing Getting started with Hugo This post is part of my AWS technical blog series. Found this helpful? Connect with me on LinkedIn or check back here for more AWS content and discussions.\n","summary":"From inspiration to action. With AI. Inspired by the talented Chris Bingham, an AWS Ambassador here at Fujitsu, I recently set myself a new target: to apply for the Ambassador program myself. Chris spoke positively about the program, and it struck me as the kind of nudge I needed to step outside my comfort zone. It also felt like an opportunity to help bring positive attention to the good AWS work being done at Fujitsu.\n","date":"2025-09-02","categories":["AWS","Kiro","AI"],"tags":["AWS","Kiro","gen-ai","Hugo"],"aws_services":["Kiro"],"difficulty":"intermediate","estimated_time":null,"cost_estimate":"$5-15","featured_image":"/images/Kiro-1.png"}]