<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lucian Patian</title>
    <description>The latest articles on DEV Community by Lucian Patian (@lp).</description>
    <link>https://dev.to/lp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1147404%2Fb6a3856b-5e90-46ad-8fe3-48e59d1e8206.jpeg</url>
      <title>DEV Community: Lucian Patian</title>
      <link>https://dev.to/lp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lp"/>
    <language>en</language>
    <item>
      <title>AWS SES emails not reaching your business inbox?</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Tue, 24 Mar 2026 14:19:20 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-ses-emails-not-reaching-your-business-inbox-3j15</link>
      <guid>https://dev.to/aws-builders/aws-ses-emails-not-reaching-your-business-inbox-3j15</guid>
      <description>&lt;p&gt;I invested some time in figuring out why my app running on AWS, using SES for sending emails was not working correctly. The strange behaviour was that for Gmail, it was working fine, for my work domain it was not delivered and I wasn't able to see any logs related to this on my corporate email server.&lt;/p&gt;

&lt;p&gt;So I started to check the standard stuff like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SES in prod mode?&lt;/li&gt;
&lt;li&gt;SES config, do I have a from address, am I using the correct region&lt;/li&gt;
&lt;li&gt;DKIM enabled?&lt;/li&gt;
&lt;li&gt;Custom "mail from" domain configured and valid&lt;/li&gt;
&lt;li&gt;No errors on the app side&lt;/li&gt;
&lt;li&gt;Checked the SES status for bounces and rejects&lt;/li&gt;
&lt;li&gt;Destination email address not being on the suppression list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything seemed fine but the test emails were not reaching my business email address. So if Gmail gets the test email, it must be "them", right? The next step was to look into the SPF records, which is a DNS record that tells receiving mail servers "these are the only servers allowed to send email on behalf of our domain." If a server isn't on the list, the email gets flagged or rejected.&lt;/p&gt;

&lt;p&gt;The parent domain had a strict policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v=spf1 mx ip4:x.x.x.x include:corporate-spf.example.com include:spf.protection.outlook.com -all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;-all&lt;/code&gt; at the end means "reject everything not on this list." And SES wasn't on the list, there was no &lt;code&gt;include:amazonses.com&lt;/code&gt; anywhere. The sending subdomain didn't have its own SPF record either, so it inherited the parent's strict rules.&lt;/p&gt;

&lt;p&gt;I added an SPF record for the subdomain to explicitly allow SES:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v=spf1 include:amazonses.com ~all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But emails still didn't arrive. SPF was part of the problem, but not the root cause.&lt;/p&gt;

&lt;p&gt;I needed more visibility to what was happening so I decided to create a SNS topic for SES send &amp;amp; delivery events.&lt;/p&gt;

&lt;p&gt;After the next test, the SNS notification came back with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notificationType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bounce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bounce"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bounceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Transient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bounceSubType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"General"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bouncedRecipients"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"emailAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[recipient]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"diagnosticCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"smtp; 553 #5.1.8 Domain of sender address &amp;lt;...@mail.yourdomain.com&amp;gt; does not exist"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It turns out that the corporate mail gateway is rejecting the email because the MAIL FROM domain "does not exist."&lt;/p&gt;

&lt;p&gt;So I went back to checking the &lt;strong&gt;it's always the DNS&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dig MX mail.mydomain.com @8.8.8.8 +short
10 feedback-smtp.eu-central-1.amazonses.com

&lt;span class="nv"&gt;$ &lt;/span&gt;dig TXT mail.mydomain.com @8.8.8.8 +short
&lt;span class="s2"&gt;"v=spf1 include:amazonses.com ~all"&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;dig A mail.mydomain.com @8.8.8.8 +short
&lt;span class="c"&gt;# (empty - no A record)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue was that I configured my SES to use MAIL FROM with mail.mydomain.com but that didn't exist, it had no A record. Apparently RFC 5321 says if there's no A record, the domain "doesn't exist" for my corporate mail gateway and the email gets rejected.&lt;/p&gt;

&lt;h4&gt;
  
  
  The easy fix
&lt;/h4&gt;

&lt;p&gt;I removed the MAIL FROM domain configured in SES so it defaulted to &lt;code&gt;amazonses.com&lt;/code&gt;, this way my corporate mail gateway was able to resolve it and emails started to get delivered as expected.&lt;/p&gt;

&lt;h4&gt;
  
  
  The strange part
&lt;/h4&gt;

&lt;p&gt;The strange part to investigate was the fact that SES was reporting successful deliveries for my test corporate emails.&lt;/p&gt;

