<?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>Forem: Anne</title>
    <description>The latest articles on Forem by Anne (@annie_rocks).</description>
    <link>https://forem.com/annie_rocks</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%2F1589347%2F64fff1eb-7eb1-4a5d-86eb-3fee2f692edf.jpg</url>
      <title>Forem: Anne</title>
      <link>https://forem.com/annie_rocks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/annie_rocks"/>
    <language>en</language>
    <item>
      <title>AWS Amplify + Amazon Cognito + AWS CDK: A Complete Setup Guide</title>
      <dc:creator>Anne</dc:creator>
      <pubDate>Wed, 25 Feb 2026 21:22:12 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-amplify-amazon-cognito-aws-cdk-a-complete-setup-guide-26f2</link>
      <guid>https://forem.com/aws-builders/aws-amplify-amazon-cognito-aws-cdk-a-complete-setup-guide-26f2</guid>
      <description>&lt;p&gt;Setting up AWS Amplify with Amazon Cognito using CDK is mostly straightforward. The tricky parts?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub integration (PAT → GitHub App migration)&lt;/li&gt;
&lt;li&gt;Cognito email configuration with SES&lt;/li&gt;
&lt;li&gt;Automating login page branding (which isn’t supported in CDK yet)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article walks you through the full setup with the important details without unnecessary boilerplate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify App with CDK
&lt;/h2&gt;

&lt;p&gt;You can create your Amplify app in CDK using Level 1 Cloudformation constructs. That part is pretty simple if you know CDK. However, the GitHub integration still requires a Personal Access Token (PAT) for the initial setup in CDK. If you go to the Amplify console after your deployment, it will immediately suggest you to migrate to GitHub App authentication. This can be done easily following the steps in the console. From now on you can revoke the PAT and replace it with a random string in your CDK code (at least 1 char length).&lt;/p&gt;

&lt;h2&gt;
  
  
  Cognito User Pool (Email as Login)
&lt;/h2&gt;