&lt;p&gt;Here's what actually happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SES sends the email to the corporate mail gateway&lt;/li&gt;
&lt;li&gt;The gateway accepts the connection (SMTP &lt;code&gt;250 OK&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Then it runs additional checks: domain existence, SPF, DKIM&lt;/li&gt;
&lt;li&gt;It quietly rejects the email with a 553 error&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without delivery notifications, these bounces are invisible. SES counts that initial &lt;code&gt;250 OK&lt;/code&gt; as success. Only when you add SNS notifications does the real story surface.&lt;/p&gt;

&lt;h4&gt;
  
  
  Lessons learned the hard way
&lt;/h4&gt;

&lt;p&gt;SES stats can be misleading. A gateway can accept an email and then silently discard it. Always enable delivery notifications when debugging. Don't trust the dashboard alone.&lt;/p&gt;

&lt;p&gt;RFC 5321 says the MAIL FROM domain must be resolvable. Some strict gateways check for A/AAAA records, not just MX. If you're using a custom MAIL FROM domain with SES, make sure it has an A record. That's the one that I missed...&lt;/p&gt;

</description>
      <category>aws</category>
      <category>backend</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Import console created resources into CloudFormation</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Thu, 11 Dec 2025 21:20:28 +0000</pubDate>
      <link>https://dev.to/aws-builders/import-console-created-resources-into-cloudformation-plj</link>
      <guid>https://dev.to/aws-builders/import-console-created-resources-into-cloudformation-plj</guid>
      <description>&lt;p&gt;This one's for beginners.&lt;/p&gt;

&lt;p&gt;Think about each time you created and configured some new AWS services from the console, got really happy about it when you got it right but in the end thought it's going to be a mess to do it all over again.&lt;/p&gt;

&lt;p&gt;I'm here to tell you that Infrastructure as a Code will save your ass so don't fight it. It will hurt in the beginning as it's a tough learning curve but it pays off on any given Sunday.&lt;/p&gt;

&lt;p&gt;I decided to write this blogpost after migrating 80+ resources across 11 services into CloudFormation because I know it hurts to start something like this and I wanted to give you the push that you needed.&lt;/p&gt;

&lt;p&gt;I'm writing IaC on a regular basis in Terraform and CloudFormation, this time I used CloudFormation because I was curious how the import feature works, since I didn't use until now.&lt;/p&gt;

&lt;p&gt;Let's talk about the benefits of importing your existing resources into IaC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;version control&lt;/strong&gt;: track every infrastructure change in git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;disaster recovery&lt;/strong&gt;: recreate your entire infrastructure from code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;consistency&lt;/strong&gt;: deploy identical environments (dev/staging/prod)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;change preview&lt;/strong&gt;: see what will change before applying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;team collaboration&lt;/strong&gt;: code reviews for infrastructure changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dependency management&lt;/strong&gt;: CloudFormation prevents breaking changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a few cons on the other hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;console updates&lt;/strong&gt;: you can still manually modify resources in the console&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;drift detection&lt;/strong&gt;: you need to actively check for drift (manual changes done from the console, not via IaC)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The import process: Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;create a CloudFormation template describing your existing resources&lt;/li&gt;
&lt;li&gt;create an import changeset specifying which resources to import&lt;/li&gt;
&lt;li&gt;execute the changeset to bring resources under CloudFormation management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds simple but the devil is in the details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons learned
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Never use create-stack for existing resources
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws cloudformation create-stack \
  --stack-name network \
  --template-body file://network.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CloudFormation creates NEW resources instead of importing existing ones. You'll end up with duplicate VPCs, subnets and a mess to clean up.&lt;/p&gt;

&lt;p&gt;Always use import changesets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws cloudformation create-change-set \
  --stack-name network \
  --change-set-name import-network \
  --change-set-type IMPORT \
  --resources-to-import file://resources.json \
  --template-body file://network.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Not all resource types support import
&lt;/h4&gt;

&lt;p&gt;CloudFormation import has limitations. Some resource types simply don't support it.&lt;/p&gt;

&lt;p&gt;Resources that DON'T support import:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS::Route53::RecordSet - DNS records&lt;/li&gt;
&lt;li&gt;route table associations&lt;/li&gt;
&lt;li&gt;VPC gateway attachments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Workarounds (any will do):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manage these outside CloudFormation&lt;/li&gt;
&lt;li&gt;delete and recreate them (accept brief downtime)&lt;/li&gt;
&lt;li&gt;use Terraform instead (supports more imports)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: For Route53 records, I deleted existing records and let CloudFormation recreate them. The 5-10 second downtime was acceptable for full IaC coverage.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. DeletionPolicy is mandatory for imports
&lt;/h4&gt;

&lt;p&gt;Use the &lt;code&gt;DeletionPolicy: Retain&lt;/code&gt; to every resource definition or you'll get the &lt;code&gt;Resources must have DeletionPolicy attribute specified in the template&lt;/code&gt; error&lt;/p&gt;

&lt;p&gt;CloudFormation needs to know what to do if you delete the stack. Retain means "keep the resource even if the stack is deleted."&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Outputs can't be added during import
&lt;/h4&gt;

&lt;p&gt;Use a two-step process to avoid the &lt;code&gt;You cannot modify or add [Outputs] during import operations&lt;/code&gt; error:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Import resources without Outputs section&lt;/li&gt;
&lt;li&gt;Update stack to add Outputs after import completes&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. Resource names must match exactly
&lt;/h4&gt;

&lt;p&gt;Your template uses a friendly name but AWS has a different name. Use exact AWS names during import, rename later via stack update to aviod the &lt;code&gt;The Identifier [AlarmName] does not match the identifier value in the template&lt;/code&gt; error.&lt;/p&gt;

&lt;h4&gt;
  
  
  6. Beware of circular dependencies
&lt;/h4&gt;

&lt;p&gt;Security groups often reference each other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SG-A allows traffic from SG-B&lt;/li&gt;
&lt;li&gt;SG-B allows traffic from SG-A&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use cross-stack references, you create a deadlock:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stack A exports SG-A, imports SG-B&lt;/li&gt;
&lt;li&gt;Stack B exports SG-B, imports SG-A&lt;/li&gt;
&lt;li&gt;Neither can be created or updated!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep mutually referencing resources in the same stack or as a last resort, use hardcoded IDs for cross references.&lt;/p&gt;

&lt;h4&gt;
  
  
  7. Start with minimal outputs
&lt;/h4&gt;

&lt;p&gt;Export only what's actually used to aviod dependencies that block stack updates and deletions.&lt;/p&gt;

&lt;h4&gt;
  
  
  8. Modular stacks are better than monoliths
&lt;/h4&gt;

&lt;p&gt;The monolith approach can break the stack because one change requires updating entire stack, it's difficult to understand and maintain, you have long deployment times:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;infrastructure.yaml (5000 lines)
├── VPC
├── Subnets
├── EC2 Instances
├── Security Groups
├── CloudFront
├── Route53
├── S3
└── Everything else
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The modular approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;templates/
├── network.yaml (VPC, subnets)
├── compute.yaml (EC2, security groups)
├── cdn.yaml (CloudFront, WAF)
├── dns.yaml (Route53)
└── storage.yaml (S3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You update only what changed, have a smaller blast radius, it's easier to understand and you get faster deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Import strategy
&lt;/h3&gt;

&lt;p&gt;Inventory and planning&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;document existing resources&lt;/strong&gt;: list everything you need to import&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;check import support&lt;/strong&gt;: verify each resource type supports import&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;identify dependencies&lt;/strong&gt;: map relationships between resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;plan stack structure&lt;/strong&gt;: decide on modular vs monolithic approach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create import templates&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;start simple&lt;/strong&gt;: begin with standalone resources (S3, SNS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;add DeletionPolicy&lt;/strong&gt;: every resource needs DeletionPolicy: Retain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;use exact names&lt;/strong&gt;: match AWS resource names exactly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;skip Outputs&lt;/strong&gt;: don't add Outputs section yet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;test in dev&lt;/strong&gt;: always test import process in non-production first&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Execute imports&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;create import changeset&lt;/strong&gt;: use &lt;code&gt;--change-set-type IMPORT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;review changes&lt;/strong&gt;: check what will be imported&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;execute changeset&lt;/strong&gt;: bring resources under IaC management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;verify&lt;/strong&gt;: confirm resources are managed by CloudFormation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Improve templates&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;add Outputs&lt;/strong&gt;: update stacks to add cross-stack exports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;replace hardcoded values&lt;/strong&gt;: Use &lt;code&gt;!ImportValue&lt;/code&gt; for dynamic references&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;document&lt;/strong&gt;: add comments explaining configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;always use import changesets, never create-stack&lt;/li&gt;
&lt;li&gt;not all resources support import so plan accordingly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DeletionPolicy&lt;/code&gt; is mandatory for imports&lt;/li&gt;
&lt;li&gt;start modular, multiple focused stacks beat monoliths&lt;/li&gt;
&lt;li&gt;minimal exports, avoid dependency hell&lt;/li&gt;
&lt;li&gt;test in dev first: &lt;strong&gt;always&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;accept brief downtime for resources that don't support import&lt;/li&gt;
&lt;li&gt;document everything, your future self will thank you&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/import-resources.html" rel="noopener noreferrer"&gt;AWS CloudFormation import documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resource-import-supported-resources.html" rel="noopener noreferrer"&gt;Resource types that support import&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html" rel="noopener noreferrer"&gt;CloudFormation best practices&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>terraform</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>CloudFormation change set privilege escalation</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Fri, 07 Nov 2025 09:36:57 +0000</pubDate>
      <link>https://dev.to/aws-builders/cloudformation-change-set-privilege-escalation-18i6</link>
      <guid>https://dev.to/aws-builders/cloudformation-change-set-privilege-escalation-18i6</guid>
      <description>&lt;p&gt;About a year ago, during a review of our cloud infrastructure, my colleague Martin Birtel from our InfoSec department, discovered that a specific AWS managed policy can give hackers super powers.&lt;/p&gt;

&lt;p&gt;The finding was related to the role &lt;a href="https://docs.aws.amazon.com/aws-managed-policy/latest/reference/SecretsManagerReadWrite.html" rel="noopener noreferrer"&gt;SecretsManagerReadWrite&lt;/a&gt; which was attached to a Lambda responsible for credential management.&lt;/p&gt;

&lt;h3&gt;
  
  
  This is a great cocktail for an account takeover as you'll see below.
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/aws-managed-policy/latest/reference/SecretsManagerReadWrite.html" rel="noopener noreferrer"&gt;SecretsManagerReadWrite&lt;/a&gt; managed policy includes surprising CloudFormation permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version" : "2012-10-17",
  "Statement" : [
    {
      "Sid" : "BasePermissions",
      "Effect" : "Allow",
      "Action" : [
        "secretsmanager:*",
        "cloudformation:CreateChangeSet",
        "cloudformation:DescribeChangeSet",
        "cloudformation:DescribeStackResource",
        "cloudformation:DescribeStacks",
        "cloudformation:ExecuteChangeSet"

      ],
      "Resource" : "*"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It allows the principal (the Lambda execution role in our case) to create and execute CloudFormation change sets. This shouldn't be an issue as long as the Lambda role is not allowed to create all kinds of resources, right?&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudFormation stacks with attached IAM roles
&lt;/h3&gt;

&lt;p&gt;When you create a CloudFormation stack, you can attach an IAM service role that has permissions to create the defined resources. After stack creation, this service role remains attached and CloudFormation uses it for all future operations, including executing change sets.&lt;/p&gt;

&lt;p&gt;If you have permissions to update CloudFormation stacks, you're already in the hacking game. This means that you can use the permissions attached at the stack creation (&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-servicerole.html" rel="noopener noreferrer"&gt;CloudFormation service role&lt;/a&gt;) to make calls to resources in a stack on your behalf.&lt;/p&gt;

&lt;p&gt;AWS gives us a heads-up in the official documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you specify a service role, CloudFormation always uses that role for all operations that are performed on that stack. It is not possible to remove a service role attached to a stack after the stack is created. Other users that have permissions to perform operations on this stack are able to use this role, regardless of whether those users have the iam:PassRole permission or not. If the role includes permissions that the user shouldn't have, you can unintentionally escalate a user's permissions. Ensure that the role grants least privilege.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The escalation path
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You have a CloudFormation stack with a high privilege service role attached (let's assume Administrator).&lt;/li&gt;
&lt;li&gt;An attacker obtains credentials for a role with SecretsManagerReadWrite policy.&lt;/li&gt;
&lt;li&gt;The attacker discovers existing stack names (through enumeration or other means).&lt;/li&gt;
&lt;li&gt;The attacker creates a change set targeting the high-privilege stack.&lt;/li&gt;
&lt;li&gt;The attacker executes the change set using the stack's service role.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your CloudFormation stack just created new resources generated by the change set, while your hacker didn't have the permissions to create them. It only had the SecretsManagerReadWrite role which allowed the creation and execution of change sets. Since the CloudFormation service role had Administrator permissions, the change sets could essentially do anything in the account.&lt;/p&gt;

&lt;p&gt;Now let's get back to our cheerful Lambda: as it had this &lt;a href="https://docs.aws.amazon.com/aws-managed-policy/latest/reference/SecretsManagerReadWrite.html" rel="noopener noreferrer"&gt;SecretsManagerReadWrite&lt;/a&gt; attached, it could call the CloudFormation API and create/execute change sets. This means it could inherit the permissions of any CloudFormation stack's service role in the account.&lt;/p&gt;

&lt;h4&gt;
  
  
  Heads-up: just because a policy is AWS managed, doesn't mean it follows the principle of least privilege for your specific use case.
&lt;/h4&gt;

&lt;p&gt;PS. AWS Support confirmed this is by design.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>security</category>
      <category>role</category>
    </item>
    <item>
      <title>AWS GameDay: Security</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Wed, 08 Oct 2025 14:33:02 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-gameday-security-d01</link>
      <guid>https://dev.to/aws-builders/aws-gameday-security-d01</guid>
      <description>&lt;p&gt;Following-up on &lt;a href="https://dev.to/aws-builders/aws-gameday-frugality-fest-4889"&gt;last year's GameDay&lt;/a&gt; which was a real success, we decided to repeat the event, this time with a focus on security.&lt;/p&gt;

&lt;p&gt;The event gathered over 100 people in 21 teams from User Groups in Timisoara, Cluj-Napoca, Iasi, Bucharest and Chisinau.&lt;/p&gt;

&lt;p&gt;Based on feedback from our meetup participants, we chose security as our focus and we were right, the hype was real.&lt;/p&gt;

&lt;p&gt;The entire event was coordinated by our local AWS Solutions Architects, one of them being in each location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2ph23tyiciim38dn427.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2ph23tyiciim38dn427.jpg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We started with a 10 minute introduction and coordinated all UGs to start exactly at 18:30 so everyone has the same chances.&lt;/p&gt;

&lt;p&gt;The teams were of 4-6 people, with quite a lot of students joining, even if they were missing the AWS background. They loved to stay along the experienced participants and to learn by doing.&lt;/p&gt;

&lt;p&gt;The event wrapped up at 20:30, with some leading teams completing all tasks within just one hour.&lt;/p&gt;

&lt;p&gt;The winning team received VIP tickets for the &lt;a href="https://www.linkedin.com/company/106290128" rel="noopener noreferrer"&gt;AWS Community Day CEE&lt;/a&gt; happening in Budapest on the 16th of October 2025.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkm7exu45kvywocrv2cc7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkm7exu45kvywocrv2cc7.jpg" alt=" " width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the networking part, we stayed for pizza and beers while congratulating everyone for participating.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrrwzt3rsdbo6pd7vkqf.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrrwzt3rsdbo6pd7vkqf.jpeg" alt=" " width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpp9trhr3qv6nnfod9jb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpp9trhr3qv6nnfod9jb.jpeg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7i6an112b2a6qsy4d0zg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7i6an112b2a6qsy4d0zg.jpeg" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoz2falg4tuqs1y03k2u.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoz2falg4tuqs1y03k2u.jpeg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg84cwl5lgsgid8x4isot.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg84cwl5lgsgid8x4isot.jpeg" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvns9iyq0qv747inzotez.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvns9iyq0qv747inzotez.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez5q13xgc8osfhcrdge6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez5q13xgc8osfhcrdge6.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The teams optimized services like IAM Access Analyzer, RDS, Secrets Manager, EventBridge, Config, Lambda, EC2, AMIs. I'm not giving out any other details, as I don't want to spoil the fun.&lt;/p&gt;

&lt;p&gt;If you'd like more details about this event, feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/lucianpatian/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. If you’re interested in organizing a similar event, make sure to contact your local AWS Solutions Architect or Technical Account Manager.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>gameday</category>
      <category>security</category>
      <category>community</category>
    </item>
    <item>
      <title>Copilot vs. Amazon Q: the Claude Sonnet story</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Thu, 02 Oct 2025 13:32:40 +0000</pubDate>
      <link>https://dev.to/aws-builders/copilot-vs-amazon-q-the-claude-sonnet-story-2ppg</link>
      <guid>https://dev.to/aws-builders/copilot-vs-amazon-q-the-claude-sonnet-story-2ppg</guid>
      <description>&lt;p&gt;I've been using Copilot and Amazon Q daily for the past few months, both having the Claude Sonnet 4 model configured.&lt;/p&gt;

&lt;p&gt;I know there are many pros and cons against writing quality code with these tools, so I had to convince myself: should I take the blue pill or the red one.&lt;/p&gt;

&lt;p&gt;The projects I've been working on are using CloudFormation and Terraform for IaC and the deployment is done using GitLab pipelines.&lt;/p&gt;

&lt;p&gt;In Amazon Q, I've also configured a few MCP servers like awslabs.cfn-mcp-server, awslabs.aws-documentation-mcp-server and terraform. In Copilot I wasn't able to configure any, due to company restrictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Now the facts:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Creating new repositories from scratch
&lt;/h4&gt;

&lt;p&gt;I decided to use CloudFormation to create a new repo from scratch with Amazon Q and the generated templates needed tweaks in about 5-10% of the code. This was impressive.&lt;/p&gt;

&lt;p&gt;On the other hand, when it came to deploying the stacks using GitLab, Amazon Q failed to suggest good validation syntax for things like "pipeline inputs". Every time the build failed with issues that weren't easy to fix, I turned to Copilot to get quick and valid fixes, while Amazon Q was a bit behind.&lt;/p&gt;

&lt;p&gt;Each time I asked Copilot to scan the repo and provide improvement suggestions, it gave me a reply that I have a solid and mature solution, nothing major can be improved. On the same code, with the same request, Amazon Q delivered a solid number of suggestions which all made sense and in fact I adopted them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; I initially "trusted" Q to use the agent mode and took things too far by committing and pushing new code to the main branch, without asking me first, while working on a feature branch. So that was the moment I decided that I need to manually approve all file changes...&lt;/p&gt;

&lt;p&gt;In the end, I was satisfied with finishing the IaC way sooner than starting from scratch in a manual way.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Creating new repositories using as inspiration other working repositories
&lt;/h4&gt;

&lt;p&gt;Simply said, this was a bad decision.&lt;/p&gt;

&lt;p&gt;This time I used Copilot to start a new repo using Terraform, while instructing it to get inspiration from another Terraform repo which is deployed in production and has 90% the same resources. For the rest of the 10% resources, I instructed Copilot to check the repo I mentioned above, written in CloudFormation and write the equivalent Terraform code.&lt;/p&gt;

&lt;p&gt;The result was a total mess-up. The code I instructed to be used as inspiration from the production repo was ignored, Copilot decided to write from scratch new custom Terraform modules, when the production repo was using the official Terraform ones.&lt;/p&gt;

&lt;p&gt;I ended up cleaning and rewriting most of the custom modules, turning them into official ones, all with a lot of headaches and frustration.&lt;/p&gt;

&lt;p&gt;I tried using Q to fix the repos Copilot created but there was just too much mess and the suggestions were just adding complexity, when I just needed simplicity by using an existing working repo.&lt;/p&gt;

&lt;p&gt;Bottom line is that I found more helpful to start from scratch with a new repo, rather than trying to use existing ones and adapting them to my needs. This is exactly the opposite of what I was expecting so I'm curious how the newly Claude Sonnet 4.5 behaves, I'll test them both soon and update this blogpost with my findings.&lt;/p&gt;

&lt;p&gt;If you need to choose from the 2, I would go with Q for IaC templates and code reviews but keep Copilot for GitLab pipeline syntax if needed.&lt;/p&gt;

</description>
      <category>githubcopilot</category>
      <category>claude</category>
      <category>sonnet</category>
      <category>aws</category>
    </item>
    <item>
      <title>Azure AD says expired, AWS OpenSearch says working: the SAML certificate that refused to die</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Thu, 07 Aug 2025 12:14:21 +0000</pubDate>
      <link>https://dev.to/aws-builders/azure-ad-says-expired-aws-opensearch-says-working-the-saml-certificate-that-refused-to-die-5h90</link>
      <guid>https://dev.to/aws-builders/azure-ad-says-expired-aws-opensearch-says-working-the-saml-certificate-that-refused-to-die-5h90</guid>
      <description>&lt;p&gt;It starts innocently enough. You're auditing certificates in Azure AD when you notice the red flag: "EXPIRED" next to your SAML certificate. Has been for months, apparently.&lt;/p&gt;

&lt;p&gt;Your first thought: "oh no, authentication must be broken..."&lt;/p&gt;

&lt;p&gt;Your second thought: "wait... why is everyone still logging in?"&lt;/p&gt;

&lt;p&gt;Welcome to the SAML certificate twilight zone, where expired certificates live forever and security teams question reality.&lt;/p&gt;

&lt;h4&gt;
  
  
  The "this can't be right" moment
&lt;/h4&gt;

&lt;p&gt;You dig deeper into your monitoring (because you're responsible like that) and confirm the timeline: AWS OpenSearch SAML certificate expired back in August. It's now well into the future, and yet...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users are happily logging in. &lt;/li&gt;
&lt;li&gt;No error messages anywhere.&lt;/li&gt;
&lt;li&gt;No angry Teams/Slack messages from the team.&lt;/li&gt;
&lt;li&gt;Your monitoring dashboards show everything green.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So naturally, you do what any rational person would do: you panic and assume you're missing something obvious.&lt;/p&gt;

&lt;h4&gt;
  
  
  The "let me fix this" disaster
&lt;/h4&gt;

&lt;p&gt;Being the helpful engineer you are, you decide to "fix" the problem. You grab the tenant wide metadata (the one with shiny new certificates valid until 2030) and update your AWS OpenSearch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: instant authentication meltdown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/2.0 500
Error: Signature validation failed. SAML Response rejected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users can't log in. Colleagues start to ping you. You quickly revert to the expired certificate and... everything works again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plot twist&lt;/strong&gt;: The "broken" expired certificate was actually the correct one.&lt;/p&gt;

&lt;p&gt;This is where things get interesting. You start digging and discover that SAML certificates live in their own special universe where normal rules don't apply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSL Certificate expires: browser throws a tantrum, shows scary warnings, users panic.&lt;/li&gt;
&lt;li&gt;SAML Certificate expires: &lt;em&gt;crickets chirping&lt;/em&gt;, everything continues working like nothing happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The technical reality check
&lt;/h3&gt;

&lt;p&gt;Here's what's actually happening (and why it's both clever and concerning):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure/Entra AD: "This certificate is expired, but hey, it still signs things mathematically"&lt;/li&gt;
&lt;li&gt;AWS OpenSearch: "I can verify this signature cryptographically, expiration date is just a suggestion"&lt;/li&gt;
&lt;li&gt;Your security team: "Why is our compliance dashboard lighting up?"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The vendor validation tour
&lt;/h3&gt;

&lt;p&gt;Microsoft Support, with the enthusiasm of someone explaining why water is wet:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If your application doesn't have any validation for the certificate's expiration and the certificate matches in both Azure AD and your application, your application is still accessible despite having an expired certificate."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Translation: "Working as intended, deal with it."&lt;/p&gt;

&lt;p&gt;AWS Support, not to be outdone:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"AWS IAM does not invalidate SAML Responses that are signed using an expired X.509 certificate. The SAML specification itself does not strictly require expiration date validation."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Translation: "This is how SAML works everywhere, not just here."&lt;/p&gt;

&lt;p&gt;Then Microsoft drops this gem in their &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/tutorial-manage-certificates-for-federated-single-sign-on" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; (appearing not once, but &lt;strong&gt;twice&lt;/strong&gt; because they really want you to get it):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If your app lacks certificate expiration validation and the certificate matches both Microsoft Entra ID and your app, it remains accessible. This condition is true even if the certificate is expired."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So it turns out this isn't a bug or an oversight. It's a documented behavior that happens to contradict what most people expect.&lt;/p&gt;

&lt;h4&gt;
  
  
  The "why should I care?" reality check
&lt;/h4&gt;

&lt;p&gt;You're living in a compliance gray area where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical reality: everything works perfectly.&lt;/li&gt;
&lt;li&gt;Audit reality: you're using expired certificates.&lt;/li&gt;
&lt;li&gt;Security reality: it's not great, but it's not catastrophic either.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The monitoring mirage
&lt;/h4&gt;

&lt;p&gt;Want to monitor your SAML certificate expiration in AWS? Here's what you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch metrics for SAML certificates: none&lt;/li&gt;
&lt;li&gt;OpenSearch configuration for strict validation: none&lt;/li&gt;
&lt;li&gt;Built-in AWS monitoring: none&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS basically says: "Certificate monitoring is Azure's job, we just validate signatures."&lt;/p&gt;

&lt;p&gt;Meanwhile, Microsoft offers email notifications at 60, 30 and 7 days before expiration. Which raises the obvious question: why bother setting up alerts for something that keeps working anyway?&lt;/p&gt;

&lt;p&gt;The answer: because your compliance auditor doesn't care about SAML protocol nuances.&lt;/p&gt;

&lt;h4&gt;
  
  
  The infrastructure reality
&lt;/h4&gt;

&lt;p&gt;If you're managing this with CloudFormation, you're in for extra fun since &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-opensearchservice-domain-idp.html" rel="noopener noreferrer"&gt;it only supports hardcoded XML metadata content&lt;/a&gt;, no URL references like Terraform has for years.&lt;/p&gt;

&lt;p&gt;So while your expired certificate keeps working, you still need to manually update CloudFormation templates every time you renew certificates. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;I submitted a &lt;a href="https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/1575" rel="noopener noreferrer"&gt;feature request&lt;/a&gt; in September 2023 for URL support. The GitHub roadmap now shows it as "Shipped" but the AWS documentation hasn't been updated to reflect this.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the thing: your expired SAML certificate working is like finding out your car runs fine without oil changes: technically possible, not recommended and definitely not something you want to explain to your manager.&lt;/p&gt;

&lt;p&gt;Follow &lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/tutorial-manage-certificates-for-federated-single-sign-on#create-a-new-certificate" rel="noopener noreferrer"&gt;Microsoft's renewal process&lt;/a&gt; - it takes a few minutes and prevents uncomfortable audit conversations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nov 2026 update:
&lt;/h4&gt;

&lt;p&gt;I've tried to update an existing Cognito CloudFormation stack using an expired certificate XML and it seems that Cognito checks for the certificate validity - it prompted me with this error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Resource handler returned message: "Signing certificates are expired (Service: CognitoIdentityProvider, Status Code: 400, Request ID: ) (SDK Attempt Count: 1)" (RequestToken: , HandlerErrorCode: InvalidRequest)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Built entirely with Amazon Q CLI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>aws</category>
      <category>opensearch</category>
      <category>saml</category>
    </item>
    <item>
      <title>AWS Client VPN setup was driving me crazy. So I built the easy button</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Thu, 24 Jul 2025 13:38:30 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-client-vpn-setup-was-driving-me-crazy-so-i-built-the-easy-button-4kko</link>
      <guid>https://dev.to/aws-builders/aws-client-vpn-setup-was-driving-me-crazy-so-i-built-the-easy-button-4kko</guid>
      <description>&lt;p&gt;You know that feeling when you decide to try something new? Ever thought about setting up an AWS Client VPN? &lt;/p&gt;

&lt;p&gt;First, you're optimistic. You search for blog posts, read the AWS documentation and dive in. Then reality hits.&lt;/p&gt;

&lt;p&gt;You need to understand certificate generation. Manual steps pile up. You realize this will be a nightmare to reproduce. You try CloudFormation but get cryptic errors like "route already exists" and you have no idea why.&lt;/p&gt;

&lt;p&gt;Eventually, you create all the resources following the documentation step by step. But guess what? The connection still doesn't work.&lt;/p&gt;

&lt;p&gt;Plot twist: It doesn't have to suck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter: the "just work already" solution
&lt;/h3&gt;

&lt;p&gt;What if VPN setup was more like ordering pizza and less like performing surgery? One command, actual results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./scripts/deploy-client-vpn.sh \
    --stack-name "please-just-work" \
    --region "us-east-1" \
    --profile "my-sanity" \
    --vpc-id "vpc-12345678" \
    --subnet-id "subnet-87654321" \
    --vpc-cidr "172.31.0.0/16"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten minutes later: You have a working VPN. With certificates. And documentation that actually explains why SSH might not work (spoiler: it's the NAT thing and here's how to fix it).&lt;/p&gt;

&lt;h3&gt;
  
  
  The plot twist nobody tells you
&lt;/h3&gt;

&lt;p&gt;Here's the kicker that breaks everyone: AWS Client VPN uses NAT routing. Your traffic doesn't come from the VPN client IP range - it comes from your subnet range. So when you configure security groups for "VPN clients", you're configuring them wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real fix&lt;/strong&gt;: Your private resources need to allow traffic from your &lt;strong&gt;private subnet CIDR&lt;/strong&gt; used in the Client VPN configuration, not from the VPN client CIDR range.&lt;/p&gt;

&lt;p&gt;Most tutorials skip this. Most solutions leave you to figure it out. This one puts it right in the troubleshooting guide with the exact commands to fix it. Because 2 AM debugging sessions are nobody's friend.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you actually get
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The reality check: no more certificate generation mysteries or "route already exists" CloudFormation failures.&lt;/li&gt;
&lt;li&gt;The time saver: 4 hours of frustration → 10 minutes of deployment.&lt;/li&gt;
&lt;li&gt;The bonus: complete cleanup command (because nobody remembers how to tear things down properly).&lt;/li&gt;
&lt;li&gt;The relief: works in any AWS account - personal, corporate or that weird sandbox environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfect for when you need secure access yesterday, not next week after you've become a networking wizard.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Easy Button
&lt;/h3&gt;

&lt;p&gt;Grab it here: &lt;a href="https://github.com/lucianpatian/aws_clientvpn" rel="noopener noreferrer"&gt;github.com/lucianpatian/aws_clientvpn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because sometimes you just want the VPN to work so you can get back to the actual work. Revolutionary concept, I know.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built entirely with Amazon Q CLI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>community</category>
      <category>clientvpn</category>
      <category>configure</category>
    </item>
    <item>
      <title>How to share SSM Parameters across AWS accounts easily</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Thu, 17 Apr 2025 13:24:11 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-share-ssm-parameters-across-aws-accounts-easily-3m68</link>
      <guid>https://dev.to/aws-builders/how-to-share-ssm-parameters-across-aws-accounts-easily-3m68</guid>
      <description>&lt;p&gt;Managing configuration settings for multiple AWS accounts might seem overwhelming but it’s actually easy using AWS Systems Manager (SSM) Parameter Store. You can keep all your configuration data in one place and share it safely with other accounts using AWS Resource Access Manager (RAM).&lt;/p&gt;

&lt;h4&gt;
  
  
  What are Shared Parameters?
&lt;/h4&gt;

&lt;p&gt;Think of shared parameters as a way to avoid the hassle of duplicating configuration data across accounts. Instead of managing the same values separately, you store them centrally in AWS Parameter Store and share them with other accounts that need access. It’s efficient, secure and saves you time.&lt;/p&gt;

&lt;p&gt;AWS RAM makes it even easier by letting you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick what to share (like an SSM parameter).&lt;/li&gt;
&lt;li&gt;Decide who gets access (specific accounts, groups or entire AWS Organizations).&lt;/li&gt;
&lt;li&gt;Set permissions (read-only access).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I began using SSM Parameters to solve a challenge with Route 53 NS entries for DNS delegation between AWS accounts. One account kept recreating its Route 53 zone files via IaC, causing the NS values to change frequently. To automate fetching the updated values and keep DNS delegation intact, I turned to shared parameters in SSM Parameter Store and it was the perfect solution.&lt;/p&gt;

&lt;h4&gt;
  
  
  Before you start - what you need
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Advanced Tier parameters only: You can only share Advanced Tier parameters, not Standard ones.&lt;/li&gt;
&lt;li&gt;Encryption for SecureString: If the parameter is encrypted, it must use a customer-managed KMS key. AWS-managed keys won’t work, so make sure to share the KMS key separately.&lt;/li&gt;
&lt;li&gt;AWS Organizations Enabled: If you’re sharing within an AWS Organization, ensure sharing is enabled in AWS RAM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step-by-step example: sharing and accessing a Parameter
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Step 1: Create the Parameter in the Source Account
&lt;/h5&gt;

&lt;p&gt;In the source account, create an Advanced Tier parameter in AWS Systems Manager Parameter Store. You can do this via the console, CLI or SDKs.&lt;br&gt;
Important: Standard parameters cannot be shared between accounts, so make sure the parameter is Advanced Tier&lt;/p&gt;
&lt;h5&gt;
  
  
  Step 2: Share the Parameter with AWS RAM
&lt;/h5&gt;

&lt;p&gt;Run the following command in the source account to share the parameter with the target account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ram create-resource-share \
    --name "MyParameter" \
    --resource-arns "arn:aws:ssm:REGION:SOURCE_ACCOUNT_ID:parameter/NAME_OF_PARAMETER" \
    --principals "TARGET_ACCOUNT_ID"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the placeholders as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;REGION&lt;/em&gt;: The AWS region where the parameter exists.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;SOURCE_ACCOUNT_ID&lt;/em&gt;: The ID of the AWS account where the parameter exists.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;NAME_OF_PARAMETER&lt;/em&gt;: The name of the parameter you want to share.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;TARGET_ACCOUNT_ID&lt;/em&gt;: The ID of the AWS account you’re sharing the parameter with.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Step 3: Accept the Share in the Target Account
&lt;/h5&gt;

&lt;p&gt;Now, log in to the target account (the account you shared the parameter with). Here’s what you need to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the AWS RAM Console.&lt;/li&gt;
&lt;li&gt;Go to the Pending Invites section.&lt;/li&gt;
&lt;li&gt;Find the invite for the resource share and click Accept.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: This step is essential. The target account won’t be able to access the parameter until the invitation is accepted.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step 4: Find the ARN of the Shared Parameter
&lt;/h5&gt;

&lt;p&gt;In the target account, use the following command to get a list of shared resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ram list-resources --resource-owner OTHER-ACCOUNTS

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will return a list of shared resources, including the ARN of the shared parameter. Look for the ARN of the parameter. It will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:ssm:REGION:SOURCE_ACCOUNT_ID:parameter/NAME_OF_PARAMETER

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Important&lt;/em&gt;&lt;/strong&gt;: The &lt;code&gt;aws ssm describe-parameters --shared&lt;/code&gt; command will not work for shared parameters, so always use &lt;code&gt;aws ram list-resources&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step 5: Retrieve the Parameter Value
&lt;/h5&gt;

&lt;p&gt;Now that you have the ARN of the shared parameter, you can use it to retrieve the parameter value. Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ssm get-parameter --name ARN_OF_SSM_PARAMETER

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the parameter is encrypted as a SecureString, you’ll need to include the &lt;code&gt;--with-decryption&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ssm get-parameter \
    --name ARN_OF_SSM_PARAMETER \
    --with-decryption
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace ARN_OF_SSM_PARAMETER with the ARN you identified in Step 4.&lt;/p&gt;

&lt;h4&gt;
  
  
  Points to keep in mind
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Sharing SecureString Parameters:
&lt;/h5&gt;

&lt;p&gt;If the parameter is encrypted with a SecureString, make sure the KMS key used to encrypt the parameter is also shared with the target account. You can refer to &lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying-external-accounts.html" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to learn how to share a KMS key with another account.&lt;/p&gt;

&lt;h5&gt;
  
  
  Using the right CLI commands:
&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;aws ram list-resources&lt;/code&gt; command is the best way to find the ARN of a shared parameter. Other commands like &lt;code&gt;aws ssm describe-parameters --shared&lt;/code&gt; may not work as expected.&lt;/p&gt;

&lt;h5&gt;
  
  
  Who can use Shared Parameters?
&lt;/h5&gt;

&lt;p&gt;When you share a parameter, the target account gets &lt;strong&gt;&lt;em&gt;read-only access&lt;/em&gt;&lt;/strong&gt;. This means they can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View the current value of the parameter.&lt;/li&gt;
&lt;li&gt;Access historical values (if allowed by permissions).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They cannot update, delete or re-share the parameter.&lt;/p&gt;

&lt;h5&gt;
  
  
  Works great with:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-ssm-parameter-types" rel="noopener noreferrer"&gt;CloudFormation templates&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html" rel="noopener noreferrer"&gt;AWS Lambda extensions for Parameters and Secrets&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/using-systems-manager-parameters.html" rel="noopener noreferrer"&gt;EC2 launch templates and AMI configurations&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://repost.aws/knowledge-center/systems-manager-parameter-store" rel="noopener noreferrer"&gt;Systems Manager Automation runbooks&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Doesn’t work with:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-param-runcommand.html" rel="noopener noreferrer"&gt;Run Command in Systems Manager&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html" rel="noopener noreferrer"&gt;CloudFormation dynamic references&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Environment variables in &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec.env.parameter-store" rel="noopener noreferrer"&gt;CodeBuild&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/apprunner/latest/dg/env-variable.html" rel="noopener noreferrer"&gt;App Runner&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/userguide/secrets-envvar-ssm-paramstore.html" rel="noopener noreferrer"&gt;Secrets in ECS&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  How Much Does It Cost?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Source Account (Owner): pays for parameter storage (e.g., $0.05/month per Advanced Tier parameter).&lt;/li&gt;
&lt;li&gt;Target Account (Consumer): pays for API calls to access the shared parameter (e.g., $0.05 for 10,000 API requests).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both accounts have independent usage limits, so one account’s usage won’t impact the other.&lt;/p&gt;

&lt;p&gt;Sharing parameters in AWS Parameter Store is an easy way to manage configurations securely and save time. Learn more from the &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-shared-parameters.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloud</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>Centralized Root Access in AWS: A Game-Changer for LandingZone security</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Fri, 20 Dec 2024 16:20:16 +0000</pubDate>
      <link>https://dev.to/aws-builders/centralized-root-access-in-aws-a-game-changer-for-landingzone-security-281m</link>
      <guid>https://dev.to/aws-builders/centralized-root-access-in-aws-a-game-changer-for-landingzone-security-281m</guid>
      <description>&lt;p&gt;One of the standout features announced by AWS in 2024 is the ability to control root access keys for all accounts within the Landing Zone using the IAM service. This centralization offers significant benefits for managing security at scale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tg8bue6h8jwcc3i6sux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tg8bue6h8jwcc3i6sux.png" alt=" " width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Centralized Root Access
&lt;/h2&gt;

&lt;p&gt;By centralizing root access, you can effectively remove and prevent root user credential recovery and access across all accounts enrolled in the Organizations service. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deleting Root User Credentials: you can remove root user passwords, access keys, signing certificates, and deactivate and delete multi-factor authentication (MFA). New accounts created in Organizations will have no root user credentials by default, enhancing security.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performing Privileged Tasks: some tasks require root user credentials, but these can now be managed by the management account or a delegated IAM administrator. This allows for secure, controlled access to perform necessary actions without compromising security.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxsxqcy5f1jtscohbeyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxsxqcy5f1jtscohbeyy.png" alt=" " width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling Account Recovery
&lt;/h2&gt;

&lt;p&gt;If root user credential recovery is needed, the Organizations management account or a delegated IAM administrator can enable the Allow password recovery privileged task. This allows the person with access to the root user email inbox to reset the password and recover credentials. It's recommended to delete root user credentials once the required task is completed to maintain security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short-Term Privileged Sessions
&lt;/h2&gt;

&lt;p&gt;AWS now allows for short-term privileged sessions, giving temporary credentials to perform specific root user tasks on member accounts. These sessions enable actions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deleting misconfigured Amazon S3 bucket policies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deleting misconfigured Amazon SQS queue policies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managing root user credentials for member accounts&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;However, one feature I would love to see is the ability to update unmanageable KMS policies directly. Currently, administrators must log in as the root user, create a support ticket, and have AWS Support grant temporary permissions. For instance, if a key policy grants access to only one user and that user is deleted, the key becomes unmanageable, requiring AWS Support intervention.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since I was excited to give it a try and remove all the console credentials and the MFA devices registered, I looked into how to create a bash script using AWS CLI to go over all accounts in my organization, check if there's a Console Password set and remove it. The same goes for the MFA Devices for each root user.&lt;/p&gt;

&lt;p&gt;The bash script can be found &lt;a href="https://github.com/lucianpatian/aws_landingzone/blob/main/delete_ou_root_profile.sh" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It iterates over all accounts in the organization, assumes the &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/security-iam-awsmanpol.html#security-iam-awsmanpol-IAMDeleteRootUserCredentials" rel="noopener noreferrer"&gt;IAMDeleteRootUserCredentials&lt;/a&gt;&lt;br&gt;
role in each child account, checks if there's any login profile and deletes it for the root user using the &lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/delete-login-profile.html" rel="noopener noreferrer"&gt;delete-login-profile&lt;/a&gt; command. Next, it lists the MFA registered devices for the root user using the&lt;br&gt;
&lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/list-mfa-devices.html" rel="noopener noreferrer"&gt;list-mfa-devices&lt;/a&gt; command and deactivates them using the&lt;br&gt;
&lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/deactivate-mfa-device.html" rel="noopener noreferrer"&gt;deactivate-mfa-device&lt;/a&gt; command. Interestingly, you don't need to delete the existing MFA devices; you need to deactivate them. This isn't specified in the official documentation, or I missed it.&lt;/p&gt;

&lt;p&gt;The script uses the credentials from the organization account as initial credentials to further assume the &lt;strong&gt;IAMDeleteRootUserCredentials&lt;/strong&gt; role in each child account. If you manage the root credentials for your company, you probably know what I'm talking about. &lt;/p&gt;

&lt;p&gt;To make granular changes, I've used the OU ID parameter (PARENT_ID) so you can start with your sandbox and non-prod accounts. This is the only thing you need to change in the script before running it.&lt;/p&gt;

&lt;p&gt;Another thing worth mentioning is that the &lt;code&gt;aws sts assume-role&lt;/code&gt; command needs the region specified, as STS uses regional endpoints. Also, the &lt;code&gt;--task-policy-arn&lt;/code&gt; option has a strange syntax: "arn=arn:aws:iam::aws:policy/root-task/IAMDeleteRootUserCredentials". Notice it starts with &lt;strong&gt;arn=arn&lt;/strong&gt;, which is a terrible way of doing it. I honestly have no idea why the service team implemented it this way.&lt;/p&gt;

&lt;p&gt;If your teams are using GuardDuty for monitoring suspicious activity in their accounts, you might want to give them a heads-up as the deletion of the root credentials will trigger an alert of &lt;code&gt;Policy:IAMUser/RootCredentialUsage&lt;/code&gt; type which looks like this: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The API GetAccountSummary was invoked using root credentials from IP address&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This feature greatly enhances the ability to manage and secure AWS environments effectively, ensuring that root access is controlled and monitored at all times.&lt;/p&gt;

&lt;p&gt;Have you tried this new feature? What do you think about it?&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>root</category>
      <category>landingzone</category>
    </item>
    <item>
      <title>How to Create a New Entra ID Enterprise Application and Configure Custom Attributes for SAML Login for AWS Cognito</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Tue, 15 Oct 2024 09:51:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-create-a-new-entra-id-enterprise-application-and-configure-custom-attributes-for-saml-login-for-aws-cognito-2fk4</link>
      <guid>https://dev.to/aws-builders/how-to-create-a-new-entra-id-enterprise-application-and-configure-custom-attributes-for-saml-login-for-aws-cognito-2fk4</guid>
      <description>&lt;p&gt;When integrating Entra ID (formerly Azure AD) with AWS Cognito for SAML login, it's important to use a unique attribute to identify users. In this guide, we'll walk you through the steps to create a new Enterprise Application in Entra ID and configure a custom attribute named &lt;code&gt;user.objectid&lt;/code&gt;. This attribute ensures that user identities remain consistent even if other attributes, such as last names, change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use &lt;code&gt;user.objectid&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;user.objectid&lt;/code&gt; attribute is unique to each user in Entra ID and does not change, even if other user attributes are updated. This is particularly important for scenarios where an employee changes their last name, as other attributes will be updated with the new value. Using &lt;code&gt;user.objectid&lt;/code&gt; prevents the creation of a new local user in your application and ensures that existing user data is preserved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps to Create a New Enterprise Application in Entra ID
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a New Application
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Navigate to the Azure Portal&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Open the Azure Portal and go to the Entra ID service.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a New Application&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Enterprise applications&lt;/strong&gt; and select &lt;strong&gt;New application&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Create your own application&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the option to &lt;strong&gt;Integrate any other application you don’t find in the gallery (Non-gallery)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Type the name of your new application and create it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnntkki97y06uf4k6fcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnntkki97y06uf4k6fcq.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Single Sign-On (SSO) login
&lt;/h3&gt;

&lt;p&gt;Create the connection between Entra ID and your application by setting the login URL and the identity of your application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Select Single Sign-On Method&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In your Enterprise application, select the &lt;strong&gt;Single Sign-On&lt;/strong&gt; option from the left menu.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;SAML&lt;/strong&gt; as the single sign-on method.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit Basic SAML Configuration&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Edit the &lt;strong&gt;Basic SAML Configuration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add the &lt;strong&gt;Identifier (Entity ID)&lt;/strong&gt; and &lt;strong&gt;Reply URL (Assertion Consumer Service URL)&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Identifier (Entity ID)&lt;/strong&gt; should follow the format: urn:amazon:cognito:sp:&lt;code&gt;cognito_userpool_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Reply URL (Assertion Consumer Service URL)&lt;/strong&gt; should follow the format: https://&lt;code&gt;cognito_domain_url&lt;/code&gt;/saml2/idpresponse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save the changes&lt;/strong&gt; to the Basic SAML Configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fka09a1ui62gu6p3ejp6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fka09a1ui62gu6p3ejp6f.png" alt=" " width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the User Access for SSO login
&lt;/h3&gt;

&lt;p&gt;Assign the users and groups that should have permissions to log in to your application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Assign Users and Groups&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;Users and groups&lt;/strong&gt; option from the Enterprise application options.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Add user/group&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the users and/or groups who should have access to your application.&lt;/li&gt;
&lt;li&gt;Confirm your selections and save.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figwwxiz169sl04j13q4v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figwwxiz169sl04j13q4v.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure User Attributes &amp;amp; Claims for SSO Login
&lt;/h3&gt;

&lt;p&gt;Configure which Entra ID attributes should be used to log in to your application.&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;
&lt;strong&gt;Edit User Attributes &amp;amp; Claims&lt;/strong&gt;
        &lt;ul&gt;
            &lt;li&gt;From the &lt;strong&gt;Single Sign-On&lt;/strong&gt; option for your Enterprise application, edit the &lt;strong&gt;User Attributes &amp;amp; Claims&lt;/strong&gt;.&lt;/li&gt;
        &lt;/ul&gt;
        &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyvwwd902k78ra609ga9.png" rel="noopener noreferrer"&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyvwwd902k78ra609ga9.png" alt="Edit User Attributes &amp;amp; Claims" width="800" height="340"&gt;
        &lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
&lt;strong&gt;Set Unique User Identifier&lt;/strong&gt;
        &lt;ul&gt;
            &lt;li&gt;Select the &lt;strong&gt;Unique User Identifier (Name ID)&lt;/strong&gt; claim to edit it.&lt;/li&gt;
            &lt;li&gt;In the &lt;strong&gt;Source attribute&lt;/strong&gt;, set the value to &lt;code&gt;user.objectid&lt;/code&gt;.&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Save the changes.&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dam9dhid4jy6qd2pd3iz.png" rel="noopener noreferrer"&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdam9dhid4jy6qd2pd3iz.png" alt="Set Unique User Identifier" width="725" height="470"&gt;
        &lt;/a&gt;
    &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Update the AWS Cognito userpool
&lt;/h2&gt;

&lt;p&gt;Once you have defined all the claim mappings on the Entra ID side, it is time to connect the dots on AWS's side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieve SAML Federation Metadata
&lt;/h3&gt;

&lt;p&gt;This is the intermediate step between configuring Entra ID and Cognito.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the Federation Metadata URL

&lt;ul&gt;
&lt;li&gt;In your Entra ID Enterprise application, navigate to the Single Sign-On section.&lt;/li&gt;
&lt;li&gt;Locate the App Federation Metadata Url.&lt;/li&gt;
&lt;li&gt;Copy this URL, as it will be needed in AWS Cognito.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanynr1rwctdgg7d58idk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanynr1rwctdgg7d58idk.png" alt=" " width="729" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable the IdP
&lt;/h3&gt;

&lt;p&gt;After all configurations are done on Entra ID side, you need to update the configuration in Cognito.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable the IdP&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In AWS Cognito, select the &lt;strong&gt;User Pool&lt;/strong&gt; and go to the &lt;strong&gt;Sign-in experience&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Under the &lt;strong&gt;Federated identity provider sign-in&lt;/strong&gt; section, click on &lt;strong&gt;Add identity provider&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;SAML&lt;/strong&gt; type.&lt;/li&gt;
&lt;li&gt;Enter a name under &lt;strong&gt;Provider name&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Identifiers&lt;/strong&gt;, add the &lt;strong&gt;IdpIdentifier&lt;/strong&gt; value.&lt;/li&gt;
&lt;li&gt;Use the recommended setting &lt;strong&gt;Require SP-initiated SAML assertions&lt;/strong&gt; for the &lt;strong&gt;IdP-initiated SAML sign-in&lt;/strong&gt; setting.&lt;/li&gt;
&lt;li&gt;Enter the metadata document endpoint URL saved previously.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuy01by23das2nicqjj7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuy01by23das2nicqjj7j.png" alt=" " width="800" height="984"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a better solution than uploading the XML file because Cognito refreshes the metadata every 6 hours or before the metadata expires. This way, you don’t have to manually refresh the metadata XML every time the Entra ID SSL certificates expire or any other change occurs on the Entra ID side that would impact the federation authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Attribute Mapping
&lt;/h3&gt;

&lt;p&gt;Configure the attributes that are stored in Entra ID and are mapped via the SAML schema in AWS Cognito. Here is a copy-and-paste friendly table for easier usage:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SAML Attribute&lt;/th&gt;
&lt;th&gt;User Pool Attribute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" rel="noopener noreferrer"&gt;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://schemas.xmlsoap.org/claims/CommonName" rel="noopener noreferrer"&gt;http://schemas.xmlsoap.org/claims/CommonName&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Preferred User Name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" rel="noopener noreferrer"&gt;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Given Name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" rel="noopener noreferrer"&gt;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Family Name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" rel="noopener noreferrer"&gt;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ielwm70us0478kiq92j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ielwm70us0478kiq92j.png" alt=" " width="755" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable the External IdP for App Clients
&lt;/h3&gt;

&lt;p&gt;Now that you have an IdP using the Entra ID configuration, you need to assign it to your application created in the Cognito userpool.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable the IdP for App Clients&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In AWS Cognito, navigate to the &lt;strong&gt;App integration&lt;/strong&gt; tab, &lt;strong&gt;App client list&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;Select the App client you want to configure and edit the &lt;strong&gt;Hosted UI&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;From the &lt;strong&gt;Identity providers&lt;/strong&gt; dropdown, select your newly created IdP (e.g., EntraID) and save the changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test the Configuration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Click on the &lt;strong&gt;View Hosted UI&lt;/strong&gt; button to quickly test your changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final Steps
&lt;/h3&gt;

&lt;p&gt;After a user has successfully authenticated via the external IdP, it will automatically be created in your Cognito userpool with the "Enabled" status. The confirmation status will be set to EXTERNAL_PROVIDER.&lt;/p&gt;

&lt;p&gt;The user attribute &lt;strong&gt;identities&lt;/strong&gt; will store the metadata relating to the external IdP that “owns” this identity. This includes the user’s ID in the external IdP’s attribute, in our case, the "Identifier (Entity ID)".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F418ucwrna2hr2wckxzx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F418ucwrna2hr2wckxzx9.png" alt=" " width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These fields will be updated on each successful authentication, so you can rely on the fact that the fields you receive via JWT attributes will be up-to-date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;By following these steps, you will have a fully functioning solution offering federated authentication with an external Entra ID. This setup ensures that user identities remain consistent and up-to-date, even when user attributes change.&lt;/p&gt;

</description>
      <category>entraid</category>
      <category>cognito</category>
      <category>aws</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>AWS GameDay: Frugality Fest</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Fri, 14 Jun 2024 14:32:42 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-gameday-frugality-fest-4889</link>
      <guid>https://dev.to/aws-builders/aws-gameday-frugality-fest-4889</guid>
      <description>&lt;p&gt;AWS GameDay is an engaging, hands-on learning event where participants tackle real-world technical challenges using AWS solutions in a team-based environment. Unlike traditional workshops, GameDays are open-ended and encourage creative problem-solving.&lt;/p&gt;

&lt;p&gt;To foster collaboration among AWS User Groups in Romania, we organized a GameDay event focused on building cost-effective applications and mastering cost-efficiency strategies. We chose the "Frugality Fest" theme, a trending topic for 2024, because it aligns well with our evening meetup schedule, typically starting at 18:30. This ensured the event wouldn't stretch late into the night, making it convenient for participants who join after their workday.&lt;/p&gt;

&lt;p&gt;Each AWS Usergroup from Romania (&lt;a href="https://www.meetup.com/aws-timisoara/" rel="noopener noreferrer"&gt;Timisoara&lt;/a&gt;, &lt;a href="https://www.meetup.com/transylvaniacloud/" rel="noopener noreferrer"&gt;Cluj-Napoca&lt;/a&gt;, &lt;a href="https://www.meetup.com/bucharest-aws-user-group/" rel="noopener noreferrer"&gt;Bucuresti&lt;/a&gt;, &lt;a href="https://www.meetup.com/aws-ro/" rel="noopener noreferrer"&gt;Iasi&lt;/a&gt;) and &lt;a href="https://www.meetup.com/amazon-web-services-user-group-md/" rel="noopener noreferrer"&gt;Moldova&lt;/a&gt; hosted on-site events coordinated remotely by AWS Romania. Each location had a dedicated AWS Romania representative to help with organization and coordination.&lt;/p&gt;

&lt;p&gt;Teams of four participated, with some pre-formed and others assembled ad-hoc. The event kicked off with a 30 minute introduction covering the event's objectives, the scenario and the rules.&lt;/p&gt;

&lt;p&gt;At 18:30, the game began and all 17 teams accessed their resources. Each venue featured a live scoreboard displayed prominently, updating in real-time.&lt;/p&gt;

&lt;p&gt;The game ran for 2.5 hours, filled with intense communication, laughter, frustration and calls for help. After the clock stopped, we captured a screenshot of the final scoreboard. The top three teams were the most experienced but everyone enjoyed the event and eagerly asked about the next GameDay.&lt;/p&gt;

&lt;p&gt;While I won't spoil the specific tasks, they involved AWS services like EC2, RDS, S3, VPC and CloudWatch. A tip from the winners: prioritize database optimization!&lt;/p&gt;

&lt;p&gt;If you'd like more details about this event, feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/lucianpatian/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. If you’re interested in organizing a similar event, make sure to contact your local AWS Solutions Architect or Technical Account Manager.&lt;/p&gt;

&lt;p&gt;Event photos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1u6qgxhic25nj76k1l3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1u6qgxhic25nj76k1l3.jpg" alt=" " width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqxrfxajej2r9x9ss83d.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqxrfxajej2r9x9ss83d.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxvjnd6e5e2v0o7v2c7t1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxvjnd6e5e2v0o7v2c7t1.jpeg" alt=" " width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65dreu5yofje7lh7il4v.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65dreu5yofje7lh7il4v.jpeg" alt=" " width="800" height="984"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcehrkocpsj23qq0tuhpo.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcehrkocpsj23qq0tuhpo.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqyvbm0f3mftoj6l617ee.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqyvbm0f3mftoj6l617ee.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ou964kubjel1ujqsxvc.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ou964kubjel1ujqsxvc.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zibzy0ox0evudoekz6n.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zibzy0ox0evudoekz6n.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xpfm8tj022stm73dhfa.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xpfm8tj022stm73dhfa.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qbzei2ooogbbtxrgh0e.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qbzei2ooogbbtxrgh0e.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3kjmlow9jutr8fh5xyq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3kjmlow9jutr8fh5xyq.jpeg" alt=" " width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gameday</category>
      <category>awsusergroup</category>
      <category>awscommunity</category>
      <category>communitybuilder</category>
    </item>
    <item>
      <title>Use AWS StepFunctions for SSM Patching Alerts</title>
      <dc:creator>Lucian Patian</dc:creator>
      <pubDate>Thu, 11 Apr 2024 07:42:16 +0000</pubDate>
      <link>https://dev.to/aws-builders/use-aws-stepfunctions-for-ssm-patching-alerts-3pj8</link>
      <guid>https://dev.to/aws-builders/use-aws-stepfunctions-for-ssm-patching-alerts-3pj8</guid>
      <description>&lt;p&gt;In this blog post we'll explore how to use AWS Step Functions and SSM Patch Manager to monitor the patch compliance status of EC2 instances and send alerts, reducing manual tracking and enhancing the security of our cloud environment.&lt;/p&gt;

&lt;p&gt;AWS Step Functions is a service that doesn't require a server to run. It allows us to connect with Lambda functions and other services to construct important business applications.&lt;/p&gt;

&lt;p&gt;The service is built around the principle of linked tasks put together in a workflow called "state machine". A task is able to invoke other AWS services or more recently &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/connect-third-party-apis.html" rel="noopener noreferrer"&gt;third-party APIs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AWS Systems Manager (SSM), which includes the Patch Manager feature, provides a unified interface for managing your AWS resources, including the ability to automate patching for EC2 instances which can make things easier for us.&lt;/p&gt;

&lt;p&gt;However, there are instances that need a reboot so the EC2 patch is completely done. We don't want to restart them automatically, especially when they're running critical services like databases. In this case, we prefer to choose when to restart. &lt;/p&gt;

&lt;p&gt;To keep track of the EC2 instances that aren't fully compliant in the SSM patch report, we need alerts. &lt;/p&gt;

&lt;p&gt;My goal was to send alerts to a Microsoft Teams channel, listing the EC2s that aren't compliant and need additional actions like rebooting. Initially, I used a Lambda function to do this but I didn't want to manage its dependencies over time so I switched to using Step Functions, taking advantage of its new feature that supports &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/11/aws-step-functions-https-endpoints-teststate-api/" rel="noopener noreferrer"&gt;HTTPS endpoints&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview of the State Machine
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pwzm77sd3sm29imwwog.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pwzm77sd3sm29imwwog.gif" alt=" " width="1026" height="1646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The entire process is initiated by an EventBridge rule, which acts as a trigger for the Step Function state machine.&lt;/p&gt;

&lt;p&gt;The state machine begins by identifying all currently active EC2 instances in the AWS account. It then retrieves all the instance IDs and filters them based on parameters such as ComplianceType=Patch and Status=NON_COMPLIANT.&lt;/p&gt;

&lt;p&gt;Next it determines if there are any instances that need review. If not, the state machine will skip to the end and stop. To do this, we use a task that counts the number of instances in the list from the previous step. If the count is more than zero, indicating that there are instances requiring attention, the state machine continues to filter these instances by their tags. This information is then used to format a message sent to a Microsoft Teams channel, which includes the names and IDs of the EC2 instances that need our attention.&lt;/p&gt;

&lt;p&gt;In the end we call the 3rd party API to send the formatted message to the Microsoft Teams channel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full steps description&lt;/strong&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DescribeInstances&lt;/strong&gt;: starts the process by identifying all currently active EC2 instances within the AWS account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ExtractInstanceIDs&lt;/strong&gt;: retrieves all the instance IDs from the previously fetched list of EC2 instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FetchInstanceComplianceData&lt;/strong&gt;: filters the instances based on the ComplianceType=Patch and Status=NON_COMPLIANT parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CalculateArrayLength&lt;/strong&gt;: calculates the size of the list of non-compliant instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CheckIfInstancesFound&lt;/strong&gt;: checks if the size of the list is greater than zero (indicating that there are non-compliant instances) or not. If no non-compliant instances are found during this step, the state machine skips to the end state and stops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DescribeTagsForFilteredInstances&lt;/strong&gt;: if there are non-compliant instances, this step fetches the tags for these instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PrepareNonCompliantInstanceList&lt;/strong&gt;: prepares a list of non-compliant instances along with their names and IDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CallThirdPartyAPI&lt;/strong&gt;: formats the message with the non-compliant instances' information and sends it to a Microsoft Teams channel.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Additional Configuration Details
&lt;/h3&gt;

&lt;p&gt;This state machine can be used to send the message to any communications tool like Slack, MS Teams or to a ticketing system.&lt;/p&gt;

&lt;p&gt;For MS Teams, the endpoint URL needs to be encoded so the "@" needs to be replaced with "%40" or you can use a URL shortener service.&lt;/p&gt;

&lt;p&gt;An HTTP Task requires an EventBridge connection, which securely manages the authentication credentials of an API provider. A connection specifies the authorization type and credentials to use for authorizing a third-party API.&lt;/p&gt;

&lt;p&gt;In our case we are just sending a message/payload to an external URL without the need of authentication but in order to use the StepFunction HTTP Task, we need to create this connection. When creating the connection, as a requirement, you also create an AWS Secret used for authentication. Again, since there's no need to authenticate to the MS Teams channel, the Secret values contain the keyname of the API and the secret is the ARN of the EventBrdige API connection:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8ksbof2qtbzqhsjxp4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8ksbof2qtbzqhsjxp4e.png" alt=" " width="800" height="763"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fab6md5n9pji7i6faggkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fab6md5n9pji7i6faggkt.png" alt=" " width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vb5v4j4i30yfza1r25c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vb5v4j4i30yfza1r25c.png" alt=" " width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure as Code
&lt;/h3&gt;

&lt;p&gt;The entire PoC was done using the AWS Console but since we are living in the age of automation, I wanted to have an easy and repeatable way of deploying the solution. &lt;/p&gt;

&lt;p&gt;In the past weeks, the Cloudformation service team &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/generate-IaC.html" rel="noopener noreferrer"&gt;announced the new IaC generator (infrastructure as code generator)&lt;/a&gt; which must be one of the most desired features for years now so I definitely wanted to give it a try.&lt;/p&gt;

&lt;p&gt;It turns out that I was able to get the Cloudformation template for all the needed resources pretty easy. The hardest thing was to select from a huge dropdown list all the resources involved in my scenario and to make sure I don't leave out any. After the template was generated, inside the StepFunction JSON definition, it was a bit difficult to replace the hardcoded values with parameters. Now it seems like a piece of cake.&lt;/p&gt;

&lt;p&gt;If you want to use this solution, checkout the &lt;a href="https://github.com/lucianpatian/aws_ssmpatch_status_stepfunctions" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt; which includes the entire Cloudformation stack needed for deployment.&lt;/p&gt;

&lt;p&gt;During the initial stages of this PoC, I encountered difficulties with the CallThirdPartyAPI task so I asked around for guidance other AWS CommunityBuilders in the dedicated Slack space and got almost instant help from &lt;a href="https://www.linkedin.com/in/bboure/" rel="noopener noreferrer"&gt;Benoît Bouré&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/dahlqvistjimmy/" rel="noopener noreferrer"&gt;Jimmy Dahlqvist&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/andmoredev/" rel="noopener noreferrer"&gt;Andres Moreno&lt;/a&gt;. Chapeau bas!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>stepfunctions</category>
      <category>ssm</category>
      <category>cloudformation</category>
    </item>
  </channel>
</rss>