&lt;p&gt;Here’s a clean CDK example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UserPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyUserPool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;signInAliases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;autoVerify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;standardAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mutable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;givenName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mutable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;familyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mutable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;userInvitation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;emailSubject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access to My App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;emailBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello,&amp;lt;br&amp;gt;Your username: {username}&amp;lt;br&amp;gt;Password: {####}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;userVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;emailSubject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verification Code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;emailBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your verification code: {####}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;accountRecovery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AccountRecovery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EMAIL_ONLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few notes to the user pool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;signInAliases set to &lt;code&gt;email&lt;/code&gt; will allow your users to login via email instead of using a dedicated username. Also in the email templates the username will be rendered as the user's email in this case&lt;/li&gt;
&lt;li&gt;The email templates are for inviting new users as well as password reset. Especially if your users don't speak English it can be very useful to replace the standard template. The templates also support basic HTML (e.g., for line breaks).&lt;/li&gt;
&lt;li&gt;Custom attributes are also possible to define, however, keep in mind that they are not searchable in Cognito.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Custom Email Sender with SES
&lt;/h2&gt;

&lt;p&gt;To send invitation and password recovery emails from your own domain, you need to configure Amazon SES:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EmailIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Identity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my@email.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please make sure, that SES in your account is out of sandbox mode, your identity is successfully verified and don't forget to grant permissions to send emails to the user pool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;emailIdentity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArnPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myUserPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userPoolArn&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ses:SendEmail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  User Pool Client for Nuxt
&lt;/h2&gt;

&lt;p&gt;Your frontend needs a client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UserPoolClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;userPool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generateSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;preventUserExistenceErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;supportedIdentityProviders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UserPoolClientIdentityProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COGNITO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;oAuth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;callbackUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://myapp.com/auth/callback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;logoutUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://myapp.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OAuthScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OAuthScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROFILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;flows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;authorizationCodeGrant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;implicitCodeGrant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generateSecret: true (needed for SSR (e.g., Nuxt))&lt;/li&gt;
&lt;li&gt;authorizationCodeGrant (recommended secure flow)&lt;/li&gt;
&lt;li&gt;preventUserExistenceErrors (avoids user enumeration leaks)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Nuxt OAuth Configuration
&lt;/h2&gt;

&lt;p&gt;In your &lt;code&gt;nuxt.config.ts&lt;/code&gt; you have to add the oauth configuration for using Cognito:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;oauth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;COGNITO_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;COGNITO_CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;COGNITO_REGION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userPoolId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;COGNITO_USER_POOL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;redirectURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;COGNITO_REDIRECT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;authorizationParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In here you can define a language parameter so that your users will see the login page in their native language. Currently supported language can be found in the &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-managed-login.html" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Domain
&lt;/h2&gt;

&lt;p&gt;In order to use your own domain instead of a default AWS cognito domain you can add one to your user pool like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;myUserPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Domain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;customDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.myapp.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;managedLoginVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ManagedLoginVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEWER_MANAGED_LOGIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The certificate in this case was created in another stack using the AWS certificate manager. Also, I am using the new AWS Managed Login (not the Hosted UI).&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating the Styling of the Cognito New Managed Login
&lt;/h2&gt;

&lt;p&gt;AWS provides a nice UI in the console to style the new managed login (logo, favicon, colors in dark and bright mode etc.), however, this can't be managed through CDK (yet?). But here's a workaround:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Style your managed login in the AWS console&lt;/li&gt;
&lt;li&gt;Export the configuration with the AWS CLI and save the output into a JSON file.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp describe-managed-login-branding &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; YOUR_USER_POOL_ID &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--managed-login-branding-id&lt;/span&gt; YOUR_BRANDING_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Apply the styling e.g. in your CI/CD pipeline after the deployment like this
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp update-managed-login-branding &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="nv"&gt;$USER_POOL_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--managed-login-branding-id&lt;/span&gt; &lt;span class="nv"&gt;$MANAGED_LOGIN_BRANDING_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--settings&lt;/span&gt; file://login-settings.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Setting up Cognito with CDK can require a bit of fumbling around with the configurations. However, once set up Cognito is a strong reliable way to authenticate your users for your Amplify hosted app. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudnative</category>
      <category>oauth</category>
      <category>nuxt</category>
    </item>
    <item>
      <title>From Seconds to Milliseconds: Fixing Python Cold Starts with SnapStart</title>
      <dc:creator>Anne</dc:creator>
      <pubDate>Mon, 27 Oct 2025 21:49:21 +0000</pubDate>
      <link>https://forem.com/aws-builders/from-seconds-to-milliseconds-fixing-python-cold-starts-with-snapstart-59mn</link>
      <guid>https://forem.com/aws-builders/from-seconds-to-milliseconds-fixing-python-cold-starts-with-snapstart-59mn</guid>
      <description>&lt;p&gt;If you’ve built a Python Lambda that uses Pydantic heavily (or other heavy model initialization, schema loading, or framework bootstrapping), you’ve probably hit this painful boundary: cold starts are dominated by your app’s initialization time, not AWS’s runtime overhead. Even if AWS could spin up pods in less than 100 ms, your own code might spend 800 ms or more importing modules, constructing many Pydantic models, loading schemas, doing dependency injection, etc. That kind of latency becomes a dealbreaker for synchronous, latency-sensitive APIs.&lt;/p&gt;

&lt;p&gt;With the availability of Lambda SnapStart for Python (runtime 3.12+), that barrier is now much lower: you can snapshot the fully-initialized environment and restore from it. In other words: the heavy startup cost of initializing your Pydantic models happens once during deployment, rather than every time the function runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Lambda SnapStart (for Python) and how it changes cold starts
&lt;/h2&gt;

&lt;p&gt;Before SnapStart, Lambda cold starts involved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Downloading your code package&lt;/li&gt;
&lt;li&gt;Starting the language runtime (Python interpreter)&lt;/li&gt;
&lt;li&gt;Running all initialization code (imports, global-level constructs, class definitions, module-level state, constructor logic, etc)&lt;/li&gt;
&lt;li&gt;Finally handling the incoming event&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your startup logic is heavy (for example, constructing &amp;gt;100 Pydantic model classes, loading JSON or YAML schemas, instantiating global validators or caches, etc.), that initialization phase can be your bottleneck.&lt;/p&gt;

&lt;p&gt;SnapStart changes that dramatically (for supported runtimes). The flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You enable SnapStart on a published version of your Lambda (or via alias to a version).&lt;/li&gt;
&lt;li&gt;When that version is published, Lambda “warms up” the function once, runs all initialization code, sets up your global state exactly as your code would do for a first invocation.&lt;/li&gt;
&lt;li&gt;Lambda then takes a memory + disk snapshot (a Firecracker micro-VM snapshot) of that fully initialized state, encrypts and persists it in a cache.&lt;/li&gt;
&lt;li&gt;Later, when a new environment is needed, (a cold) Lambda restores from that snapshot, essentially skipping the heavy init and resuming from the “post-init” state.&lt;/li&gt;
&lt;li&gt;Your handler logic runs as-is.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In effect, a lot of the “cold start” cost is eliminated. AWS claims that you can get “sub-second” startup times even for heavy apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snapshot safety, uniqueness, and runtime hooks
&lt;/h2&gt;

&lt;p&gt;Because SnapStart literally reuses memory state, if your initialization code produces unique artifacts (for example, a new random UUID on startup, or a unique timestamp, or establishes network sockets), those may incorrectly get reused across invocations if not handled carefully.&lt;/p&gt;

&lt;p&gt;To mitigate this, AWS provides runtime hooks for Python:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@register_before_snapshot&lt;/code&gt;: functions that run right before the snapshot is taken. Use this hook to “undo” or clean up any ephemeral or unique initialization state (e.g. close DB connections, clear caches, reset random seeds). &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@register_after_restore&lt;/code&gt;: functions that run immediately upon restore from snapshot, before handling the first event. Use this to reinstantiate anything you can’t reuse (e.g. re-open connections, reinitialize entropy, refresh tokens). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These hooks help ensure your function remains correct even when operating from a snapshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using SnapStart in CDK for Python Lambdas
&lt;/h2&gt;

&lt;p&gt;Let’s get into how to configure SnapStart when deploying via AWS CDK (in Python). The high-level steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a Lambda construct (e.g. aws_lambda.Function or the PythonFunction from the aws_lambda_python_alpha module).&lt;/li&gt;
&lt;li&gt;Ensure your runtime is at least Python 3.12 (SnapStart is supported on Python 3.12+).&lt;/li&gt;
&lt;li&gt;Enable the SnapStart property.&lt;/li&gt;
&lt;li&gt;Add a version alias so that SnapStart can be applied to the published version.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a sample skeleton (CDK in Python):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;aws_lambda&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;aws_lambda_python_alpha&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;lambda_python&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;aws_iam&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;constructs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyLambdaStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Define the Lambda
&lt;/span&gt;    &lt;span class="n"&gt;my_fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lambda_python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PythonFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyPydanticFn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 2. Correct runtime
&lt;/span&gt;      &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app.handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path/to/your/code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;memory_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;snap_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SnapStartConf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ON_PUBLISHED_VERSIONS&lt;/span&gt; &lt;span class="c1"&gt;# 3. Enable SnapStart
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Create a version
&lt;/span&gt;    &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_version&lt;/span&gt;  &lt;span class="c1"&gt;# forces a Version artifact
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;One thing to notice in that pattern:&lt;br&gt;
We rely on &lt;code&gt;my_fn.current_version&lt;/code&gt; to force CDK to emit a AWS::Lambda::Version resource (without that, there’s no version to attach SnapStart to).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to consider
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The pre-snapshot initialization phase has a time limit (the INIT time limit of 10 seconds) as usual. Your before-snapshot hooks count toward that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After snapshot restore, the @after_restore hook must finish within 10 seconds as well, else it might throw a SnapStartTimeoutException.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whenever your code (or dependencies) change in a way that should invalidate the snapshot, you must force a new version so a new snapshot is captured. If CDK's version hashing doesn't catch something, you can call version.invalidate_version_based_on(...) with a changing token (e.g. content hash of certain files) to ensure version recreation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the time of writing, SnapStart is not supported with ephemeral storage &amp;gt; 512 MB, EFS, provisioned concurrency, or containers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, AWS Lambda SnapStart for Python removes one of the biggest hurdles to running complex, model-heavy applications in a serverless environment. By snapshotting your function’s fully initialized state at deployment time, it eliminates the repeated cost of initializing frameworks like Pydantic on every cold start. With a small configuration change in CDK and a few runtime hooks to manage transient state, you can achieve consistently fast startup times and make Lambda a practical, scalable choice even for Python applications that used to be too slow to launch.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>cloud</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
