<?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: Robert J. Berger</title>
    <description>The latest articles on DEV Community by Robert J. Berger (@rberger).</description>
    <link>https://dev.to/rberger</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%2F22361%2Fa66f645d-e925-427d-96a2-7d535cc82241.jpeg</url>
      <title>DEV Community: Robert J. Berger</title>
      <link>https://dev.to/rberger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rberger"/>
    <language>en</language>
    <item>
      <title>Building AWS Ruby Lambdas that Require Gems with Native Extension</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Mon, 30 Jan 2023 01:29:45 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-aws-ruby-lambdas-that-require-gems-with-native-extension-17h</link>
      <guid>https://dev.to/aws-builders/building-aws-ruby-lambdas-that-require-gems-with-native-extension-17h</guid>
      <description>&lt;h2&gt;
  
  
  What's the Problem?
&lt;/h2&gt;

&lt;p&gt;If your Ruby development environment (e.g. a Mac) is different from your target Lambda in terms of operating system or architecture, there may be problems creating your Lambda image. This is especially true if your Ruby Gem Dependencies include C extensions. Without taking special steps, you may end up with a Lambda image that won't work when you try to run the Lambda.&lt;/p&gt;

&lt;p&gt;This is due to the fact that some Gems have native extensions that ether have prebuilt versions for specific target OS and architectures. Many common Gems have native extentions. Some of these include Gems like &lt;code&gt;nokogiri&lt;/code&gt;, &lt;code&gt;json&lt;/code&gt;,  &lt;code&gt;nio4r&lt;/code&gt; and &lt;code&gt;ffi&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;bundle install&lt;/code&gt; on your Mac to create a &lt;code&gt;vendor/bundle&lt;/code&gt; that you expect to package into your Lambda image, you will only get the versions of these GEMS that are for your Mac OS and architecture. For instance an M1 Mac will install native extention Gems for &lt;code&gt;arm64-darwin&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;If you zip that up into your Lambda function image and run it, you may see an error message similar to the following in your Cloudwatch logs when trying to run the Lambda (Gems that have Extensions will be listed in the &lt;code&gt;Could not  find&lt;/code&gt; line):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"errorMessage": "Could not find byebug-11.1.3, json-2.6.2, nokogiri-1.14.0, racc-1.6.2 in any of the sources",
"errorType": "Init&amp;lt;Bundler::GemNotFound&amp;gt;",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this situation would happen not just for Macs, but for any development environment that is not the same OS (Linux) and runtime architecture (arm64 or amd64/x86_64) as your target Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundle in a Quick Lambda Linux Docker Container
&lt;/h2&gt;

&lt;p&gt;The solution, as usual in such cases, is to use Docker to do the Gem Bundling in an environment that is the same as your target Lambda. It would be nice to still do it as a series of shell commands or part of a script and not bother with needing a &lt;code&gt;Dockerfile&lt;/code&gt;. Turns out its pretty easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dir=`pwd`
bundle config set --local deployment 'true'
docker run --platform=linux/amd64 \
  -e BUNDLE_SILENCE_ROOT_WARNING=1 \
  -v `pwd`:`pwd` \
  -w `pwd` \ 
  amazon/aws-sam-cli-build-image-ruby2.7 bundle install
bundle config unset deployment
bundle config unset path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command effectively runs &lt;code&gt;bundle install&lt;/code&gt; in your current directory, except it is being run in the context of an AWS Lambda amd64 (x86_64) Linux with all the build tools for Ruby.&lt;/p&gt;

&lt;p&gt;This Docker runtime has the current directory of your Mac mapped into its current directory. So it can read and write any files and directories it needs from your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dissecting the Commands
&lt;/h2&gt;

&lt;p&gt;Here's some more details on what each line of the command do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;dir=`pwd`&lt;/code&gt;
Capture the current directory path in a shell variable just to make it easy to reuse in the following commands&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;bundle config set --local deployment 'true'&lt;/code&gt;&lt;br&gt;
Set &lt;code&gt;bundler&lt;/code&gt; configuration to &lt;code&gt;deployment&lt;/code&gt; mode&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;bundle install --deployment&lt;/code&gt; &lt;a href="https://bundler.io/v2.4/man/bundle-install.1.html#OPTIONS" rel="noopener noreferrer"&gt;among other flags are depreciated&lt;/a&gt; and its now recommended to use &lt;a href="https://bundler.io/v2.4/man/bundle-config.1.html" rel="noopener noreferrer"&gt;bundle config&lt;/a&gt; to manage things like we do here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Either style of command updates the local &lt;code&gt;.bundle/config&lt;/code&gt; in your project with
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; BUNDLE_DEPLOYMENT: "true"
 BUNDLE_PATH: "vendor/bundle"
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--local&lt;/code&gt; ensures that this gets applied only to your project&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deployment  true&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Disallows changes to the &lt;code&gt;Gemfile&lt;/code&gt; if the &lt;code&gt;Gemfile.lock&lt;/code&gt; is older than Gemfile&lt;/li&gt;
&lt;li&gt;It also sets the &lt;code&gt;BUNDLE_PATH&lt;/code&gt; to &lt;code&gt;vendor/bundle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This config be "sticky" after this which you probably don't want if you later do normal local development 

&lt;ul&gt;
&lt;li&gt;Later &lt;code&gt;config unset&lt;/code&gt; commands will unset these&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;docker run&lt;/code&gt;&lt;br&gt;
The start of the &lt;a href="https://docs.docker.com/engine/reference/commandline/run/" rel="noopener noreferrer"&gt;docker command run&lt;/a&gt;. The following are the arguments to this command&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-e BUNDLE_SILENCE_ROOT_WARNING=1&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suppress the warning not to run as root, as docker default to the root user&lt;/li&gt;
&lt;li&gt;Processes in docker container run as &lt;code&gt;root&lt;/code&gt; unless you do some work to add users and set it to run as another user. And &lt;code&gt;bundler&lt;/code&gt; issues warnings if you run it as root. So the -e flag creates an Environment Variable &lt;code&gt;BUNDLE_SILENCE_ROOT_WARNING&lt;/code&gt; that is consumed by &lt;code&gt;bundler&lt;/code&gt; and tells &lt;code&gt;bundler&lt;/code&gt; not to issue that warning. &lt;/li&gt;
&lt;li&gt; This is not required, but those warnings are aestheticly unpleasing. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-v ${dir}:${dir}&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bind mount a volume: Maps the top of your ruby project with the Gemfile (&lt;code&gt;`pwd`&lt;/code&gt; aka &lt;code&gt;${dir}&lt;/code&gt; aka your current directory) into the docker container with the same path.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-w ${dir}&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working directory inside the container: Sets the working directory of the docker container runtime to be same as our current directory path. &lt;/li&gt;
&lt;li&gt;This flag + the &lt;code&gt;-v&lt;/code&gt; flag enables_the &lt;code&gt;bundler&lt;/code&gt; process running in the container to read and write all the files and directories in your project.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;amazon/aws-sam-cli-build-image-ruby2.7&lt;/code&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Positional argument that specifies the docker image to use&lt;/li&gt;
&lt;li&gt;We use the &lt;a href="https://hub.docker.com/r/amazon/aws-sam-cli-build-image-ruby2.7" rel="noopener noreferrer"&gt;amazon/aws-sam-cli-build-image-ruby2.7&lt;/a&gt; image from DockerHub because its an image that has everything your ruby lambda Linux runtime will have plus it has all the Linux build tools needed to build any Ruby gems with extentions. &lt;/li&gt;
&lt;li&gt;You don't need to be using SAM to use this image. &lt;/li&gt;
&lt;li&gt;The same image is also available from AWS ECR as &lt;a href="https://gallery.ecr.aws/sam/build-ruby2.7" rel="noopener noreferrer"&gt;public.ecr.aws/sam/build-ruby2.7&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;It would be possible to use other images that have what you need, but this one seems to work well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;bundle install&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Positional last argument: The &lt;code&gt;bundle&lt;/code&gt; command to run once the docker image starts.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;bundle config unset deployment&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This combined with the next command unsets what &lt;code&gt;bundle config set deployment true&lt;/code&gt; set.&lt;/li&gt;
&lt;li&gt;You will probably want to run these two commands before doing any &lt;code&gt;bundle&lt;/code&gt; commands before doing any normal local development.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;bundle config unset path&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See previous command description&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Zip it Up Good
&lt;/h2&gt;

&lt;p&gt;You need to zip up your Ruby Project including the newly created &lt;code&gt;vendor/bundle&lt;/code&gt;. This assume your current directory is your Ruby project with all your code, Gemfile, Gemfile.lock etc. Won't go into the details here. This is just the mechanics of packaging up your project into a zipfile suitable for uploading to your Lambda Function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir -p pkg/out
zip -q -r pkg/out/my-lambda.zip . -x pkg/*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Upload to Lambda function
&lt;/h3&gt;

&lt;p&gt;This assumes you have already created your Lambda Function named &lt;code&gt;my-lambda&lt;/code&gt; in your AWS Account in &lt;code&gt;us-west-2&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda update-function-code &lt;span class="nt"&gt;--function-name&lt;/span&gt; my-lambda &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zip-file&lt;/span&gt; fileb://pkg/out/my-lambda.zip &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For reference, the official docs: &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/ruby-package.html" rel="noopener noreferrer"&gt;Deploy Ruby Lambda functions with .zip file archives&lt;/a&gt; (Ignore their instructions for using &lt;code&gt;bundle&lt;/code&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Things Up
&lt;/h2&gt;

&lt;p&gt;If you use a modern Linux environment with the same processor architecture as your target Lambda, you can probably get away without having to use this Docker work around. Though it could still be safest to use this even there. The docker container being used is literally the same linux runtime as the target Lambda.&lt;/p&gt;

&lt;p&gt;Otherwise, this technique should work for just about any development environment that can run Docker containers and run similar commands. Its only been tested it on a Mac though.&lt;/p&gt;

&lt;p&gt;Do let me know if you have any feedback,  know of any better ways or have improvements to this technique in the comments or by contacting me directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Robert J. Berger&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As the Chief Architect, Rob guides the evolution of the &lt;a href="https://www.informediq.com" rel="noopener noreferrer"&gt;InformedIQ&lt;/a&gt; Software and Infrastructure. His experience spans the rise and fall of many technology lifecycles from machine vision, digitization of professional video production equipment, Internet Infrastructure, Wireless, E-commerce, Big Data, IoT, DevOps and Machine Learning. He has been a founder or a technical leader in several startups in Silicon Valley.&lt;/p&gt;

&lt;p&gt;Mastodon: &lt;a href="https://hachyderm.io/@rberger" rel="noopener noreferrer"&gt;https://hachyderm.io/@rberger&lt;/a&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Python src Layout for AWS Lambdas</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Thu, 26 Jan 2023 08:41:39 +0000</pubDate>
      <link>https://dev.to/aws-builders/python-src-layout-for-aws-lambdas-4ple</link>
      <guid>https://dev.to/aws-builders/python-src-layout-for-aws-lambdas-4ple</guid>
      <description>&lt;p&gt;Over the last several years, the Python community has been moving towards a packaging layout format known as the &lt;code&gt;src layout&lt;/code&gt; as the recommended way to organize directories and files for Python Packages.&lt;/p&gt;

&lt;p&gt;The main takeaways of the &lt;code&gt;src layout&lt;/code&gt; are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your project has a &lt;code&gt;src&lt;/code&gt; folder

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;src&lt;/code&gt; folder only has sub folder[s]. Each sub folder&lt;/li&gt;
&lt;li&gt;Is a Python Package&lt;/li&gt;
&lt;li&gt;Its name is the name of the package&lt;/li&gt;
&lt;li&gt;It contains an &lt;code&gt;__init__.py&lt;/code&gt; (that's pretty much what makes it a Python Package)&lt;/li&gt;
&lt;li&gt;All Python source code that makes up your appplication (other than tests or build utilities) for the project are in these sub folder[s]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Modern Python tooling expects / works best with this layout&lt;/li&gt;

&lt;li&gt;Makes packaging easier and less prone to errors&lt;/li&gt;

&lt;li&gt;"editable" installs for development and regular installation for testing&lt;/li&gt;

&lt;li&gt;Generally no need for "import trickery"&lt;/li&gt;

&lt;li&gt;References at end of this article&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;We were looking for standards and best practices for organizing our Python AWS Lambda projects using &lt;a href="https://python-poetry.org" rel="noopener noreferrer"&gt;Poetry&lt;/a&gt; in our monorepo and kept coming across the &lt;code&gt;src layout&lt;/code&gt; as the recommended way to organize Python projects that had multiple modules. At the same time just about all examples of Python Lambda projects did not use &lt;code&gt;src layout&lt;/code&gt; and in fact seemed to want to have the handler module at the top level of the project directory.  There just weren't many examples of this combination of &lt;code&gt;src layout&lt;/code&gt; &lt;code&gt;Poetry&lt;/code&gt; and AWS Lambdas.&lt;/p&gt;

&lt;p&gt;Turns  out that it wasn't really difficult. The main "trick" is setting the Lambda &lt;code&gt;handler&lt;/code&gt; name correctly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All this example code is available at  &lt;a href="https://github.com/Informed/blogpost-python-src-layout" rel="noopener noreferrer"&gt;Informed/blogpost-python-src-layout&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Example src  layout for an AWS Lambda
&lt;/h2&gt;

&lt;p&gt;Here's an example &lt;code&gt;src layout&lt;/code&gt; suitable for AWS Lambda&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a section of what could be a regular or monorepo that shows one lambda project: &lt;code&gt;my_lambda&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;You could have more lambda projects under services, each would be a separate subproject with the same layout&lt;/li&gt;
&lt;li&gt;How to implement such a multi-project monorepo is left to a future blog post&lt;/li&gt;
&lt;li&gt;The actual lambda code is in &lt;code&gt;src/my_lambda&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;There is an  example of additional non-code files in &lt;code&gt;src/my_lambda/stuff&lt;/code&gt; that might be used by the lambda or &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html" rel="noopener noreferrer"&gt;lambda layers&lt;/a&gt; such as an &lt;a href="https://aws-otel.github.io/docs/getting-started/lambda/lambda-python" rel="noopener noreferrer"&gt;otel layer&lt;/a&gt; that will be part of the deployed zip image.

&lt;ul&gt;
&lt;li&gt;This is not required, just showing an example of how you can easily bundle in additional non-code files&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;You can have additional Python modules in here like &lt;code&gt;utils.py&lt;/code&gt; that are imported into the &lt;code&gt;handler.py&lt;/code&gt; module&lt;/li&gt;

&lt;li&gt;If your Lambda application is more complex you can have local Packages as subdirectories of &lt;code&gt;src/my_lambda&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;blogpost-python-src-layout
├── LICENSE
├── README.md
├── doc
│   └── python_src_layout_for_aws_lambdas.md
└── services
    └── my_lambda
        ├── CHANGELOG.md
        ├── LICENSE
        ├── README.md
        ├── poetry.lock
        ├── pyproject.toml
        ├── src
        │   └── my_lambda
        │       ├── __init__.py
        │       ├── handler.py
        │       ├── stuff
        │       │   ├── config.yml
        │       │   └── data.csv
        │       └── utils.py
        └── tests
            └── test_my_handler.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example  Poetry pyproject.toml
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.poetry]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my_lambda"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.0.0"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This is my lambda which is mine"&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Da Dev &amp;lt;daDev@example.com&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^3.9"&lt;/span&gt;
&lt;span class="py"&gt;pyyaml&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6.0"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.poetry.group.dev.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;coverage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;extras&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"toml"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^6.5.0"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;pytest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^7.2.0"&lt;/span&gt;
&lt;span class="py"&gt;flake8&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^6.0.0"&lt;/span&gt;
&lt;span class="py"&gt;python-lambda-local&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^0.1.13"&lt;/span&gt;
&lt;span class="py"&gt;boto3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.20.32"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.pytest.ini_options]&lt;/span&gt;
&lt;span class="py"&gt;addopts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;["--import-mode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;importlib&lt;/span&gt;&lt;span class="s"&gt;"]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;
&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;["poetry-core&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;"]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"poetry.core.masonry.api"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Poetry expects a &lt;code&gt;src layout&lt;/code&gt;. The fact that the &lt;code&gt;name&lt;/code&gt; is the same as the directory name under &lt;code&gt;src&lt;/code&gt; and that directory is a Python Package (i.e. it has an &lt;code&gt;__init__.py&lt;/code&gt; and source code) means that Poetry will treat everything in &lt;code&gt;src/my_lambda&lt;/code&gt; as the package it's going to build. Everything in &lt;code&gt;src/my_lambda&lt;/code&gt; and any dependencies under &lt;code&gt;[tool.poetry.dependencies]&lt;/code&gt;  will end up in the deployed zip image that will become the lambda function image. In this example we have the &lt;code&gt;pyyaml&lt;/code&gt; package included.&lt;/p&gt;

&lt;p&gt;The dependencies under &lt;code&gt;[tool.poetry.group.dev.dependencies]&lt;/code&gt; will only be used for local builds, and not included in the zip image. You can put dependencies that are in the lambda runtime, like boto3 or any lambda layers (like Otel, or aws_powertools) in &lt;code&gt;[tool.poetry.group.dev.dependencies]&lt;/code&gt; if you want them available for local testing.&lt;/p&gt;

&lt;p&gt;There is also the option (and some folks recommend) to put a more up to date and fixed version of boto3 in &lt;code&gt;[tool.poetry.dependencies]&lt;/code&gt; since AWS  can change the version used in the runtime at any time. The main downside of this is it makes your uploaded image larger.&lt;/p&gt;

&lt;p&gt;The section '[tool.pytest.ini_options]' is recommended for all new packages that use &lt;a href="https://docs.pytest.org/en/7.2.x/explanation/goodpractices.html#tests-outside-application-code" rel="noopener noreferrer"&gt;pytest&lt;/a&gt; for running their tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set the Lambda Handler to be the "two dot" format
&lt;/h2&gt;

&lt;p&gt;The main thing that is different for configuring Lambdas when you use the &lt;code&gt;src layout&lt;/code&gt; is that you must specify the handler as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;package_name&amp;gt;.&amp;lt;handler_module_name&amp;gt;.&amp;lt;handler_function_name&amp;gt;`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also known as the &lt;a href="https://gist.github.com/gene1wood/06a64ba80cf3fe886053f0ca6d375bc0" rel="noopener noreferrer"&gt;2 dot solution&lt;/a&gt; that specifies the handler like a package import path.&lt;/p&gt;

&lt;p&gt;In our example that would be:&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="n"&gt;my_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example src/my_lambda/handler.py
&lt;/h2&gt;

&lt;p&gt;This is just a very minimal lambda function that demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Importing the &lt;code&gt;PyYaml&lt;/code&gt; package we specify as a dependency in the &lt;code&gt;pyproject.toml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Printing the &lt;code&gt;event&lt;/code&gt; info passed in the handler&lt;/li&gt;
&lt;li&gt;Calling an imported function from the same lambda package (&lt;code&gt;utils.my_util&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Fetching and printing one of the non-source files in &lt;code&gt;src/my_lambda/stuff&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pkgutil&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;

&lt;span class="c1"&gt;# We're not using pyyaml, just showing that it's installed
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loading function&lt;/span&gt;&lt;span class="sh"&gt;"&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;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received event: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value1 = &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value2 = &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value3 = &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;my_util&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cwd: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getcwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pkgutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_lambda/stuff/config.yml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;File contents of my_lambda/stuff/config.yml:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Return the first key value
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building and deploying the Lambda
&lt;/h2&gt;

&lt;p&gt;This is the basics of building and deploying the lambda using Poetry. You may have a more sophisticated CI/CD process.&lt;/p&gt;

&lt;p&gt;These commands should be executed in the top level of the project (i.e. &lt;code&gt;my_repo/services/my_lambda&lt;/code&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the distribution
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: These commands may not work for your actual application if it has dependencies that need to be compiled as part of the build process, particularly if you are building your application on a machine that is not the same architecture as your target Lambda (i.e. building on an M1 Macintosh and targeting an x86_64 lambda). In that case you need to use Docker to do your building which is beyond the scope of this post.&lt;/p&gt;

&lt;p&gt;These examples were only tested on an M1 Mac but should work on any modern Mac or *nix machine and will build zip images suitable for targeting Linux x86 Lambdas if you do not need to build binary dependencies.&lt;br&gt;
The &lt;code&gt;pip install&lt;/code&gt; shown later uses the argument &lt;code&gt;--platform manylinux2014_x86_64&lt;/code&gt; which forces the packaging to only include binary wheels built for Linux x86_64. The argument &lt;code&gt;--only-binary :all:&lt;/code&gt; ensures that it will not try to compile any source only dependencies and instead will emit an error letting you know that you must build the package in the native target environment (i.e. use a Docker build process). &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1) First we export the dependencies from Poetry so that we can later use &lt;code&gt;pip install&lt;/code&gt; to create the files needed for the zip image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;poetry export -f requirements.txt --output requirements.txt  --without-hashes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Have Poetry build all the wheels and such&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;poetry build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will result in a bunch of files in the directory &lt;code&gt;dist&lt;/code&gt; the top level of your project.&lt;/p&gt;

&lt;p&gt;3) Use pip to create the packages&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;poetry run pip install -r requirements.txt --upgrade --only-binary :all: --platform manylinux2014_x86_64 --target package dist/*.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate all the wheels of all the dependencies in the &lt;code&gt;package&lt;/code&gt; directory, suitable for zipping into the lambda image. &lt;/p&gt;

&lt;p&gt;4) Zip up the image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd package
mkdir -p out
zip -r -q out/my-lambda.zip . -x '*.pyc' out
cd ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will result in a file in &lt;code&gt;my_repo/services/my_lambda/package/out/my-lambda.zip&lt;/code&gt; that is suitable for uploading to AWS as the Lambda Function image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Lambda function
&lt;/h3&gt;

&lt;p&gt;You could do this in the AWS console or use the following AWS CLI Commands (If you use the AWS Console, it will create the execution role and trust policy automatically by default).&lt;/p&gt;

&lt;p&gt;1) Create the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-awscli.html#with-userapp-walkthrough-custom-events-create-iam-role" rel="noopener noreferrer"&gt;execution role and trust policy&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws iam create-role --role-name my-lambda-ex --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You should get a result like the following.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy the arn from this output and you will use it for the next command&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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;"Role"&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;"Path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"RoleName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-lambda-ex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"RoleId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AROAWUWOOBVLDKY7ZE7P3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::1234567890123:role/my-lambda-ex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"CreateDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-01-12T06:08:22+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AssumeRolePolicyDocument"&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda.amazonaws.com"&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;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&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;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;2) Create the Lambda function&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have to set the &lt;code&gt;handler&lt;/code&gt; to the 2 dot style &lt;code&gt;my_lambda.handler.handler&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change the fake account-id (&lt;code&gt;1234567890123&lt;/code&gt;) in the &lt;code&gt;role arn&lt;/code&gt; to your AWS account-id&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda create-function &lt;span class="nt"&gt;--function-name&lt;/span&gt; my-lambda &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zip-file&lt;/span&gt; fileb://package/out/my-lambda.zip &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--handler&lt;/span&gt; my_lambda.handler.handler &lt;span class="nt"&gt;--runtime&lt;/span&gt; python3.9 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt; arn:aws:iam::1234567890123:role/my-lambda-ex
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  If you already have created the Lambda function
&lt;/h4&gt;

&lt;p&gt;If you created the Lambda function some other way like via the Console or you want to update the Lambda function you created earlier, you can use the following command just to update it.&lt;/p&gt;

&lt;p&gt;This mechanism loads the lambda from the S3 asset you uploaded earlier&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda update-function-code &lt;span class="nt"&gt;--function-name&lt;/span&gt; my-lambda &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zip-file&lt;/span&gt; fileb://package/out/my-lambda.zip &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;At this point your lambda should be ready to test. You can use the default test input in the AWS Lambda Console:&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;"key1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value3"&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;You can run the default &lt;code&gt;Test&lt;/code&gt; on the AWS Lambda console and you will see all the log output as well as the result which would look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test Event Name
basic

Response
"value1"

Function Logs
Loading function
START RequestId: 2f33f7b2-ddae-46df-a61b-1263ef404d6b Version: $LATEST
Received event: {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
value1 = value1
value2 = value2
value3 = value3
Hello from my_util
cwd: /var/task
File contents of my_lambda/stuff/config.yml:
['thing:\n', '  hand: manicure\n']
END RequestId: 2f33f7b2-ddae-46df-a61b-1263ef404d6b
REPORT RequestId: 2f33f7b2-ddae-46df-a61b-1263ef404d6b  Duration: 1.57 ms   Billed Duration: 2 ms   Memory Size: 128 MB Max Memory Used: 40 MB  Init Duration: 172.83 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you could run it via the AWS CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws lambda invoke --cli-binary-format raw-in-base64-out \
  --function-name my-lambda \
  --payload '{ "key1": "value1", "key2": "value2", "key3": "value3"}' \
  outputfile.txt   --log-type Tail \
  --query 'LogResult' --output text |  base64 -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will print the log output (same as what  was shown above in example of running the Test in the AWS Console) &lt;/p&gt;

&lt;p&gt;You can see the returned value of the handler in &lt;code&gt;outputfile.txt&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running tests with &lt;code&gt;Pytest&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;There is also a simple test example in &lt;code&gt;services/my_lambda/tests/test_my_handler.py&lt;/code&gt;&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;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;my_lambda.handler&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;

&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&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;test_my_handler&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Emulate running in the same directory context as the lambda would
&lt;/span&gt;    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before you run pytest for the first time in this project (or before you do any local development that needs  any of the packages in the &lt;code&gt;[tool.poetry.group.dev.dependencies]&lt;/code&gt; section of &lt;code&gt;pyproject.toml&lt;/code&gt;) you need to run at least once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install all the dependencies listed in your main &lt;code&gt;[tool.poetry.dependencies]&lt;/code&gt; and in &lt;code&gt;[tool.poetry.group.dev.dependencies]&lt;/code&gt; in the virtualenv that is managed by Poetry.&lt;/p&gt;

&lt;p&gt;You can run the test from &lt;code&gt;services/my_lambda&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;poetry run pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  References on &lt;code&gt;src layout&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PyPA Python Packaging User Guide &lt;a href="https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/#src-layout-vs-flat-layout" rel="noopener noreferrer"&gt;src layout vs flat layout&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.pytest.org/en/6.2.x/goodpractices.html#tests-outside-application-code" rel="noopener noreferrer"&gt;Pytest Good Integration Practices: Tests outside application code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The post that pretty much started the movement: &lt;a href="https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure" rel="noopener noreferrer"&gt;Packaging a python library&lt;/a&gt; by &lt;a href="https://blog.ionelmc.ro/about/" rel="noopener noreferrer"&gt;Ionel Cristian Mărieș&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Another early influential blog post &lt;a href="https://hynek.me/articles/testing-packaging/" rel="noopener noreferrer"&gt;Testing &amp;amp; Packaging&lt;/a&gt; by &lt;a href="https://hynek.me/about/" rel="noopener noreferrer"&gt;Hynek Schlawack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://guicommits.com/organize-python-code-like-a-pro/" rel="noopener noreferrer"&gt;Organize Python code like a PRO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://browniebroke.com/blog/convert-existing-poetry-to-src-layout/" rel="noopener noreferrer"&gt;Convert a Poetry package to the src layout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>poetry</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Deploy EventCatalog to AWS CloudFront with Google SSO Access Control via Terraform</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Mon, 18 Jul 2022 05:15:01 +0000</pubDate>
      <link>https://dev.to/aws-builders/deploy-eventcatalog-to-aws-cloudfront-with-google-sso-access-control-via-terraform-7jp</link>
      <guid>https://dev.to/aws-builders/deploy-eventcatalog-to-aws-cloudfront-with-google-sso-access-control-via-terraform-7jp</guid>
      <description>&lt;p&gt;This article shows how to deploy your own &lt;a href="https://www.eventcatalog.dev" rel="noopener noreferrer"&gt;EventCatalog&lt;/a&gt; in &lt;a href="https://aws.amazon.com/cloudfront/" rel="noopener noreferrer"&gt;AWS CloudFront&lt;/a&gt; via &lt;a href="https://www.terraform.io" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; and updates to the Catalog via CI/CD (&lt;a href="http://www.circleci.com" rel="noopener noreferrer"&gt;CircleCi&lt;/a&gt; in this case, but can be easily applied to other CI systems). It also shows how to use &lt;a href="https://aws.amazon.com/lambda/edge/" rel="noopener noreferrer"&gt;Lambda@Edge&lt;/a&gt; to implement &lt;a href="https://developers.google.com/identity/protocols/oauth2/openid-connect" rel="noopener noreferrer"&gt;Google SSO  / OpenID Connect&lt;/a&gt; via the &lt;a href="https://github.com/Widen/cloudfront-auth" rel="noopener noreferrer"&gt;Widen/cloudfront-auth&lt;/a&gt; Project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.eventcatalog.dev" rel="noopener noreferrer"&gt;EventCatalog&lt;/a&gt; was created by &lt;a href="https://www.boyney.io" rel="noopener noreferrer"&gt;David Boyne&lt;/a&gt;. It is an wonderful Open Source project that acts as a unifying Documentation tool for Event-Driven Architectures. It helps you document, visualize and keep on top of your Event Driven Architectures' events, schemas, producers, consumers and services.&lt;/p&gt;

&lt;p&gt;You can go to the above links to find out more about EventCatalog itself. &lt;/p&gt;

&lt;p&gt;Working on having a github repo with the full example at some point soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;p&gt; 1. Create EventCatalog Project&lt;/p&gt;

&lt;p&gt;       1.1. Requirements&lt;/p&gt;

&lt;p&gt;       1.2. Generate the Scaffold Project Website&lt;br&gt;
 2. Create the Terraform to deploy to Cloudfront&lt;/p&gt;

&lt;p&gt;       2.3. Create the file main.tf&lt;/p&gt;

&lt;p&gt;       2.4. Create the lambda.tf file&lt;/p&gt;

&lt;p&gt;       2.5. Create the cloudfront.tf file&lt;/p&gt;

&lt;p&gt;       2.6. Create the variables.tf file&lt;/p&gt;

&lt;p&gt;       2.7. Create the sandbox.tfvars file&lt;/p&gt;

&lt;p&gt;       2.8. Create a placeholder lambda code zip file&lt;br&gt;
 3. Initial deployment with temp lambda@edge code&lt;br&gt;
 4. Build the Lambda@edge code with Widen/cloudfront-auth&lt;/p&gt;

&lt;p&gt;       4.9. Create the OAuth Credentials in the Google developers console&lt;/p&gt;

&lt;p&gt;             a. Create a new Project&lt;/p&gt;

&lt;p&gt;             b. Create OAuth Consent and Credentials&lt;/p&gt;

&lt;p&gt;       4.10. Generate the code for Lambda@edge&lt;br&gt;
 5. Deploy the EventCatalog content to S3&lt;/p&gt;

&lt;p&gt;       5.11. Manual deployment&lt;/p&gt;

&lt;p&gt;       5.12. Deployment with CircleCi&lt;br&gt;
 6. Deploy the new lambda@edge code with terraform&lt;br&gt;
 7. Improvements? Suggestions? Alternatives?&lt;br&gt;
 8. About the Author&lt;/p&gt;
&lt;h2&gt;
  
  
  Create EventCatalog Project
&lt;/h2&gt;

&lt;p&gt;You can create a sample EventCatalog Project using the EventCatalog CLI. This can be the scaffolding for your own project. In this case we're going to use the sample project it will install as our project for this article.&lt;/p&gt;
&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js version &amp;gt;= 14 or above (which can be checked by running node -v). You can use nvm for managing multiple Node versions on a single machine installed

&lt;ul&gt;
&lt;li&gt;We're going to be using Node.js version 16.x.x&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Yarn version &amp;gt;= 1.5 (which can be checked by running yarn --version). Yarn is a performant package manager for JavaScript and replaces the npm client. It is not strictly necessary but highly encouraged.

&lt;ul&gt;
&lt;li&gt;We're using Yarn 1.22.x&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Generate the Scaffold Project Website
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to a directory on your computer's filesystem where you want to save the project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate the scaffolding for the project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We're  going to call the project &lt;code&gt;my-catalog&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @eventcatalog/create-eventcatalog@latest my-catalog
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* This will generate a new directory structure as a git project:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
my-catalog
├── services
│   ├── Basket Service
│   │     └──index.md
│   ├── Data Lake
│   │     └──index.md
│   ├── Payment Service
│   │     └──index.md
│   ├── Shipping Service
│   │     └──index.md
├── events
│   ├── AddedItemToCart
│   │     └──versioned
│   │     │  └──0.0.1
│   │     │     └──index.md
│   │     │     └──schema.json
│   │     └──index.md
│   │     └──schema.json
│   ├── OrderComplete
│   │     └──index.md
│   │     └──schema.json
│   ├── OrderConfirmed
│   │     └──index.md
│   │     └──schema.json
│   ├── OrderRequested
│   │     └──index.md
│   ├── PaymentProcessed
│   │     └──index.md
├── static
│   └── img
├── eventcatalog.config.js
├── .eventcatalog-core/
├── package.json
├── README.md
├── yarn.lock
├── Dockefile
├── .dockerignore
├── .gitignore
└── .git/
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Change directory into &lt;code&gt;my-catalog&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can preview the EventCatalog with the command:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;  npm run dev
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;And then point your browser to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will be able to view the sample Events, Services, and Domains there.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Once you are done checking it out, kill the npm proces with CTL-C&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Create the Terraform to deploy to Cloudfront
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;terraform&lt;/code&gt; directory in &lt;code&gt;my-catalog&lt;/code&gt; and add an &lt;code&gt;assets&lt;/code&gt; directory to it&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You could make this directory outside of the catalog if you would prefer to manage it that way
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; terraform/assets
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;.gitignore&lt;/code&gt; in the terraform directory&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  curl https://raw.githubusercontent.com/github/gitignore/main/Terraform.gitignore -o terraform/.gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the file &lt;code&gt;main.tf&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This file has all the terraform code to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up the terraform environment&lt;/li&gt;
&lt;li&gt;Specify the AWS provider&lt;/li&gt;
&lt;li&gt;alt_fqdns a placeholder for now. May want to make alt_fqds a variable. It needs to be a list of strings Used by cloudfront.tf to specify aliases for the certificate and DNS but its kind of hard to support that with the sso callback
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;###&lt;/span&gt;
&lt;span class="c1"&gt;### Using locals to form variables by concatinating input variables&lt;/span&gt;
&lt;span class="c1"&gt;### Unfortunately can not do that in variables.tf or &amp;lt;env&amp;gt;.tfvars&lt;/span&gt;
&lt;span class="c1"&gt;###&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fqdn&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.app_name}-${var.project_name}.${var.environment}.${var.base_domain_name}"&lt;/span&gt;
  &lt;span class="nx"&gt;alt_fqdns&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;zone_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.environment}.${var.base_domain_name}"&lt;/span&gt;
  &lt;span class="nx"&gt;lambda_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.environment}-${var.project_name}-${var.app_name}-${var.lambda_name_suffix}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.2.0"&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="c1"&gt;# Need to use version &amp;lt; 4.0.0 to work with cloudposse/cloudfront-s3-cdn&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.75.2"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# You should use a different state management than local&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"local"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the &lt;code&gt;lambda.tf&lt;/code&gt; file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Configure the AWS IAM role and policies for the lambda@edge&lt;/li&gt;
&lt;li&gt;Create the lambda@edge service
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;### Set up IAM role and policies for the lambda&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_edge_assume_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Service"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"edgelambda.amazonaws.com"&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;span class="c1"&gt;# Define the IAM role for logging from the Lambda function.&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_edge_logging_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"logs:CreateLogGroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"logs:CreateLogStream"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"logs:PutLogEvents"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:logs:*:*:*"&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="c1"&gt;# Add IAM policy for logging to the iam role&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_edge_logging"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.lambda_name}-lambda_edge_logging"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_edge_logging_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# Create the iam role for the lambda function&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_edge"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.app_name}_lambda_edge_cloudfront"&lt;/span&gt;
  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_edge_assume_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create the lambda@edge function&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"edge"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_file_name&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_name&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"index.handler"&lt;/span&gt;
  &lt;span class="nx"&gt;timeout&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5"&lt;/span&gt;
  &lt;span class="nx"&gt;publish&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# The filebase64sha256() function is available in Terraform 0.11.12 and later&lt;/span&gt;
  &lt;span class="c1"&gt;# For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function:&lt;/span&gt;
  &lt;span class="c1"&gt;# source_code_hash = "${base64sha256(file("lambda_function_payload.zip"))}"&lt;/span&gt;
  &lt;span class="nx"&gt;source_code_hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filebase64sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nodejs12.x"&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the &lt;code&gt;cloudfront.tf&lt;/code&gt; file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create the CloudFront CDN instance and S3 bucket with lambda@edge association 

&lt;ul&gt;
&lt;li&gt;Uses the &lt;a href="https://github.com/cloudposse/terraform-aws-cloudfront-s3-cdn" rel="noopener noreferrer"&gt;cloudposse/cloudfront-s3-cdn/aws&lt;/a&gt; terraform module to do all the hard work&lt;/li&gt;
&lt;li&gt;This module currently will work only with the &lt;code&gt;hashicorp/aws&lt;/code&gt; provider of versions &lt;code&gt;&amp;lt; 4.0.0&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This is why we are not using the latest version of the &lt;code&gt;hashicorp/aws&lt;/code&gt; provider&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Create the TLS Certificate using AWS ACM
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"cloudfront-s3-cdn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloudposse/cloudfront-s3-cdn/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.82.4"&lt;/span&gt;

  &lt;span class="nx"&gt;namespace&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_namespace&lt;/span&gt;
  &lt;span class="nx"&gt;environment&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
  &lt;span class="nx"&gt;stage&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_name&lt;/span&gt;
  &lt;span class="nx"&gt;encryption_enabled&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;allow_ssl_requests_only&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="c1"&gt;# This will allow a complete deletion of the bucket and all of its contents&lt;/span&gt;
  &lt;span class="nx"&gt;origin_force_destroy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# DNS Settings&lt;/span&gt;
  &lt;span class="nx"&gt;parent_zone_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;
  &lt;span class="nx"&gt;acm_certificate_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acm_request_certificate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;aliases&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fqdn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt_fqdns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;ipv6_enabled&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;dns_alias_enabled&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Caching Settings&lt;/span&gt;
  &lt;span class="nx"&gt;default_ttl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
  &lt;span class="nx"&gt;compress&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Website settings&lt;/span&gt;
  &lt;span class="nx"&gt;website_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;index_document&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"index.html"&lt;/span&gt;
  &lt;span class="nx"&gt;error_document&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"404.html"&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acm_request_certificate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Link Lambda@Edge to the CloudFront distribution&lt;/span&gt;
  &lt;span class="nx"&gt;lambda_function_association&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="nx"&gt;event_type&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"viewer-request"&lt;/span&gt;
    &lt;span class="nx"&gt;include_body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;lambda_arn&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qualified_arn&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;###&lt;/span&gt;
&lt;span class="c1"&gt;### Request an SSL certificate&lt;/span&gt;
&lt;span class="c1"&gt;###&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"acm_request_certificate"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;                            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloudposse/acm-request-certificate/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt;                           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;
  &lt;span class="nx"&gt;domain_name&lt;/span&gt;                       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fqdn&lt;/span&gt;
  &lt;span class="nx"&gt;subject_alternative_names&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt_fqdns&lt;/span&gt;
  &lt;span class="nx"&gt;process_domain_validation_options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;ttl&lt;/span&gt;                               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"300"&lt;/span&gt;
  &lt;span class="nx"&gt;wait_for_certificate_issued&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;zone_name&lt;/span&gt;                         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the &lt;code&gt;variables.tf&lt;/code&gt; file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Variable Definitions for EventCatalog-Sandbox
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The region to use for the Terraform run"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"profile"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The local IAM profile to use for the Terraform run"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The environment to use for the Terraform run"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"project_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The name of the project to use"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The name of this app"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eventcatalog"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"base_domain_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The base domain name for the environment"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_namespace"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The namespace prefix for s3 buckets"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"zone_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The route53 zone id for the domain zone of the FQDNs"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"lambda_file_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The name of the lambda function file that was generated by the Widen/cloudfront-auth project"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"lambda_name_suffix"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The suffix to append to the lambda function name to make it unique if need to destroy and recrete CloudFront distribution"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"000"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the &lt;code&gt;sandbox.tfvars&lt;/code&gt; file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This file sets or overrides the default values for the terraform run

&lt;ul&gt;
&lt;li&gt;Set these as appropriate for your environment&lt;/li&gt;
&lt;li&gt;Region may need to be &lt;code&gt;us-east-1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;region&lt;/span&gt;           &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="nx"&gt;profile&lt;/span&gt;          &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sandbox"&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rob"&lt;/span&gt;
&lt;span class="nx"&gt;project_name&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"blogpost"&lt;/span&gt;
&lt;span class="nx"&gt;app_name&lt;/span&gt;         &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eventcatalog"&lt;/span&gt;
&lt;span class="nx"&gt;lambda_file_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assets/temp.zip"&lt;/span&gt;

&lt;span class="c1"&gt;##&lt;/span&gt;
&lt;span class="c1"&gt;## These must be different for your environment&lt;/span&gt;
&lt;span class="nx"&gt;base_domain_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"informediq-infra.com"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_namespace&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"informediq"&lt;/span&gt;
&lt;span class="nx"&gt;zone_id&lt;/span&gt;          &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Z10***********************K7U"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a placeholder lambda code zip file
&lt;/h3&gt;

&lt;p&gt;We have a slight chicken and egg problem where we need to have the&lt;br&gt;
Cloudformation name to create the lambda@edge code zip file with the&lt;br&gt;
&lt;a href="https://github.com/Widen/cloudfront-auth" rel="noopener noreferrer"&gt;Widen/cloudfront-auth&lt;/a&gt; project.&lt;/p&gt;

&lt;p&gt;So we'll make a dummy temp zip file to start with. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a file &lt;code&gt;assets/temp.js&lt;/code&gt; with the following content:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  exports.handler = async (event) =&amp;gt; {
      // TODO implement
      const response = {
          statusCode: 200,
          body: JSON.stringify('Hello from Lambda!'),
      };
      return response;
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Zip that file
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;cd &lt;/span&gt;assets
  zip &lt;span class="nt"&gt;-r&lt;/span&gt; temp.zip temp.js
  &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Initial deployment with temp lambda@edge code
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Setup any credentials/login needed to run the AWS CLI / Terraform CLI from your shell window.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first time you want to run things (or anytime you add terraform modules)&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Do the Terraform apply&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You could do a plan, but we're deploying for the first time anyway&lt;/li&gt;
&lt;li&gt;We are specifying it to use the &lt;code&gt;sandbox.tfvars&lt;/code&gt; file to supply the input of the variables needed
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply  &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sandbox.tfvars
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;The first run of this may take a long time to complete. I've seen it seem to be stuck at&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.cloudfront-s3-cdn.module.logs.aws_s3_bucket.default[0]: Still creating...
module.cloudfront-s3-cdn.aws_s3_bucket.origin[0]: Still creating...
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;for more than 30 minutes. Not sure why. But after the first run its fast.&lt;/p&gt;

&lt;p&gt;You may also get a warning:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  │ Warning: Argument is deprecated
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;You can ignore that. Seems to be something depreciated that is used by the &lt;a href="https://github.com/cloudposse/terraform-aws-cloudfront-s3-cdn" rel="noopener noreferrer"&gt;cloudposse/cloudfront-s3-cdn/aws&lt;/a&gt; terraform module.&lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;At the end of the run it will print out the outputs with something like:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Apply complete! Resources: 14 added, 0 changed, 0 destroyed.

  Outputs:

  cf_aliases = tolist([
    "eventcatalog-projectname.rob.informediq-infra.com",
    "eventcatalog.rob.informediq-infra.com",
  ])
  cf_domain_name = "d32pr*******z3r.cloudfront.net"
  s3_bucket = "informediq-rob-eventcatalog-origin"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of this info will be needed for the following steps to setup the Google SSO.&lt;/p&gt;

&lt;p&gt;At this point if you tried to access &lt;code&gt;https://eventcatalog.rob.informediq-infra.com&lt;/code&gt; you would get an error since the lambda@edge has the dummy code in it. This will be rectified in the following steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the Lambda@edge code with Widen/cloudfront-auth
&lt;/h2&gt;

&lt;p&gt;Clone the Widen/cloudfront-auth repo in a directory outside of your &lt;code&gt;my-catalog&lt;/code&gt; EventCatalog or terroform repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git clone git@github.com:Widen/cloudfront-auth.git
   &lt;span class="nb"&gt;cd &lt;/span&gt;cloudfront-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the instructions in the &lt;a href="https://github.com/Widen/cloudfront-auth/blob/master/README.md#identity-provider-guides" rel="noopener noreferrer"&gt;README&lt;/a&gt; for the Identity Provider of your choice. We are going to use the Google Hosted Domain mechanism:&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the OAuth Credentials in the &lt;a href="https://console.developers.google.com" rel="noopener noreferrer"&gt;Google developers console&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This assumes you don't already have a Project in the Google Developers Console but you have an account in the Google Developers Console.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a new Project
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click on the Projects pulldown on the very top menubar to the right of the &lt;code&gt;Google Cloud&lt;/code&gt; logo&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on &lt;code&gt;New project&lt;/code&gt; in the modal popup that shows after clicking the pulldown
&lt;img src="https://media.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%2Fa0rlfobzyogg5wmvik45.jpg" alt="Google Console New Project"&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Fill in the New Project Form and click on &lt;code&gt;CREATE&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://media.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%2Ff7fjeqrntkeolkow2czg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ff7fjeqrntkeolkow2czg.jpg" alt="Google Console Create Project"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;h4&gt;
  
  
  Create OAuth Consent and Credentials
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Select &lt;code&gt;APIs &amp;amp; Services&lt;/code&gt; from the menu bar on the left to go to that page of the project&lt;br&gt;&lt;br&gt;
&lt;a href="https://media.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%2Ffwbrnd06k29rsumf1vp2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffwbrnd06k29rsumf1vp2.jpg" alt="Google Console Select APIs and Services"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select &lt;code&gt;Credentials&lt;/code&gt; from the new menu bar on the left&lt;br&gt;
&lt;a href="https://media.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%2Fkqbhhahqekx0imslz46n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkqbhhahqekx0imslz46n.jpg" alt="Google Console Select Credentials"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on &lt;code&gt;Configure Consent Screen&lt;/code&gt; to configure the OAuth consent info&lt;br&gt;&lt;br&gt;
&lt;a href="https://media.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%2Ffb6o31xnj5qauld5qdwi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffb6o31xnj5qauld5qdwi.jpg" alt="Google Console Select Configure Consent Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;code&gt;Internal&lt;/code&gt; and then click on &lt;code&gt;CREATE&lt;/code&gt;
&lt;img src="https://media.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%2Fhdiet88vckekq3ysz0ez.jpg" alt="Google OAuth Consent Screen"&gt;
&lt;/li&gt;
&lt;li&gt;Fill in at least 

&lt;ul&gt;
&lt;li&gt;App Name (&lt;code&gt;EventCatalog Sandbox&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;User Support email 

&lt;ul&gt;
&lt;li&gt;This will be a pulldown and should have the email associated with the Google Dev account&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Authorized domains

&lt;ul&gt;
&lt;li&gt;This should be the domain used for the email address of people logging in via Google SSO.&lt;/li&gt;
&lt;li&gt;In my case this is &lt;code&gt;informed.iq&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Developer contact information email address

&lt;ul&gt;
&lt;li&gt;Can be your email
&lt;img src="https://media.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%2Fl0tq7c6qz0vh1if6iygi.jpg" alt="Google App Registration"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;SAVE AND CONTINUE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;SAVE AND CONTINUE&lt;/code&gt; on the next screen (&lt;code&gt;Scopes Page&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;BACK TO DASHBOARD&lt;/code&gt; on the next screen (&lt;code&gt;Summary Page&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Credentials&lt;/code&gt; on the left hand nav bar to get back to the Credentials page&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Click on &lt;code&gt;+ Create Credentials&lt;/code&gt; on the top menu bar and select &lt;code&gt;OAuth client ID&lt;/code&gt; from the pulldown&lt;br&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fiaqioc3rdjqwhavaf5la.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fiaqioc3rdjqwhavaf5la.jpg" alt="Google Console Create Credentials"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Web application&lt;/strong&gt; for the Application type&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Authorized redirect URIs&lt;/strong&gt;, enter your Cloudfront hostname with your preferred path value for the authorization callback. For our working example: &lt;code&gt;https://eventcatalog-projectname.rob.informediq-infra.com/_callback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;CREATE&lt;/code&gt; when done
&lt;img src="https://media.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%2Ftvjjo4hx553q9sg7savp.jpg" alt="Create OAuth Client ID"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Capture the resulting OAuth Client ID and Secret&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A modal window will show the OAuth Client ID and secret.&lt;/li&gt;
&lt;li&gt;You should store that somewhere, though you can also always view it on the Google Console later&lt;/li&gt;
&lt;li&gt;You can also download the JSON with the info and save it that way
&lt;img src="https://media.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%2Fklo78zpqpigli6s8avlw.jpg" alt="OAuth Credentials"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We're now done with the Google Developer's Console&lt;/p&gt;
&lt;h3&gt;
  
  
  Generate the code for Lambda@edge
&lt;/h3&gt;

&lt;p&gt;NOTE: Make sure you are in the &lt;code&gt;Widen/cloudfront-auth&lt;/code&gt; directory for the following commands&lt;/p&gt;

&lt;p&gt;Unfortunately, The &lt;code&gt;Widen/cloudfront-auth&lt;/code&gt; project has not seen any updates in a while. But it is still widely used.&lt;/p&gt;

&lt;p&gt;You can first run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npm audit fix &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To at least remove some blatant high risk vulnerabilities. It seens to not impact the actual use of the project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Execute &lt;code&gt;./build.sh&lt;/code&gt;. NPM will run to download dependencies and a RSA key will be generated.

&lt;ul&gt;
&lt;li&gt;There will be some messages about the npm install&lt;/li&gt;
&lt;li&gt;There is a &lt;code&gt;Warning&lt;/code&gt; that seems to  be filling in the value of the first prompt &lt;code&gt;&amp;gt;: Enter distribution name:&lt;/code&gt; you can ignore the warning and start filling in the values

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Distribution Name&lt;/code&gt; - The value of &lt;code&gt;cf_domain_name&lt;/code&gt; from the terraform run&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Authentication methods&lt;/code&gt; - 1 Google&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Client ID&lt;/code&gt; - The Client ID generated in the Google Console OAuth Credentials process&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Client Secret&lt;/code&gt; - The Client Secret generated in the Google Console OAuth Credentials process&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Redirect URI&lt;/code&gt; - The URL based on the domain name for the Cloudfront instance which was passed in the Google Console OAuth Credentials process&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Hosted Domain&lt;/code&gt; - The email address domainname that will be used by people logging in via Google SSO&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Session Duration&lt;/code&gt; - How many hours the session should last until the user needs to re-authenticate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Authorization methods&lt;/code&gt; - We are selecting &lt;code&gt;1&lt;/code&gt; for &lt;code&gt;Hosted Domain&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;NOTE: Redacting a few items for security&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;: Enter distribution name: d32pr*******z3r.cloudfront.net
&amp;gt;: Authentication methods:
    (1) Google
    (2) Microsoft
    (3) GitHub
    (4) OKTA
    (5) Auth0
    (6) Centrify
    (7) OKTA Native

    Select an authentication method: 1
Generating public/private rsa key pair.
Your identification has been saved in ./distributions/d32pr*******z3r.cloudfront.net/id_rsa
Your public key has been saved in ./distributions/d32pr*******z3r.cloudfront.net/id_rsa.pub
The key fingerprint is:
SHA256:vJS0/*************************************************iE2ic rberger@tardis.local
The key's randomart image is:
+---[RSA 4096]----+
|       .o. =. .==|
|         oo.+.=.+|
|        ooo .o.B.|
|       o.+E...= .|
|        S .o   o.|
|       . +... + o|
|        o o+.o + |
|         . ==..  |
|          +=+o.  |
+----[SHA256]-----+
writing RSA key
&amp;gt;&amp;gt;: Client ID: 787***********************13cho.apps.googleusercontent.com
&amp;gt;&amp;gt;: Client Secret: GOCSPX-****************untA
&amp;gt;&amp;gt;: Redirect URI: https://eventcatalog-projectname.rob.informediq-infra.com/_callback
&amp;gt;&amp;gt;: Hosted Domain: informed.iq
&amp;gt;&amp;gt;: Session Duration (hours):  (0)  12
&amp;gt;&amp;gt;: Authorization methods:
   (1) Hosted Domain - verify email's domain matches that of the given hosted domain
   (2) HTTP Email Lookup - verify email exists in JSON array located at given HTTP endpoint
   (3) Google Groups Lookup - verify email exists in one of given Google Groups

   Select an authorization method: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Copy the resulting &lt;code&gt;zip&lt;/code&gt; file found in the distribution folder in the Widen/cloudfront-auth directory to the &lt;code&gt;assets&lt;/code&gt; directory in the terraform directory

&lt;ul&gt;
&lt;li&gt;The process will output the path that the zip file was saved as relative to.&lt;/li&gt;
&lt;li&gt;In my setup the command to do the copy is:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;cp &lt;/span&gt;distributions/d32pr&lt;span class="k"&gt;*******&lt;/span&gt;z3r.cloudfront.net/d32pr&lt;span class="k"&gt;*******&lt;/span&gt;z3r.cloudfront.net.zip ../my-catalog/terraform/assets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy the EventCatalog content to S3
&lt;/h2&gt;

&lt;p&gt;You can deploy the content manually. But you really should use a CI/CD systems to deploy the EventCatalog content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual deployment
&lt;/h3&gt;

&lt;p&gt;The key actions needed are to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change directory to be in the top of the EventCatalog repo&lt;/li&gt;
&lt;li&gt;Build the static assets using the EventCatalog cli&lt;/li&gt;
&lt;li&gt;Copy the static assets to the S3 bucket created by Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First we'll show doing it manually&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Build the static assets &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assumes you are in the top level of the EventCatalog Repo&lt;/li&gt;
&lt;li&gt;You only need to do &lt;code&gt;yarn install&lt;/code&gt; the first time you use any of the commands
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nb"&gt;install
&lt;/span&gt;yarn build
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upload the static assets to S3&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Assumes you have installed the AWS CLI 
* You have configured you local shell environment with proper IAM Profile to run the AWS CLI
* Use the actual s3 bucket you created in your terraform run
    * The example shows the bucket we've used in our working example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws s3 &lt;span class="nb"&gt;sync&lt;/span&gt; .eventcatalog-core/out s3://informediq-rob-eventcatalog-origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment with CircleCi
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Assumes you have a &lt;a href="https://circleci.com/integrations/github/" rel="noopener noreferrer"&gt;CircleCI account and have it hooked up to your Github account&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is beyond the scope of this article to show how to setup and use Github and CircleCI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You will need to set &lt;a href="https://circleci.com/docs/2.0/env-vars" rel="noopener noreferrer"&gt;CircleCi Project or Context environment variables&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_Region&lt;/code&gt; (Needs to be &lt;code&gt;us-east-1&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create the &lt;code&gt;.circleci&lt;/code&gt; directory at the top of your EventCatalog repo directory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a file &lt;code&gt;.circleci/config.yml&lt;/code&gt; with the following content&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will need to substitue the s3 bucket name with the one you actually created with terraform
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.1&lt;/span&gt;

&lt;span class="c1"&gt;# CircleCi Orbs (libraries) used by this config&lt;/span&gt;
&lt;span class="na"&gt;orbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;circleci/node@5.0.2&lt;/span&gt;
  &lt;span class="na"&gt;aws-s3&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;circleci/aws-s3@3.0.0&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;eventcatalog-contentbuild&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cimg/node:16.15&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install EventCatalog tooling&lt;/span&gt;
          &lt;span class="na"&gt;working_directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/project&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;if [ ! -e "node_modules/@eventcatalog/core/bin/eventcatalog.js" ]; then yarn install; else echo "eventbridge seems to be cached"; fi;&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build the EventCatalog static content&lt;/span&gt;
          &lt;span class="na"&gt;working_directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/project&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;echo Running eventbridge build in `pwd`&lt;/span&gt;
            &lt;span class="s"&gt;yarn build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;aws-s3/sync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Copy the static content to the S3 bucket&lt;/span&gt;
          &lt;span class="c1"&gt;# Replace the s3 bucket name with the one you actually created with terraform&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION&lt;/span&gt;
          &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/project/.eventcatalog-core/out&lt;/span&gt;
          &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3://informediq-rob-blogpost-eventcatalog-origin&lt;/span&gt;

&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;eventcatalog-contentworkflow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;eventcatalog-contentbuild&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# We're getting the AWS Credentials from our CircleCI Organization context&lt;/span&gt;
            &lt;span class="c1"&gt;# You could also just use Project level Environment Variables with&lt;/span&gt;
            &lt;span class="c1"&gt;# IAM AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rberger-aws-user-creds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have created this file and have all your commits in your EventCatalog Repo, push it to Github which should trigger your CircleCI run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can confirm that it sent it to s3 by using the AWS Console or CLI to view the contents of the S3 bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy the new lambda@edge code with terraform
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go back to your terraform directory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure the new zip file is in the &lt;code&gt;assets&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the tfvars input file (&lt;code&gt;sandbox.tfvars&lt;/code&gt; in our working example) with the new filename&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lambda_file_name = "assets/d32pr*******z3r.cloudfront.net.zip"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;region           = "us-east-1"
profile          = "sandbox"
environment      = "rob"
project_name     = "blogpost"
app_name         = "eventcatalog"
lambda_file_name = "assets/d32pr*******z3r.cloudfront.net.zip"
## On first run, set lambda_file_name to `assets/temp.zip`
# lambda_file_name = "assets/temp.zip"

##
## These should be different for your environment
base_domain_name = "informediq-infra.com"
bucket_namespace = "informediq"
zone_id          = "Z10***********************K7U"
##

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

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;terraform apply&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sandbox.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A successful run will display the output values&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* They should be something along the lines of the following:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
Outputs:

cf_aliases = tolist([
  "eventcatalog-blogpost.rob.informediq-infra.com",
  "eventcatalog.rob.informediq-infra.com",
])
cf_domain_name = "d32pr*******z3r.cloudfront.net"
s3_bucket = "informediq-rob-eventcatalog-origin"
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to go to ether of your cf_aliases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For instance:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   https://eventcatalog.rob.informediq-infra.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If you aren't already logged in, it should pass you to Google SSO authentication.&lt;/li&gt;
&lt;li&gt;Once you are logged in you should see the Home Page of the EventCatalog
&lt;img src="https://media.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%2F6fv8ozgc49y03p768oz9.jpg" alt="EventCatalog Home Page"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now start using the EventCatalog by updating the source files to fit your Domains, Services, and Events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements? Suggestions? Alternatives?
&lt;/h2&gt;

&lt;p&gt;Please feel free to comment or contact me if you find any bugs, issues or have suggestions for improvements!&lt;/p&gt;

&lt;p&gt;I am interested in hearing about alternatives to the &lt;a href="https://github.com/Widen/cloudfront-auth" rel="noopener noreferrer"&gt;Widen/cloudfront-auth&lt;/a&gt; as it has not been updated in a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;As the Chief Architect, Rob guides the evolution of the InformedIQ Software and Infrastructure. His experience spans the rise and fall of many technology lifecycles from machine vision, digitization of professional video production equipment, Internet Infrastructure, Wireless, E-commerce, Big Data, IoT, DevOps and Machine Learning. He has been a founder or a technical leader in several startups in Silicon Valley. Proud to be part of the AWS Community Builders for the second year.&lt;/p&gt;

&lt;p&gt;We're Hiring! &lt;a href="https://informed.iq/careers/" rel="noopener noreferrer"&gt;https://informed.iq/careers/&lt;/a&gt;&lt;br&gt;
Twitter: &lt;a class="mentioned-user" href="https://dev.to/rberger"&gt;@rberger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article was published originally in the &lt;a href="https://informed.iq/deploy-eventcatalog-to-aws-cloudfront-with-google-sso-access-control-via-terraform/" rel="noopener noreferrer"&gt;InformedIQ Blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>eventdriven</category>
      <category>aws</category>
      <category>api</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Connect Quicksight to RDS in a private VPC</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Thu, 27 Jan 2022 08:45:26 +0000</pubDate>
      <link>https://dev.to/aws-builders/connect-quicksight-to-rds-in-a-private-vpc-4nl9</link>
      <guid>https://dev.to/aws-builders/connect-quicksight-to-rds-in-a-private-vpc-4nl9</guid>
      <description>&lt;p&gt;There are cases where you want to connect &lt;a href="https://aws.amazon.com/quicksight/" rel="noopener noreferrer"&gt;AWS Quicksight&lt;/a&gt; to pull data from an RDS Database in one of your Private VPCs. Its one of those things that you don’t do often and its just funky enough and different enough from most AWS services that I have had to relearn how to do it each time. So here’s what I’ve learnt for posterity.&lt;/p&gt;

&lt;p&gt;Quicksight can automatically connect to databases that can be accessed via a public IP. If your DB is publicly accessible to the Internet (with Security Group filtering of course), then you can pretty much ignore this article.&lt;/p&gt;

&lt;p&gt;If you happen to have the weird case where your DB does have a public IP address but it is not actually accessible to the public Internet for policy, technical or historical reasons, then read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quicksight &lt;code&gt;VPC Connection&lt;/code&gt; Requirements
&lt;/h2&gt;

&lt;p&gt;Quicksight has the option of creating a connection between you instance of Quicksight and one of your VPCs. It does that by injecting a Network Interface into a subnet you specify from the target VPC.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmvhidjxo66afrogvw3ps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmvhidjxo66afrogvw3ps.png" alt="Networking of Quicksight and VPC"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You only have to supply the&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VPC ID&lt;/code&gt; that your target DB is in.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Subnet&lt;/code&gt; that is routable to the subnet your target DB is in&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Security Group&lt;/code&gt; dedicated to the Quicksight connection that will allow all TCP taffic to the target DB&lt;/li&gt;
&lt;li&gt;Optionally a &lt;code&gt;DNS Inbound Endpoint&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're going to assume that the target DB and its VPC already exists.&lt;/p&gt;

&lt;p&gt;You can use an existing &lt;code&gt;Subnet&lt;/code&gt; as long as its in the same VPC and is routable to the subnets used by the target DB. By default subnets in a VPC can route to any other subnet in the VPC, but you should double check.&lt;/p&gt;

&lt;p&gt;When you create the &lt;code&gt;VPC Connection&lt;/code&gt; in the Quicksight management console, it will automatically create a &lt;code&gt;Network Interface&lt;/code&gt; on the specified &lt;code&gt;Subnet&lt;/code&gt; and will be associated with the &lt;code&gt;Security Group&lt;/code&gt; specified.&lt;/p&gt;

&lt;p&gt;Note that the &lt;code&gt;Security Group&lt;/code&gt; associated with this new Quicksight &lt;code&gt;Network Interface&lt;/code&gt; will be stateless. Any response packets coming back from a Quicksight request will have randomly allocated port numbers. Normally Security groups are stateful and handle this for you. But in the case of the Quicksight Network Interface you have to explicitly enable that any port is allowed for inbound.&lt;/p&gt;

&lt;p&gt;The optional &lt;code&gt;DNS Inbound Endpoint&lt;/code&gt; allows you to tell Quicksight to use the private DNS Resolver for your VPC instead of querying just the Public DNS zones. This is what is needed if your target DB has a Public IP address. Without this setting this Quicksight will get the Public IP address when it queries the &lt;code&gt;Endpoint name&lt;/code&gt; of your DB. You will be scratching your head for days wondering why the connection is not working.&lt;/p&gt;

&lt;p&gt;If you do use &lt;code&gt;DNS Inbound Endpoint&lt;/code&gt; option, you will have to set it up in &lt;code&gt;Route53&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Detailed instructions for all of this are described below.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;VPC Connection&lt;/code&gt; will allow Quicksight to connect to any of the following in your VPC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon OpenSearch Service&lt;/li&gt;
&lt;li&gt;Amazon Redshift&lt;/li&gt;
&lt;li&gt;Amazon Relational Database Service&lt;/li&gt;
&lt;li&gt;Amazon Aurora&lt;/li&gt;
&lt;li&gt;MariaDB&lt;/li&gt;
&lt;li&gt;Microsoft SQL Server&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Oracle&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Presto&lt;/li&gt;
&lt;li&gt;Snowflake&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can reuse a &lt;code&gt;VPC Connection&lt;/code&gt; for any Datasource in your Quicksight account in a region.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subnet Info
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Get VPC Info
&lt;/h3&gt;

&lt;p&gt;We'll need the &lt;code&gt;VPC ID&lt;/code&gt; and the &lt;code&gt;CIDR Block&lt;/code&gt; associated&lt;/p&gt;

&lt;p&gt;You can look at your RDS Configuration to see what VPC it is in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F389txmajmtco7wenq1k4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F389txmajmtco7wenq1k4.png" alt="RDS Configuraiton showing VPC"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VPC ID&lt;/code&gt; ends in &lt;code&gt;2aed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CIDR Block&lt;/code&gt;: 10.0.0.0/16&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8bkb70fxmi7p3811a3x0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8bkb70fxmi7p3811a3x0.png" alt="VPC Console view of the VPC of interest"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pick a Subnet for the Quicksight Network Interface to Use
&lt;/h3&gt;

&lt;p&gt;The criteria are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the same VPC as the target DB&lt;/li&gt;
&lt;li&gt;Is a private subnet&lt;/li&gt;
&lt;li&gt;In the same Availability Zone as at least one of the subnets associated with the target DB&lt;/li&gt;
&lt;li&gt;Routable to that subnet in the target DB

&lt;ul&gt;
&lt;li&gt;Has a route table that routes to the VPC CIDR Block&lt;/li&gt;
&lt;li&gt;And the Target DB Subnets also can route to the VPC CIDR Block&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Doesnt have an ACL that would block acces to/from the target DB

&lt;ul&gt;
&lt;li&gt;This is the usual case&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Find the subnets used on the target DB
&lt;/h4&gt;

&lt;p&gt;In this example our target DB is an Aurora Postgres cluster. Looking at the RDS Console we can find the subnets its usings&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Click on one of the subnets to view the subnet info&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdloqutmsnd155387rko8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdloqutmsnd155387rko8.png" alt="RDS Console with subnet info"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See what &lt;code&gt;Availability Zone&lt;/code&gt; its in (us-east-1b in this example)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fohn6jyp4mikhvj1w28xs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fohn6jyp4mikhvj1w28xs.png" alt="RDS Subnet info"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confirm it routes to the VPC CIDR Block (10.0.0.0/16 in this example)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fhu64ahlot0mjg46hvm66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fhu64ahlot0mjg46hvm66.png" alt="RDS Console showing Availability Zon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the subnet view in the VPC Console.&lt;/li&gt;
&lt;li&gt;Find an existing subnet that also routes to the same VPC CIDR Block (or overlapping subset with the DB subnet and its on the same &lt;code&gt;Availability Zone&lt;/code&gt;)

&lt;ul&gt;
&lt;li&gt;You could also create a new subnet for this as long as it meets the same criteria&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;In our example its the subnet that ends with &lt;code&gt;90dc&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fvqgl85w7yg82ak2mdyld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fvqgl85w7yg82ak2mdyld.png" alt="Existing Subnet suitable for Quicksight"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Group
&lt;/h2&gt;

&lt;p&gt;Create a new &lt;code&gt;Security Group&lt;/code&gt; dedicated to the Quicksight Network Interface. You don't &lt;em&gt;have&lt;/em&gt; to create one specific to this, but it will make management easier than trying to mix it in with your existing Security Groups.&lt;/p&gt;

&lt;p&gt;We'll call it &lt;code&gt;Amazon-QuickSight-access&lt;/code&gt;. Nothing magic about the name though, whatever fits into your naming scheme.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inbound Rules
&lt;/h3&gt;

&lt;p&gt;Set the &lt;code&gt;Inbound Rules&lt;/code&gt; to allow trafic on all TCP ports. As mentioned earlier, this is because this will be a stateless security group and all response packets will have random inbound ports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outbound Rules
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Outbound Rules&lt;/code&gt; should limit the destinations to just your target DB. The easiest way is to set the destination to be the Security Group set in your RDS Database.&lt;/p&gt;

&lt;p&gt;You should also limit the outbound ports to be ones appropriate for your target DB, such as port 5432 for Postgres.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F1n0rc3h9q4kf6zagkveb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F1n0rc3h9q4kf6zagkveb.png" alt="Outbound Rules with security group destination"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We ran into a problem where for historical reasons, there were some existing inbound rules in the Target DB that prevented us from using the Target DB as the destination security group, so we used a CIDR range that covered the Target DB range of addresses. This should be an unusual situation and you can probably ignore it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Feua3ziesd5yofp7axccy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Feua3ziesd5yofp7axccy.png" alt="Outbound Rules wiht security group destinationn"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  DNS Resolver Endpoints (optional)
&lt;/h2&gt;

&lt;p&gt;You only need to fill this in for cases where the DNS lookup of your Target DB &lt;code&gt;Endpoint&lt;/code&gt; would be incorrect against Public DNS. The usual use case for this is when you have a somewhat complicated VPC Peering setup where your Target DB is on the other side of a VPC Peering setup. In that case, only the DNS Resolver in your private VPC may know the proper resolution of the Target DB Endpoint.&lt;/p&gt;

&lt;p&gt;In our case, we had the unusal situation that our Target DB had a public IP address, so when Quicksight would do a DNS Query on the Target DB &lt;code&gt;Endpoint&lt;/code&gt; name, it would get the Public IP address which was not valid for the &lt;code&gt;VPC Connection&lt;/code&gt;. The workaround is for Quicksight to use the local VPC DNS Resolver. And thus our need to setup the &lt;code&gt;DNS Resovler Endpoints&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It took a while to figure out this was why we could never get the &lt;code&gt;VPC Connection&lt;/code&gt; to work until we set this up. The diagnostics of the &lt;code&gt;VPC Connection&lt;/code&gt; Validation check does not differentiate between Networking, DNS, or username/password problems. An issue in anyone of those can make the connection validation fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Route53 Resolver Inbound Endpoints
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Create a Security Group for the Resolver
&lt;/h4&gt;

&lt;p&gt;The resolver needs to have a security group for itself to allow the DNS requests to get to it.&lt;/p&gt;

&lt;h5&gt;
  
  
  Inbound Rules
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Create a new Security Group and call it something like &lt;code&gt;quicksight-route53-resolver&lt;/code&gt; or whatever fits your nameing scheme.&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;Inbound Rules&lt;/code&gt; to allow for DNS UDP and DNS TCP from all sources on the VPC CIDR Block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fazmh10xr9w77zhm49epo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fazmh10xr9w77zhm49epo.png" alt="Resolver Security Group Inbound Rules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Outbound Rules
&lt;/h5&gt;

&lt;p&gt;Can leave the default outbound to all rule&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup the Route53 Endpoint Resolver
&lt;/h4&gt;

&lt;p&gt;You will need to go to the Route53 Console and select &lt;code&gt;Resolver-&amp;gt;Inbound endpoints&lt;/code&gt; and click on the &lt;code&gt;Create Inbound Endpoint&lt;/code&gt; button.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the &lt;code&gt;Endpoint name&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Something that fits your naming scheme&lt;/li&gt;
&lt;li&gt;Our example is &lt;code&gt;quicksight-prod&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Set the VPC ID to be the same as your &lt;code&gt;VPC-ID&lt;/code&gt; used for your Target DB&lt;/li&gt;

&lt;li&gt;Set the Security Group to the Security Group you created&lt;/li&gt;

&lt;li&gt;Set the Availability Zone / Subnet for two IP Addresses for the resolver

&lt;ul&gt;
&lt;li&gt;Could be any that are routable to the Subnet that is assigned to the &lt;code&gt;VPC Connection&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Should be a private subnet&lt;/li&gt;
&lt;li&gt;Might as well make one of them the same Subnet used by the &lt;code&gt;VPC Connection&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check the option &lt;code&gt;Use an IP address that is selected automatically&lt;/code&gt; for both&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Submit&lt;/code&gt; when done&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5s77wseanpvff3mee0nb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5s77wseanpvff3mee0nb.png" alt="Route53 Create Resolver Inbound Endpoint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will then end up with an &lt;code&gt;Inbound Endpoint&lt;/code&gt; that will have been assigned two IP addresses. These addresses will be needed to supply to the &lt;code&gt;VPC Connection&lt;/code&gt; and be used to update the Quicksight Security Group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frcn0dehx6bvvs2u6zuqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frcn0dehx6bvvs2u6zuqr.png" alt="Route53 Quicksight Inbound Resolvern"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example the two IP Addresses are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10.0.100.120&lt;/li&gt;
&lt;li&gt;10.0.101.80&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Update the Quicksight Security Group for DNS
&lt;/h4&gt;

&lt;p&gt;If you are using the DNS Resolver Inbound Endpoints feature, you will also have to update the &lt;code&gt;Outbound Rules&lt;/code&gt; of the Security Group we created earlier for the &lt;code&gt;Quicksight Network Interface&lt;/code&gt;. This is to enable Quicksight to be able to access the DNS Resolver as well as the Target DB.&lt;/p&gt;

&lt;p&gt;To do this we will add DNS UDP and DNS TCP to the &lt;code&gt;Output Rules&lt;/code&gt; of the &lt;code&gt;Amazon-QuickSight-access&lt;/code&gt; Security Group for each of the two IP Addresses from the &lt;code&gt;Inbound Resolver&lt;/code&gt; we just created. Note that you need to have the CIDR suffix &lt;code&gt;/32&lt;/code&gt; at the end when entering them into the Security Group editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7oe9c209nq2be9mztdoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7oe9c209nq2be9mztdoe.png" alt="Quicksight Security Group with DNS Resolver IPs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Actual VPC Connection
&lt;/h2&gt;

&lt;p&gt;Now we have everything we need to setup the actual &lt;code&gt;VPC Connection&lt;/code&gt; in the Quicksight management console.&lt;/p&gt;

&lt;p&gt;You will of course need to have proper permissions to access and manage Quicksight in your account. That is beyond the scope of this article. We're going to assume you have all that already.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter the Quicksight Console, click on your username on the topr right of the page and select &lt;code&gt;Manage Quicksight&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fltwwv6hbsbp9mak5p9ff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fltwwv6hbsbp9mak5p9ff.png" alt="Home page select Manage Quicksight"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Then select &lt;code&gt;Manage VPC Connections&lt;/code&gt; in the left hand Navbar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fp1h4hrud6rip1w3j79xf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fp1h4hrud6rip1w3j79xf.png" alt="Select Manage VPC Connection from Navbar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Add VPC Connection&lt;/code&gt; to create the new connection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fc8whb4vzjhjpdskaaknl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fc8whb4vzjhjpdskaaknl.png" alt="Add VPC Connection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the form with the info we found or created earlier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VPC Connection Name&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Appropriate name of the connection based on your naming conventions&lt;/li&gt;
&lt;li&gt;Our example: &lt;code&gt;my-aurora-db&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;VPC ID&lt;/code&gt;: The &lt;code&gt;VPC ID&lt;/code&gt; we have been using earlier

&lt;ul&gt;
&lt;li&gt;Our example ends with &lt;code&gt;2aed&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Subnet ID&lt;/code&gt;: The Subnet we chose for the Quicksight &lt;code&gt;Network Interface&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;In our example it ends with &lt;code&gt;90dc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Security Group ID&lt;/code&gt;: The Security Group we created for Quicksight

&lt;ul&gt;
&lt;li&gt;Our example: &lt;code&gt;Amazon-QuickSight-access&lt;/code&gt; (ended in &lt;code&gt;16e8&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;DNS Resolver Endpoints&lt;/code&gt;: The IP addresses from the DNS Resolver &lt;code&gt;Inbound Endpoints&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Our example: &lt;code&gt;10.0.100.120&lt;/code&gt; and &lt;code&gt;10.0.101.80&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fktylsm4rldf45a046dhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fktylsm4rldf45a046dhu.png" alt="VPC Connection Form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Dataset using the VPC Connection
&lt;/h2&gt;

&lt;p&gt;Now that the &lt;code&gt;VPC Connection&lt;/code&gt; has been setup, we can use it to create a Dataset from the Target DB.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on the &lt;code&gt;Quicksight&lt;/code&gt; logo on the top left of the screen to get back to the Quicksight home page.&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Datasets&lt;/code&gt; at the bottom of the left Navbar&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;New dataset&lt;/code&gt; on the top right of the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fz2s3hm5k2lafo39ggka3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fz2s3hm5k2lafo39ggka3.png" alt="Getting to the Dataset create page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Aurora&lt;/code&gt; (or other source, but we're not going to show other sources in this article)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fill in the &lt;code&gt;New Aurora data source&lt;/code&gt; form
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Data source name&lt;/code&gt;: Our example is &lt;code&gt;my-data-source&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Connection type&lt;/code&gt;: Select the VPC connection we created

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;my-aurora-db&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fp561n5y84cl68qe1o75w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fp561n5y84cl68qe1o75w.png" alt="Select VPC Connection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Database connector&lt;/code&gt;: &lt;code&gt;PostgreSQL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Database server&lt;/code&gt;: The &lt;code&gt;Endpoint&lt;/code&gt; of your Aurora DB&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is the fully qualified DNS name of your DB endpoint&lt;/li&gt;
&lt;li&gt;You can find it in the &lt;code&gt;Connectivity &amp;amp; security&lt;/code&gt; tab on the DB's RDS Console page&lt;/li&gt;
&lt;li&gt;You probably want to use a reader endpoint&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;Port&lt;/code&gt;: The proper port for your Target DB&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Postgres default is &lt;code&gt;5432&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;Database Name&lt;/code&gt;: The name of the database within the RDS of interest&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same name you would use in a DB connection or in psql to connect to your working DB's&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Username&lt;/code&gt;: The db username needed to connect&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Password&lt;/code&gt;: The db password&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Click on &lt;code&gt;Validate Connection&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It should turn to &lt;code&gt;Validated&lt;/code&gt; with a checkmark if all went well. Should happen within a few seconds.&lt;/p&gt;

&lt;p&gt;At this point you can now click on the &lt;code&gt;Create data source&lt;/code&gt; button and do the normal Quicksight data source stuff. That is all independent of the VPC Connection and is not part of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  If the VPC Connection fails to Validate
&lt;/h2&gt;

&lt;p&gt;If the Validate failed you are going to have to check several things. There will be an error message. You can click on the &lt;code&gt;details&lt;/code&gt; link, but its probably not going to be helpful.&lt;/p&gt;

&lt;p&gt;The error diagnosicts for the VPC Connection rarely gives you any more info other than it could not connect to the DB.&lt;/p&gt;

&lt;p&gt;You need to determine if its because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The routing from the Quicksight Network Interface to the Target DB&lt;/li&gt;
&lt;li&gt;The Security Group settings&lt;/li&gt;
&lt;li&gt;Basic error in the regular connection parameters (&lt;code&gt;Database name&lt;/code&gt;, &lt;code&gt;Username&lt;/code&gt;, &lt;code&gt;Password&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Database Server&lt;/code&gt; is the correct value and Quicksight DNS query is getting the right value (private IPs not public or nothing at all)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Check for basic connectivity
&lt;/h3&gt;

&lt;p&gt;You can check the basic connectivity (routing and security groups) is working using the &lt;code&gt;Reachability Analyzer&lt;/code&gt; in the VPC Console. Unfortunately the analyzer has a limited set of elements that can be specified as a Source and Destination. The only one that applies to the Quicksight VPC Connection as a source and Aurora RDS as a Destination are &lt;code&gt;Network Interfaces&lt;/code&gt;. So we're going to need to find the IDs of those two Network Interfaces.&lt;/p&gt;

&lt;h4&gt;
  
  
  Find the ID of the VPC Connection Network Interface
&lt;/h4&gt;

&lt;p&gt;You will need to know the &lt;code&gt;Network Interface&lt;/code&gt; ID of the interface created for the VPC Connection. To figure that out go to the EC2 Console page and click on &lt;code&gt;Network Interfaces&lt;/code&gt; under &lt;code&gt;Network &amp;amp; Security&lt;/code&gt; in the Navbar on the left&lt;/p&gt;

&lt;p&gt;Then search for the name you used for the Quicksight connection. Our example was &lt;code&gt;my-aurora-db&lt;/code&gt; It will be part of the description of the &lt;code&gt;Network Interface&lt;/code&gt; associated with that connection. In our example it starts with &lt;code&gt;eni-0b9e&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftdipd0pe4gs85p18ptma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftdipd0pe4gs85p18ptma.png" alt="Search for Network Interface"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Find the ID of the Target DB Network Interface
&lt;/h4&gt;

&lt;p&gt;You will need to know any of the &lt;code&gt;Network Interface&lt;/code&gt; IDs of the Target DB. There can be a few as there may be one per Availability Zone. It doesn't matter which one you choose.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Still on the EC2 Console page &lt;code&gt;Network Interfaces&lt;/code&gt; page, search for the Target DB's Security Group name.

&lt;ul&gt;
&lt;li&gt;You can find the Target DB Security Group name on the RDS Console page for your Target DB under the &lt;code&gt;Connectivty &amp;amp; Security&lt;/code&gt; tab labeled &lt;code&gt;VPC security groups&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Select any one of them.

&lt;ul&gt;
&lt;li&gt;Our example starts with: &lt;code&gt;eni-050d&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Run the Reachability Analyzer
&lt;/h3&gt;

&lt;p&gt;Go to the VPC Console and click on the &lt;code&gt;Create and analyze path&lt;/code&gt; button on the top right of the page&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fssr8m0x267j7r09xvj66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fssr8m0x267j7r09xvj66.png" alt="Rechability Analyzer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give it a name&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Network Interfaces&lt;/code&gt; for the &lt;code&gt;Source type&lt;/code&gt; and &lt;code&gt;Destination type&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Specify the &lt;code&gt;Network Interface ID&lt;/code&gt; of the Quicksight &lt;code&gt;Network Interface&lt;/code&gt; we found

&lt;ul&gt;
&lt;li&gt;Starts with &lt;code&gt;eni-0b9e&lt;/code&gt; in our example&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Specify the &lt;code&gt;Network Interface ID&lt;/code&gt; of the Target DB &lt;code&gt;Network Interface&lt;/code&gt; we found

&lt;ul&gt;
&lt;li&gt;Starts with &lt;code&gt;eni-050d&lt;/code&gt; in our example&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Specify &lt;code&gt;5432&lt;/code&gt; for the &lt;code&gt;Destination port&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Or whatever port you set for your Target DB if not Postgres&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Protcol is &lt;code&gt;TCP&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fbf9wgaunvjnkodbia3rj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fbf9wgaunvjnkodbia3rj.png" alt="Start Create and Analyze"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Create and analyze path&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If it all works you should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fm2d9r61x8ntf9zw5fdd0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fm2d9r61x8ntf9zw5fdd0.png" alt="Analyze Success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If that works, you configured the DNS Endpoint Resolver, but your VPC Connection / Dataset creation still doesn't work, you may want to repeat the &lt;code&gt;Reachability Analyzer&lt;/code&gt; test for the DNS TCP and UDP ports in addition to the Postgres Port to double check for the DNS passing properly between the resolver and Quicksight.&lt;/p&gt;

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

&lt;p&gt;If the &lt;code&gt;Reachability Analyzer&lt;/code&gt; said connectivity is ok and it still doesn't work, then its probable that one of the other basic connection parameters is wrong, or there is something wrong with the &lt;code&gt;Endpoint&lt;/code&gt; name. If you hadn't tried setting up the DNS Endpoint Resolver option, you can try that to see if there was a problem with how Quicksight was resolving the DNS for your &lt;code&gt;Endpoint&lt;/code&gt;. That was what started this whole journey for me.&lt;/p&gt;

&lt;p&gt;Otherwise, hopefully this did work for you and you can now happly view your Target DB in Quicksight!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>awscommunity</category>
      <category>quicksight</category>
    </item>
    <item>
      <title>How to Use Amplify Studio Figma Connector with Clojurescript</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Sun, 09 Jan 2022 21:56:44 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-use-amplify-studio-figma-connector-with-clojurescript-382d</link>
      <guid>https://dev.to/aws-builders/how-to-use-amplify-studio-figma-connector-with-clojurescript-382d</guid>
      <description>&lt;h2&gt;
  
  
  Amplify Studio / Figma / Clojurescript / Reagent Tutorial
&lt;/h2&gt;

&lt;p&gt;Implements the AWS Tutorial &lt;a href="https://welearncode.com/studio-vacation-site/" rel="noopener noreferrer"&gt;Build a Vacation Rental Site with Amplify Studio&lt;/a&gt; but instead of being Javascript based, uses Clojurescript for the project implementation. It does incorporate the Javascript output of Amplify Studio but all code to use it is in Clojurescript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tooling Used in the project:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/AutoScreencast/create-reagent-app" rel="noopener noreferrer"&gt;Create Reagent App&lt;/a&gt; to create the project scaffold&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://clojurescript.org" rel="noopener noreferrer"&gt;Clojurescript&lt;/a&gt; (The whole point of this article!)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://shadow-cljs.org/" rel="noopener noreferrer"&gt;Shadow-CLJS&lt;/a&gt; as the build tool / compiler&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webpack.js.org" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; Key to preping JSX and JS Files from Amplify Studio and UI Components to be used with shadlow-cljs transpiled clojurescript.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://babeljs.io" rel="noopener noreferrer"&gt;Babel&lt;/a&gt; Used by webpack to convert JSX to JS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/reagent-project/reagent" rel="noopener noreferrer"&gt;Reagent&lt;/a&gt; (CLJS wrapper around &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;) for building your user interface&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/amplify/studio/" rel="noopener noreferrer"&gt;Amplify Studio&lt;/a&gt; and all the related &lt;a href="https://aws.amazon.com/amplify/" rel="noopener noreferrer"&gt;AWS Amplify tooling&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.figma.com/community/file/1047600760128127424" rel="noopener noreferrer"&gt;Figma AWS Amplify UI Kit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup an Amplify Studio Project
&lt;/h3&gt;

&lt;p&gt;All the initial setup of the Amplify Studio Project on AWS and the associated Figma project is already described in the first part of the excellent &lt;a href="https://welearncode.com/studio-vacation-site/" rel="noopener noreferrer"&gt;Build a Vacation Rental Site with Amplify Studio&lt;/a&gt; so will not repeat it here.&lt;/p&gt;

&lt;p&gt;That first part of the article will have you do all the following in the appropriate Web Consoles and services AWS and Figma). You won't be doing any CLI commands on your local dev computer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup an Amplify Studio project via the Amplify Sandbox&lt;/li&gt;
&lt;li&gt;Create a basic data model in Amplify Studio&lt;/li&gt;
&lt;li&gt;Deploy the start of the Amplify project to AWS&lt;/li&gt;
&lt;li&gt;Create some sample data&lt;/li&gt;
&lt;li&gt;Set up a Figma project using the Amplify UI Components and shows you how to modify it&lt;/li&gt;
&lt;li&gt;Import the modified Figma project into the Amplify project&lt;/li&gt;
&lt;li&gt;Link the data model and the UI Component in Amplify Studio&lt;/li&gt;
&lt;li&gt;Create a collection view using Amplify Studio&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Start by following the instructions from the original article, &lt;a href="https://welearncode.com/studio-vacation-site/" rel="noopener noreferrer"&gt;Build a Vacation Rental Site with Amplify Studio&lt;/a&gt;, up thru to the section: &lt;code&gt;Pull to Studio&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Once you have completed that, come back to here and follow the rest of this post at this point.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating an Amplify Studio App with Clojurescript
&lt;/h2&gt;

&lt;p&gt;This is the actual instructions on how to create your Amplify Studio app in Clojurescript instead of Javascript. It replaces the remainder all the content after &lt;code&gt;Pull to Studio&lt;/code&gt; from the original article &lt;a href="https://welearncode.com/studio-vacation-site/" rel="noopener noreferrer"&gt;Build a Vacation Rental Site with Amplify Studio&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a git repo with shadow-cljs / reagent scaffolding
&lt;/h3&gt;

&lt;p&gt;Instead of using &lt;code&gt;create-react-app&lt;/code&gt; that would have created a Javascript/React app, we’re going to use &lt;a href="https://www.npmjs.com/package/create-reagent-app" rel="noopener noreferrer"&gt;create-reagent-app&lt;/a&gt; to create the scaffolding of a shadow-cljs / reagent / react app repo.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will make this a git repo and snapshot the state at every stage so that if you make a mistake you can go back to an earlier step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-reagent-app  amplifystudio-cljs-tutorial
&lt;span class="nb"&gt;cd &lt;/span&gt;amplifystudio-cljs-tutorial
git init
git add &lt;span class="nt"&gt;-A&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial Commit after create-reagent-app"&lt;/span&gt;
npm-install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Add webpack and related dependencies
&lt;/h3&gt;

&lt;p&gt;Shadow-cljs can not directly consume JSX files that are the output of the Figma plugin and it needs some help to incorporate the AWS UI Components files that Amplify Studio injects into the project.&lt;/p&gt;

&lt;p&gt;The use of Babel to prepare JSX files for Shadow-cljs is based on info from &lt;a href="https://shadow-cljs.github.io/docs/UsersGuide.html#_javascript_dialects" rel="noopener noreferrer"&gt;Shadow CLJS User’s Guide - JavaScript Dialects&lt;/a&gt;. This tutorial moves the babel management into webpack configuration as described later on.&lt;/p&gt;

&lt;p&gt;The following dependencies are needed primarily to install webpack and its dependencies.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;html-webpack-plugin&lt;/code&gt; and &lt;code&gt;html-beautifier-webpack-plugin&lt;/code&gt; are used to inject the proper JS include for the output of webpack into the index.html.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @babel/cli @babel/core @babel/preset-react @babel/preset-env babel-loader html-webpack-plugin html-beautifier-webpack-plugin process webpack webpack-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then update any dependencies to the latest versions&lt;br&gt;
If you don’t already have it, install &lt;a href="https://www.npmjs.com/package/npm-check-updates" rel="noopener noreferrer"&gt;npm-check-updates&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g npm-check-updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And then run it to update any dependencies to the latest versions ignoring specified versions in the package.json.&lt;/p&gt;

&lt;p&gt;I like to start projects with the latest versions of everything. But you could just make sure &lt;code&gt;shadow-cljs&lt;/code&gt; is the latest version, best to stay latest with that.&lt;/p&gt;

&lt;p&gt;If you run it without the &lt;code&gt;-u&lt;/code&gt; it will just show you what it would update and you could manually update the ones you care about.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ncu &lt;span class="nt"&gt;-u&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Update your local git repo
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Snapshot after adding webpack dependencies"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you want, you could push it to your own remote Github or other repository&lt;/p&gt;
&lt;h3&gt;
  
  
  Add AWS Dependencies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AWS Account

&lt;ul&gt;
&lt;li&gt;If you don’t already have an AWS account, you’ll need to create one in order to follow the steps outlined in this tutorial. &lt;a href="https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start" rel="noopener noreferrer"&gt;Create an AWS Account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Amplify CLI
If you don’t already have the Amplify CLI installed you can install it with
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @aws-amplify/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Configure Account / IAM / CLI to work with Amplify
If you already have an AWS account you want to use and you have things setup in your workstation / Terminal to use AWS CLI via profiles in ~/.aws/credentials, you can just set your profile in your terminal for the profile to use
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your profile&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and you don’t need to do &lt;code&gt;amplify configure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you haven’t set up aws amplify on your local dev machine before, follow the instructions at &lt;a href="https://docs.amplify.aws/cli/start/install/#configure-the-amplify-cli" rel="noopener noreferrer"&gt;Configure the Amplify CLI&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Install the aws-amplify libraries in your project
&lt;/h3&gt;

&lt;p&gt;Still at the top of the &lt;code&gt;amplifystudio-cljs-tutorial&lt;/code&gt; repo, install the libraries&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i aws-amplify @aws-amplify/ui-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You might want to commit the changes to git just as a snapshot in case the next step messes anything up.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"After adding amplify deps"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Sync repo with Amplify project
&lt;/h3&gt;

&lt;p&gt;Using the amplify CLI, pull the project info and ui-components into your repo.&lt;/p&gt;

&lt;p&gt;You’ll get the command to do this from your Amplify Apps page that was created earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fecp5du0auajrbshb852d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fecp5du0auajrbshb852d.png" alt="Command for amplify pull"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are using an AWS account via IAM, you should log in to your AWS Console on your default browser. The following command is going to open up your default browser to authenticate to AWS.&lt;/p&gt;

&lt;p&gt;If you are not using AWS IAM for auth, but are using the Amplify Console that has its own username/password style login, you don’t need to do anything in advance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DON’T TYPE THIS EXACT LINE&lt;/strong&gt;&lt;br&gt;
Use the line from your environment as it has the appID for your application&lt;br&gt;
The following line is just an example&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify pull &lt;span class="nt"&gt;--appId&lt;/span&gt; dgt42342sdv765la &lt;span class="nt"&gt;--envName&lt;/span&gt; staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will eventually open a browser page to authenticate the process. As mentioned earlier, if you are using IAM for access, its easiest if you logged into the AWS Console with your browser first. If you forget to do this, you can still login now, and copy and past the link shown in the output of the CLI command and it will retry authenticating.&lt;/p&gt;

&lt;p&gt;If you are using the Amplify Studio username/password, you will get that dialog on the browser and you can fill it in and click Yes&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3yhz5njb5pqlygf0m3ls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3yhz5njb5pqlygf0m3ls.png" alt="Login Success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will then prompt you for a bunch of things to set up your amplify project in this repo&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Opening link: https://us-west-2.admin.amplifyapp.com/admin/dgt42342sdv765la/staging/verify/
✔ Successfully received Amplify Studio tokens.
Amplify AppID found: dgtkqevv765la. Amplify App name is: rental-cljs
Backend environment staging found in Amplify Console app: rental-cljs
? Choose your default editor:
  Android Studio
  Xcode (Mac OS only)
  Atom Editor
  Sublime Text
  IntelliJ IDEA
  Vim (via Terminal, Mac OS only)
❯ Emacs (via Terminal, Mac OS only)
(Move up and down to reveal more choices)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Of course the only choice that makes sense is Emacs 🤓&lt;br&gt;
(Note even though it says via terminal, it works fine with GUI Emacs)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Choose the type of app that you're building (Use arrow keys)
  android
  flutter
  ios
❯ javascript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Keep javascript&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? What javascript framework are you using (Use arrow keys)
  angular
  ember
  ionic
❯ react
  react-native
  vue
  none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Keep react&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Source Directory Path:  src/amplify
? Distribution Directory Path: public
? Build Command:  npm run-script build
? Start Command: npm run-script start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Enter &lt;code&gt;src/amplify&lt;/code&gt;for &lt;code&gt;Source Directory Path&lt;/code&gt;&lt;br&gt;
&lt;em&gt;NOTE: we're going to send the amplify JSX/JS files to a directory that is NOT in the clojurescript classpath&lt;/em&gt;&lt;br&gt;
Enter &lt;code&gt;public&lt;/code&gt; for &lt;code&gt;Distribution Directory Path&lt;/code&gt;&lt;br&gt;
This build puts everything in &lt;code&gt;public&lt;/code&gt; but other scaffolding or cljs projects may use some other path. It should be the same as the directory above &lt;code&gt;js&lt;/code&gt; in the &lt;code&gt;output-dir&lt;/code&gt; parameter in &lt;code&gt;shadow-cljs.edn&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can keep the defaults for &lt;code&gt;Build Command&lt;/code&gt; and &lt;code&gt;Start Command&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The rest of the config inputs and outputs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Synced UI components.
GraphQL schema compiled successfully.

Edit your schema at /Users/rberger/work/aws/amplifystudio-cljs-tutorial/amplify/backend/api/rentalcljs/schema.graphql or place .graphql files in a directory at /Users/rberger/work/aws/amplifystudio-cljs-tutorial/amplify/backend/api/rentalcljs/schema
Successfully generated models. Generated models can be found in /Users/rberger/work/aws/amplifystudio-cljs-tutorial/src/main
? Do you plan on modifying this backend? (Y/n) Y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Say &lt;code&gt;Y&lt;/code&gt; for &lt;code&gt;Do you plan on modifying this backend?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You might want to checkpoint your git repo again after this.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"After pulling Amplify Studio project"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can make sure the basic reagent setup is still working by doing:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The first time you run this, it will take a while to download all the Clojurescript / Clojure dependencies.&lt;/p&gt;

&lt;p&gt;And see that the app is running at &lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;br&gt;
You will just see &lt;code&gt;Create Reagent App&lt;/code&gt; on the page as a header.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxisbvv4p0ev2wifw9yud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxisbvv4p0ev2wifw9yud.png" alt="Initial Create Reagent App success page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Update to support mixing webpack with shadow-cljs
&lt;/h2&gt;

&lt;p&gt;Based on David Vujic’s work &lt;a href="https://davidvujic.blogspot.com/2021/08/hey-webpack-hey-clojurescript.html" rel="noopener noreferrer"&gt;Agile &amp;amp; Coding: Hey Webpack, Hey ClojureScript&lt;/a&gt; we’re going to add mechanisms to build the javascript code using webpack and the clojurescript code with shadow-cljs. This is necessary when using more recent versions of the AWS Amplify libraries.&lt;/p&gt;
&lt;h3&gt;
  
  
  Make sure Shadow-cljs dependencies are up to date
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;shadow-cljs.edn&lt;/code&gt; make sure that the dependencies are up to date (you can check for the latest versions at &lt;a href="https://clojars.org/" rel="noopener noreferrer"&gt;Clojars&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:dependencies&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;reagent&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;"1.1.0"&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="n"&gt;binaryage/devtools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"1.0.4"&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;h3&gt;
  
  
  Shadow-cljs js-options
&lt;/h3&gt;

&lt;p&gt;Add the following lines to shadow-cljs.edn between the &lt;code&gt;:asset-path&lt;/code&gt; and &lt;code&gt;:modules&lt;/code&gt; stanzas in the &lt;code&gt;:app&lt;/code&gt; section as per &lt;a href="https://github.com/thheller" rel="noopener noreferrer"&gt;Thomas Heller&lt;/a&gt;'s article&lt;br&gt;
&lt;a href="https://code.thheller.com/blog/shadow-cljs/2020/05/08/how-about-webpack-now.html" rel="noopener noreferrer"&gt;How about webpack now?&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="no"&gt;:js-options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:js-provider&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;:external&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="no"&gt;:external-index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"target/index.js"&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;h3&gt;
  
  
  Make a template from index.html
&lt;/h3&gt;

&lt;p&gt;Webpack will be used to update index.html with the proper script include that points to the webpack bundle.&lt;/p&gt;
&lt;h4&gt;
  
  
  Move &lt;code&gt;public/index.html&lt;/code&gt; to &lt;code&gt;public/index.html.tmpl&lt;/code&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;public/index.html public/index.html.tmpl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Edit &lt;code&gt;public/index.html.tmpl&lt;/code&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;defer&lt;/code&gt; to the main script tag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Change:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Add in a sytlesheet for the fonts
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Add the following line after the other &lt;code&gt;link&lt;/code&gt; tags in &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
  &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;
  &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css?family=Inter:slnt,wght@-10..0,100..900&amp;amp;display=swap"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Copy the Amplify CSS to public
&lt;/h4&gt;

&lt;p&gt;Note that the source is &lt;code&gt;styles.css&lt;/code&gt; (plural) and the destination is &lt;code&gt;style.css&lt;/code&gt; (singular)&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="nb"&gt;cp &lt;/span&gt;node_modules/@aws-amplify/ui/dist/styles.css public/css/style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Update the scaffold Clojurescript code to support Amplify
&lt;/h2&gt;

&lt;p&gt;Here's where we actually get to the actually writing of some code to use the Amplify UI Components in an App.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;src/main/amplifystudio_cljs_tutorial/app/core.cljs&lt;/code&gt; with the following changes&lt;/p&gt;
&lt;h3&gt;
  
  
  Add the dependencies for the &lt;code&gt;require&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Add the aws amplify and ui imports to the require so it looks like:&lt;/p&gt;

&lt;p&gt;Note that the &lt;code&gt;amplify pull&lt;/code&gt; will populate &lt;code&gt;src/amplify/ui-components&lt;/code&gt; and the &lt;code&gt;webpack&lt;/code&gt; execution described further on, will set things up so the &lt;code&gt;"ui-components/CardACollection"&lt;/code&gt; require can be fulfilled. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/amplify&lt;/code&gt; should &lt;em&gt;NOT&lt;/em&gt; be in the clojure[script] class path (usually set in shadow-cljs.edn &lt;code&gt;:source-paths&lt;/code&gt; map)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amplifystudio-cljs-tutorial.app.core&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;reagent.dom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdom&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="s"&gt;"/aws-exports"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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="s"&gt;"aws-amplify"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amplify&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="s"&gt;"@aws-amplify/ui-react"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AmplifyProvider&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="s"&gt;"ui-components/RentalCollection"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RentalCollection&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;h3&gt;
  
  
  Update the &lt;code&gt;app&lt;/code&gt; function
&lt;/h3&gt;

&lt;p&gt;This is the actual initial page code that is run by the render function. It is primarily &lt;a href="https://github.com/reagent-project/reagent/blob/master/doc/UsingHiccupToDescribeHTML.md" rel="noopener noreferrer"&gt;hiccup&lt;/a&gt; syntax.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hiccup describes HTML elements and user-defined components as a nested ClojureScript vector.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first element is either a keyword or a symbol

&lt;ul&gt;
&lt;li&gt;If it is a keyword, the element is an HTML element where (name keyword) is the tag of the HTML element.&lt;/li&gt;
&lt;li&gt;If it is a symbol, reagent will treat the vector as a component, as described in the next section.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If the second element is a map, it represents the attributes to the element. The attribute map may be omitted.&lt;/li&gt;
&lt;li&gt;Any additional elements must either be Hiccup vectors representing child nodes or string literals representing child text nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Displays an &lt;code&gt;h1&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;Wraps the &lt;code&gt;RentalCollection&lt;/code&gt; we created in Figma / ui-components with the &lt;code&gt;AmplifyProvider&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;:&amp;gt;&lt;/code&gt; is a function, &lt;a href="http://reagent-project.github.io/docs/master/reagent.core.html#var-adapt-react-class" rel="noopener noreferrer"&gt;adapt-react-class&lt;/a&gt;, that tells hiccup/reagent to interpret the next symbol as a React Component.&lt;br&gt;
More info at: &lt;a href="https://cljdoc.org/d/reagent/reagent/1.1.0/doc/tutorials/react-features" rel="noopener noreferrer"&gt;React Features in Reagent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;app&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&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="no"&gt;:&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AmplifyProvider&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;:h1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Amplify Studio Tutorial"&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="no"&gt;:&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RentalCollection&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;For comparison here is the equivalent Javascript:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AmplifyProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RentalCollection&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AmplifyProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;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;h3&gt;
  
  
  Update the &lt;code&gt;main&lt;/code&gt; function
&lt;/h3&gt;

&lt;p&gt;This function is the first code called in the program. It is where you would put any initialization code and then it calls the render function that kicks of the reagent/react event loop.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a bit of logging so we can see that we're hitting the code at runtime&lt;/li&gt;
&lt;li&gt;Add the Amplify initialization code.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="no"&gt;:export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&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="nf"&gt;js/console.log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"main top"&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="nb"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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="nf"&gt;render&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;In the Clojurescript statement:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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;&lt;code&gt;-&amp;gt;&lt;/code&gt; is the &lt;a href="https://clojuredocs.org/clojure.core/-%3E" rel="noopener noreferrer"&gt;thread-first macro&lt;/a&gt;. In this case it means that &lt;code&gt;Amplify&lt;/code&gt; will be passed in as the second argument of the following form. I.E. its the equivalent to this Clojurescript statement:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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;In ether case, it is the Javascript interop equivalent to:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Setup Webpack / Babel
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Babel config file
&lt;/h3&gt;

&lt;p&gt;Babel does the work of converting JSX files to Javascript files suitable for consumption by webpack and shadow-cljs. It is called by webpack.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;.babelrc&lt;/code&gt; in the top level of the repo with the content:&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;"presets"&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="s2"&gt;"@babel/preset-env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@babel/preset-react"&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;This tells babel to run the presets:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://babeljs.io/docs/en/babel-preset-env" rel="noopener noreferrer"&gt;@babel/preset-env&lt;/a&gt; is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). This both makes your life easier and JavaScript bundles smaller!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://babeljs.io/docs/en/babel-preset-react#docsNav" rel="noopener noreferrer"&gt;@babel/preset-react&lt;/a&gt; loads the following plugins:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://babeljs.io/docs/en/babel-plugin-syntax-jsx" rel="noopener noreferrer"&gt;@babel/plugin-syntax-jsx&lt;/a&gt; - enables parsing of JSX&lt;br&gt;
&lt;a href="https://babeljs.io/docs/en/babel-plugin-transform-react-jsx" rel="noopener noreferrer"&gt;@babel/plugin-transform-react-jsx&lt;/a&gt; - transform JSX to Javascript&lt;br&gt;
&lt;a href="https://babeljs.io/docs/en/babel-plugin-transform-react-display-name" rel="noopener noreferrer"&gt;@babel/plugin-transform-react-display-name&lt;/a&gt; - Set displayName in the Javascript&lt;/p&gt;

&lt;p&gt;And with the development option Classic runtime adds:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://babeljs.io/docs/en/babel-plugin-transform-react-jsx-self" rel="noopener noreferrer"&gt;@babel/plugin-transform-react-jsx-self&lt;/a&gt; - sets &lt;code&gt;self&lt;/code&gt; in the transformed code&lt;br&gt;
&lt;a href="https://babeljs.io/docs/en/babel-plugin-transform-react-jsx-source" rel="noopener noreferrer"&gt;@babel/plugin-transform-react-jsx-source&lt;/a&gt; - injects the source information (file, lineno) into the the Javascript&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Webpack configuration file
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://webpack.js.org/concepts/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, which are static assets to serve your content from.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are using it to convert the JSX &lt;code&gt;ui-component&lt;/code&gt; files from Figma/Amplify Studio into vanilla Javascript via babel.&lt;/p&gt;

&lt;p&gt;Webpack is also being used to bundle the &lt;code&gt;src/amplify/models&lt;/code&gt; and &lt;code&gt;src/amplify/ui-components&lt;/code&gt; directories/files that are pulled from amplify into the repo as modules so that their objects can be &lt;code&gt;imported&lt;/code&gt; into the app. This is configured in the &lt;code&gt;resolve&lt;/code&gt; block below.&lt;/p&gt;

&lt;p&gt;This will be a webpack configuration file, &lt;code&gt;webpack.config.js&lt;/code&gt; in the top level of the repo. The following will describe the elements we're going to use in that file.&lt;/p&gt;
&lt;h4&gt;
  
  
  Requires
&lt;/h4&gt;

&lt;p&gt;The following requires the webpack modules and plugins used&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HtmlWebpackPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HtmlBeautifierPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html-beautifier-webpack-plugin&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;h4&gt;
  
  
  Basic Webpack config
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Set mode to development&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entry&lt;/code&gt; - The file generated by shadow-cljs describing all the require/imports seen in the code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt; - Where webpack should put its final bundle of javascript that will be included by a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag in the index.html&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;devtool&lt;/code&gt; - Tells webpack to generate source maps to be consumed by the browser devtools
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./target/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js/libs/bundle.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clean&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source-map&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;h4&gt;
  
  
  Rules
&lt;/h4&gt;

&lt;p&gt;This is the main directives that tell weback what to do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;test: /\.m?js/,&lt;/code&gt; - Regex that specifies what file types to apply to the first rule to (ones that end with &lt;code&gt;.mjs&lt;/code&gt; or &lt;code&gt;.js&lt;/code&gt;)

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fullySpecified: false&lt;/code&gt; - the import / require statements should not end with file suffixes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alias&lt;/code&gt; - Maps the path to the javascript files to a module name. This allows the code to require the Amplify Studio &lt;code&gt;models&lt;/code&gt; and &lt;code&gt;ui-components&lt;/code&gt; as importable modules.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test: /\.jsx$/&lt;/code&gt; - Regex that specifies which file types to apply to the second rule (JSX files)

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;exclude&lt;/code&gt; - Don't apply it to files installed by npm in &lt;code&gt;/node_modules/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use&lt;/code&gt; - Apply babel to the JSX files. The &lt;code&gt;.babelrc&lt;/code&gt; file specified earlier tells babel to transform the JSX files to vanilla javascript
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;rules&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="c1"&gt;// docs: https://webpack.js.org/configuration/module/#resolvefullyspecified&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;m&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;js/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;fullySpecified&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;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/amplify/models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-components&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/amplify/ui-components&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;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;jsx$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/node_modules/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;use&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="s2"&gt;babel-loader&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;h4&gt;
  
  
  Plugins
&lt;/h4&gt;

&lt;p&gt;This is where plugins are loaded.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;process&lt;/code&gt; - This was needed as webpack 5 no longer includes a polyfil for the &lt;code&gt;process&lt;/code&gt; Node.js variable. There were some dependencies that required &lt;code&gt;process.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webpack.js.org/plugins/html-webpack-plugin/" rel="noopener noreferrer"&gt;HtmlWebpackPlugin&lt;/a&gt; - Enables creating &lt;code&gt;public/index.html&lt;/code&gt; from a temlate so that webpack can inject the path to its bundle into the index.html. Also useful if you want to automate the updates of the index.html for other things.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/zamanruhy/html-beautifier-webpack-plugin#readme" rel="noopener noreferrer"&gt;HtmlBeautifierPlugin&lt;/a&gt; Cleans up the index.html with proper newlines mainly
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ProvidePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;process/browser&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./public/index.html.tmpl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.html&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlBeautifierPlugin&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 Html plugins / index.html templating are not totally necessary. You could just add your own script tag to index.html instead such as:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/libs/bundle.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  The full &lt;code&gt;webpack.config.js&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Create a file &lt;code&gt;webpack.config.js&lt;/code&gt; also at the top level of the repo with the content:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HtmlWebpackPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HtmlBeautifierPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html-beautifier-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./target/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js/libs/bundle.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clean&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source-map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&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="c1"&gt;// docs: https://webpack.js.org/configuration/module/#resolvefullyspecified&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;m&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;js/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;fullySpecified&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;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/amplify/models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-components&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/amplify/ui-components&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;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;jsx$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/node_modules/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;use&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="s2"&gt;babel-loader&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;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extensions&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="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.jsx&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;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ProvidePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;process/browser&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./public/index.html.tmpl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.html&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlBeautifierPlugin&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;h4&gt;
  
  
  Add a script to run webpack
&lt;/h4&gt;

&lt;p&gt;Add the following line to the &lt;code&gt;”scripts”&lt;/code&gt; section of &lt;code&gt;package.json&lt;/code&gt;. It will allow you to run a that will update the bundle automatically when you change any of the amplify files or when shadow-cljs updates the &lt;code&gt;target/index.js&lt;/code&gt;&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="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"pack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webpack --watch"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Update git
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Update .gitignore
&lt;/h4&gt;

&lt;p&gt;add the following to &lt;code&gt;.gitignore&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/target/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Add all the new files to the commit
&lt;/h4&gt;

&lt;p&gt;git add -A&lt;br&gt;
git commit -m "Sync up all the final changes"&lt;/p&gt;
&lt;h2&gt;
  
  
  Running the development service locally
&lt;/h2&gt;

&lt;p&gt;Start the shadow-cljs watch process. (&lt;code&gt;shadow-cljs watch app&lt;/code&gt;) using the npm command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And in another terminal window, also at the top of the repo run the webpack watch process:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run pack

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

&lt;/div&gt;


&lt;p&gt;Should see something like the following. The images and values are dependent on how you set up the data when following along with the first part of the &lt;a href="https://welearncode.com/studio-vacation-site/" rel="noopener noreferrer"&gt;Build a Vacation Rental Site with Amplify Studio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F0et5xl1gkhrng8qihpmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0et5xl1gkhrng8qihpmo.png" alt="Initial Integration View"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If when you start the shadow-cljs process, &lt;code&gt;npm start&lt;/code&gt;, and you get something like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
shadow-cljs - watching build :app
&lt;span class="o"&gt;[&lt;/span&gt;:app] Configuring build.
&lt;span class="o"&gt;[&lt;/span&gt;:app] Compiling ...
&lt;span class="o"&gt;[&lt;/span&gt;2022-01-02 21:55:15.214 - WARNING] :shadow.cljs.devtools.server.util/handle-ex - &lt;span class="o"&gt;{&lt;/span&gt;:msg &lt;span class="o"&gt;{&lt;/span&gt;:type :start-autobuild&lt;span class="o"&gt;}}&lt;/span&gt;
AssertionError Assert failed: &lt;span class="o"&gt;(&lt;/span&gt;map? rc&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;js-provider :external&lt;/code&gt; config in &lt;code&gt;shadow-cljs.edn&lt;/code&gt; is masking the actual error. In order to see what the error is, comment out the &lt;code&gt;:js-options&lt;/code&gt; block in &lt;code&gt;shadow-cljs.edn&lt;/code&gt; like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:builds
 {:app
  {:target     :browser
   :output-dir "public/js"
   :asset-path "/js"
   ;; :js-options {:js-provider    :external
   ;;              :external-index "target/index.js"}
   :modules    {:main
                {:init-fn amplifystudio-cljs-tutorial.app.core/main}}}

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

&lt;/div&gt;


&lt;p&gt;and then run &lt;code&gt;npm start&lt;/code&gt; again and see what the error is. Correct the error and then remember to uncomment the &lt;code&gt;:js-options&lt;/code&gt; block.&lt;/p&gt;
&lt;h2&gt;
  
  
  Completing the Tutorial with Amplify UI Overrides
&lt;/h2&gt;

&lt;p&gt;We pick back up the original tutorial at the &lt;code&gt;Use a Prop&lt;/code&gt; section to show how the UI Components can be customized just with Component Props and runtime Overrides.&lt;/p&gt;

&lt;p&gt;Overrides are a powerful feature that are builtin to the Amplify UI Components and allow you to inject attributes into the children of components at runtime. It makes the Amplify UI Components very flexible without having to modify the actual code of the components. This allows you to update the Figma design aspects and still update your local copy of the ui-components with an &lt;code&gt;amplify pull&lt;/code&gt; since you don't make local changes to that code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use a Prop
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You can customize these React components in your own code. First, you can use props in order to modify your components. If you wanted to make your grid of rentals into a list, for example, you could pass the prop type="list" to your RentalCollection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Javascript you would say:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RentalCollection&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and in Clojurescript:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;:&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RentalCollection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"list"&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;h4&gt;
  
  
  And that will make the view go from a grid to a list:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7uah9q1ip4cu24yl5oq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7uah9q1ip4cu24yl5oq5.png" alt="Colllection as a list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The props are listed for each component type at &lt;a href="https://ui.docs.amplify.aws/components" rel="noopener noreferrer"&gt;Amplify UI Connected Components&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Use an Override
&lt;/h3&gt;

&lt;p&gt;Overrides allow you to inject props into the children of a component.&lt;/p&gt;

&lt;p&gt;In our example RentalCollection, the images in the child cards are kind of squashed. To fix that we want to set the &lt;code&gt;objectFit&lt;/code&gt; prop of the image element of the card to &lt;code&gt;cover&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Javascript you would use:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RentalCollection&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;overrides&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Collection.CardA[0]&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;overrides&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="s2"&gt;Flex.Image[0]&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;objectFit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cover&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;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In Clojurescript we use:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;:&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RentalCollection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="no"&gt;:overrides&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Collection.CardA[0]"&lt;/span&gt;&lt;span class="w"&gt;
                                  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:overrides&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Flex.Image[0]"&lt;/span&gt;&lt;span class="w"&gt;
                                               &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:object-fit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"cover"&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;h4&gt;
  
  
  Now the images are no longer squished:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fyl6jmfytet8bmplwqqeb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fyl6jmfytet8bmplwqqeb.png" alt="objectFit cover prop applied to children images"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Themes and Conclusion
&lt;/h2&gt;

&lt;p&gt;That completes showing the differences of using Clojurescript instead of Javascript with Amplify Studio and Amplify UI Connected Components.&lt;/p&gt;

&lt;p&gt;You can refer back to the original AWS Tutorial &lt;a href="https://welearncode.com/studio-vacation-site/" rel="noopener noreferrer"&gt;Build a Vacation Rental Site with Amplify Studio&lt;/a&gt; for the remaining content on how to use the &lt;a href="https://www.figma.com/community/plugin/1040722185526429545/AWS-Amplify-Theme-Editor" rel="noopener noreferrer"&gt;AWS Amplify Theme Editor&lt;/a&gt; in Figma to add a theme to the UI Components. This should work without having to change any of your Clojurescript code as you modify the Ui component code that you load via &lt;code&gt;amplify pull&lt;/code&gt; via Figma.&lt;/p&gt;

&lt;p&gt;It is also possible to &lt;a href="https://ui.docs.amplify.aws/theming" rel="noopener noreferrer"&gt;apply themes directly in your code&lt;/a&gt;. Doing that with Clojurescript will be left to a possible future article.&lt;/p&gt;

&lt;p&gt;The full project / code for this repo is at&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rberger" rel="noopener noreferrer"&gt;
        rberger
      &lt;/a&gt; / &lt;a href="https://github.com/rberger/amplifystudio-cljs-tutorial" rel="noopener noreferrer"&gt;
        amplifystudio-cljs-tutorial
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Tutorial on using Clojurescript with Amplify Studio / Figma
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Feel free to post issues or questions there.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Set up SSL/TLS for shadow-cljs https server</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Thu, 21 Oct 2021 06:10:50 +0000</pubDate>
      <link>https://dev.to/rberger/set-up-ssltls-for-shadow-cljs-https-server-2np7</link>
      <guid>https://dev.to/rberger/set-up-ssltls-for-shadow-cljs-https-server-2np7</guid>
      <description>&lt;p&gt;While developing clojurescript web apps, you may require that the development http server &lt;a href="https://github.com/thheller/shadow-cljs" rel="noopener noreferrer"&gt;shadow-cljs&lt;/a&gt; operate with SSL/TLS to serve up HTTPS, not just HTTP.&lt;/p&gt;

&lt;p&gt;This is particuarly true if you need to test things out on an iPhone or Android phone but still run with the development server so you can iterated changes just as quick as when you are working with desktop clients.&lt;/p&gt;

&lt;p&gt;Its a bit tricky to get everything lined up to make SSL/TLS work locally as Apple (and I presume other browsers) no longer support self-signed certificates for HTTPS servers. So you need a private CA and a certificate generated from the private CA.&lt;/p&gt;

&lt;p&gt;This is a guide to set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Private Certificate Authority (CA)&lt;/li&gt;
&lt;li&gt;A Server Certificate for your shadow-cljs development server&lt;/li&gt;
&lt;li&gt;How to configure shadow-cljs.edn for SSL&lt;/li&gt;
&lt;li&gt;How to install the CA Root Certificate on other clients (like an iPhone) so they can access the shadow-cljs servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;**NOTE&lt;/em&gt;&lt;em&gt;: This server / CA / Certificates should never be used in production or in any particularly public way. It’s not secure. We’re doing this to get around the normal browser / server security just for local development.&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Install mkcert
&lt;/h2&gt;

&lt;p&gt;See the following for more info or how to install on Linux: &lt;a href="https://github.com/FiloSottile/mkcert" rel="noopener noreferrer"&gt;GitHub - FiloSottile/mkcert&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install mkcert on macOS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;mkcert
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;nss &lt;span class="c"&gt;# if you use Firefox&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a local CA to be used by mkcert and clients
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mkcert &lt;span class="nt"&gt;-install&lt;/span&gt;
Created a new &lt;span class="nb"&gt;local &lt;/span&gt;CA 💥
Sudo password:
The &lt;span class="nb"&gt;local &lt;/span&gt;CA is now installed &lt;span class="k"&gt;in &lt;/span&gt;the system trust store! ⚡️
The &lt;span class="nb"&gt;local &lt;/span&gt;CA is now installed &lt;span class="k"&gt;in &lt;/span&gt;the Firefox trust store &lt;span class="o"&gt;(&lt;/span&gt;requires browser restart&lt;span class="o"&gt;)!&lt;/span&gt; 🦊
The &lt;span class="nb"&gt;local &lt;/span&gt;CA is now installed &lt;span class="k"&gt;in &lt;/span&gt;Java&lt;span class="s1"&gt;'s trust store! ☕️
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a pkcs12 certificate
&lt;/h2&gt;

&lt;p&gt;Easiest to do this in the directory you are running the shadow-cljs project.&lt;br&gt;
Create a subdirectory &lt;code&gt;ssl&lt;/code&gt; at the same level as shadow-cljs (top level of the repo usually) and cd into &lt;code&gt;ssl&lt;/code&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="nb"&gt;cd&lt;/span&gt; ~/work/my-project
❯ &lt;span class="nb"&gt;ls
&lt;/span&gt;Makefile        RELEASE_TAG     bin             dev             package.json    shadow-cljs.edn &lt;span class="nb"&gt;test
&lt;/span&gt;README.org      amplify         deps.edn        node_modules    resources       src             yarn.lock

❯ &lt;span class="nb"&gt;mkdir &lt;/span&gt;ssl
❯ &lt;span class="nb"&gt;cd &lt;/span&gt;ssl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the certificate that the shadow-cljs servers will use as their server certificates. You want to specify all the domains and IPs that would be associated with the certificate and the way you will access the server.&lt;br&gt;
In my case my iMac has two interfaces plus localhost. One interface is the Ethernet, the other is the wifi. And just to be safe, I’m putting in their IPv6 addresses as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ mkcert &lt;span class="nt"&gt;-pkcs12&lt;/span&gt; discovery.local localhost  192.168.20.10 192.168.20.11 127.0.0.1 ::1 fd95:cb6f:7955:0:1878:b8b5:1b3b:ad27 fd95:cb6f:7955:0:4cd:c922:d1b3:2eb5

Created a new certificate valid &lt;span class="k"&gt;for &lt;/span&gt;the following names 📜
 - &lt;span class="s2"&gt;"discovery.local"&lt;/span&gt;
 - &lt;span class="s2"&gt;"localhost"&lt;/span&gt;
 - &lt;span class="s2"&gt;"192.168.20.10"&lt;/span&gt;
 - &lt;span class="s2"&gt;"192.168.20.11"&lt;/span&gt;
 - &lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;
 - &lt;span class="s2"&gt;"::1"&lt;/span&gt;
 - &lt;span class="s2"&gt;"fd95:cb6f:7955:0:1878:b8b5:1b3b:ad27"&lt;/span&gt;
 - &lt;span class="s2"&gt;"fd95:cb6f:7955:0:4cd:c922:d1b3:2eb5"&lt;/span&gt;

The PKCS#12 bundle is at &lt;span class="s2"&gt;"./discovery.local+7.p12"&lt;/span&gt; ✅

The legacy PKCS#12 encryption password is the often hardcoded default &lt;span class="s2"&gt;"changeit"&lt;/span&gt; ℹ️

It will expire on 20 January 2024 🗓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install the cert into the keystore
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;The passwords  you use here should not be used anywhere else, particularly on public services. They do not have to be super secret, great passwords as they will be in the clear in your shadow-cljs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You will create a local Java JKS Keystore in &lt;code&gt;ssl&lt;/code&gt; to be used by shadow-cljs servers&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Destination Password&lt;/code&gt;: This will be the password specified in shadow-cljs.edn to gain access to the keystore.  Our example will be &lt;code&gt;super-secret&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Source keystore password&lt;/code&gt;: The password that &lt;code&gt;mkcert&lt;/code&gt; used to generate the Server Certificate and thus the password of the Server Certificate. I could not find a way to specify it. It defaults to &lt;code&gt;changeit&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ keytool &lt;span class="nt"&gt;-importkeystore&lt;/span&gt; &lt;span class="nt"&gt;-destkeystore&lt;/span&gt; keystore.jks &lt;span class="nt"&gt;-srcstoretype&lt;/span&gt; PKCS12 &lt;span class="nt"&gt;-srckeystore&lt;/span&gt; discovery.local+7.p12

Importing keystore discovery.local+7.p12 to keystore.jks...
Enter destination keystore password: super-secret
Re-enter new password: super-secret
Enter &lt;span class="nb"&gt;source &lt;/span&gt;keystore password: changeit
Entry &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;1 successfully imported.
Import &lt;span class="nb"&gt;command &lt;/span&gt;completed:  1 entries successfully imported, 0 entries failed or cancelled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure shadow-cljs.edn to enable SSL
&lt;/h2&gt;

&lt;p&gt;Mainly need to add an &lt;code&gt;:ssl&lt;/code&gt; coda to the start of the &lt;code&gt;shadow-cljs.edn&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:deps&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:nrepl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8777&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:ssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ssl/keystore.jks"&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="no"&gt;:password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"retold-fever"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:dev-http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;8020&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"resources/public"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shadow-cljs.edn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to specify the hostnames. In fact that will limit access to IP addresses that resolve to that name which may be incorrect.&lt;br&gt;
More info on the &lt;code&gt;:ssl&lt;/code&gt; configuration at &lt;a href="https://shadow-cljs.github.io/docs/UsersGuide.html#_ssl" rel="noopener noreferrer"&gt;Shadow CLJS User’s Guide: SSL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[Re]start your shadow-cljs watch process and it should say something like the following at some point in its startup where &lt;code&gt;https&lt;/code&gt; is the protocol shown for the http and shadow-cljs servers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
shadow-cljs - HTTP server available at https://localhost:8020
shadow-cljs - server version: 2.15.8 running at https://localhost:9631
shadow-cljs - nREPL server started on port 8777
shadow-cljs - watching build :app
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming you set the certificate to support any other domain names and IP addresses associated with your computer running this, they will also work as the host address in your client URL accessing this server. But only if running on the same machine as this server.&lt;/p&gt;

&lt;p&gt;If you want to make another device (like an iPhone or another computer) access this server, follow the next steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export the Root CA of your Private CA to other Clients
&lt;/h2&gt;

&lt;p&gt;In order for other machines on your LAN to access the shadow-cljs server running with the Private CA and Server certificate set up in the earlier steps,  you will need to export the Root CA from that machine to these other clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find the location of the Root Certificate of the Private CA
&lt;/h3&gt;

&lt;p&gt;When you ran &lt;code&gt;mkcert install&lt;/code&gt; it created the root certificates of the Private CA and stashed them somewhere appropriate for your system. You can find out where with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ mkcert &lt;span class="nt"&gt;-CAROOT&lt;/span&gt;
/Users/rberger/Library/Application Support/mkcert

❯ &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s1"&gt;'/Users/rberger/Library/Application Support/mkcert'&lt;/span&gt;
rootCA-key.pem rootCA.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will want to copy the &lt;code&gt;rootCA.pem&lt;/code&gt; to other clients that would access the shadow-cljs servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  For transferring to other Macs or iOS devices
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open &lt;span class="s1"&gt;'/Users/rberger/Library/Application Support/mkcert'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will open a finder window with the directory where these pem files are:&lt;br&gt;
&lt;a href="https://media.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%2Fexsbtc5r2dsrczgnvpti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fexsbtc5r2dsrczgnvpti.png" alt="Root Cert in Finder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then select AirDrop to send them to other macOS or iOS devices&lt;br&gt;
Otherwise you can email it or send the file some other way to a destination device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the Private CA Root Cert on iOS device
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Once you send the Cert to an iOS device, you will get a message &lt;br&gt;
&lt;a href="https://media.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%2Ft8fjzuy9jytqxwvvztwd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ft8fjzuy9jytqxwvvztwd.png" alt="Choose Device"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select iPhone  and then select Close:&lt;br&gt;
&lt;a href="https://media.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%2Fvz3leh2s56lo1kh8g26d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fvz3leh2s56lo1kh8g26d.jpg" alt="Close"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to Settings and you’ll see the a new option &lt;code&gt;Profile Downloaded&lt;/code&gt; Click on that and the go thru the rest of the dialogs agreeing to Install the downloaded profile.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6i0ef9km1knjg3y2cbo3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6i0ef9km1knjg3y2cbo3.jpg" alt="Profile Downloaded in Settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After completing all the install dialogs, this client should be ready to connect to the shadow-cljs using https.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>clojurescript</category>
      <category>tls</category>
      <category>https</category>
    </item>
    <item>
      <title>Accessing AppSync APIs that require Cognito Login outside of Amplify</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Wed, 01 Sep 2021 01:23:37 +0000</pubDate>
      <link>https://dev.to/aws-builders/accessing-appsync-apis-that-require-cognito-login-outside-of-amplify-5bg8</link>
      <guid>https://dev.to/aws-builders/accessing-appsync-apis-that-require-cognito-login-outside-of-amplify-5bg8</guid>
      <description>&lt;h2&gt;
  
  
  The Need
&lt;/h2&gt;

&lt;p&gt;You have this great Amplify App using AppSync GraphQL. You eventually find that you need to be able to access that data in your AppSync GraphQL database from tools other than your Amplify App. Its easy if you just have your AppSync API protected just by an API Key. But that isn't great security for your data!&lt;/p&gt;

&lt;p&gt;One way to protect your AppSync data is to use &lt;a href="https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/js/#cognito-user-pools" rel="noopener noreferrer"&gt;Cognito Identity Pools&lt;/a&gt;. Amplify makes it pretty transparent if you are  using Amplify to build your clients. AppSync lets you do really nice &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/security-authorization-use-cases.html" rel="noopener noreferrer"&gt;table and record level access control based on logins and roles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What happens if you want to access that data from something other than an Amplify based client? How do you "login" and get the JWT credentials you need to access your AppSync APIs?&lt;/p&gt;

&lt;h2&gt;
  
  
  Use AWS CLI
&lt;/h2&gt;

&lt;p&gt;The most general way is to use the AWS CLI to effectively login and retrieve the JWT credentials that can then be passed in the headers of any requests you make to your AppSync APIs.&lt;/p&gt;

&lt;p&gt;Unfortunately its not as easy as just having your login and password. It also depends on how you configured your Cognito Identity Pool and its related Client Apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cognito User Pool Client App
&lt;/h3&gt;

&lt;p&gt;You can have multiple Client Apps specified for your Cognito User Pool. I suggest  having one dedicated to these external applications. That way you can have custom configuration just for this and not disrupt your main  Amplify apps. Also you can easily turn it off if you need too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F74z6pgmf1qdyqv9wkllr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F74z6pgmf1qdyqv9wkllr.png" title="User Pool Client Apps" alt="User Pool Client Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my case I created a new client app &lt;code&gt;shoppabdbe800b-rob-test2&lt;/code&gt; as a way to test a client app with no &lt;code&gt;App Client Secret&lt;/code&gt;. This makes it easier to access from the command line as you do not have to generate a Secret Hash (will describe how to deal with that below).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fhipjcnn0e3q4ronqvgi0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fhipjcnn0e3q4ronqvgi0.png" title="App Client Config with no secret" alt="App Client Config with no secret"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to allow admin level access (ie a user with admin permission) you need to check &lt;code&gt;Enable username password auth for admin APIs for authentication (ALLOW_ADMIN_USER_PASSWORD_AUTH)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you want to allow regular users to login you must also select &lt;code&gt;Enable username password based authentication (ALLOW_USER_PASSWORD_AUTH)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The defaults for the other fields should be ok. Be sure to save your changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimal IAM permissions
&lt;/h3&gt;

&lt;p&gt;As far as I can tell, these are the minimal IAM permissions to make the aws &lt;code&gt;cognito-idp&lt;/code&gt; command work for admin and regular users of AppSync (replace the Resource arn with the arn of the user pool[s] you want to control):&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VisualEditor0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"cognito-idp:AdminInitiateAuth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"cognito-idp:AdminGetUser"&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;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:cognito-idp:us-east-1:XXXXXXXXXXXXX:userpool/us-east-1_XXXXXXXXX"&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="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VisualEditor1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"cognito-idp:GetUser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"cognito-idp:InitiateAuth"&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;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;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;h3&gt;
  
  
  Get the Credentials with no App Client Secret
&lt;/h3&gt;

&lt;p&gt;This example is if you did not set the App Client Secret.&lt;/p&gt;

&lt;p&gt;You should now be able to get the JWT credentials from the AWS CLI.&lt;/p&gt;

&lt;p&gt;This assumes you have&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html" rel="noopener noreferrer"&gt; set up your&lt;/a&gt; &lt;code&gt;~/.aws/credentials&lt;/code&gt; file or whatever is appropriate for your command line environment so that you have the permissions to access this service.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When using the &lt;code&gt;ADMIN_USER_PASSWORD_AUTH&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp admin-initiate-auth &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; us-east-1_XXXXXXXXXX &lt;span class="nt"&gt;--auth-flow&lt;/span&gt; ADMIN_USER_PASSWORD_AUTH &lt;span class="nt"&gt;--client-id&lt;/span&gt; XXXXXXXXXXXXX &lt;span class="nt"&gt;--auth-parameters&lt;/span&gt; &lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;username1,PASSWORD&lt;span class="o"&gt;=&lt;/span&gt;XXXXXXXXXXXXX &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; creds.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;When using the &lt;code&gt;USER_PASSWORD_AUTH&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp initiate-auth &lt;span class="nt"&gt;--auth-flow&lt;/span&gt; USER_PASSWORD_AUTH &lt;span class="nt"&gt;--client-id&lt;/span&gt; XXXXXXXXXXXXX &lt;span class="nt"&gt;--auth-parameters&lt;/span&gt; &lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;username2,PASSWORD&lt;span class="o"&gt;=&lt;/span&gt;XXXXXXXXXXXX &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; creds.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course replace the &lt;code&gt;XXXX&lt;/code&gt;'s with the actual values.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;user-pool-id&lt;/code&gt; - The pool id found at the top of the &lt;em&gt;User Pool Client Apps&lt;/em&gt; page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client-id&lt;/code&gt; - The &lt;code&gt;client-id&lt;/code&gt; of the &lt;code&gt;app client&lt;/code&gt; you are using&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;USERNAME&lt;/code&gt; - The Username normally used to login to your Amplify app&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PASSWORD&lt;/code&gt; - The Password normally used to login to your Amplify app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The results will be in &lt;code&gt;creds.json&lt;/code&gt;. (You could not use the &lt;code&gt;&amp;gt; creds.json&lt;/code&gt; if you want to just see the results)&lt;/p&gt;

&lt;h3&gt;
  
  
  Get the Credentials when there is an App Client Secret
&lt;/h3&gt;

&lt;p&gt;This assumes you have an App Client that has an &lt;code&gt;app secret key&lt;/code&gt; set.&lt;/p&gt;

&lt;p&gt;The main thing here is you need to generate a &lt;code&gt;secret hash&lt;/code&gt; to send along with the command.&lt;/p&gt;

&lt;p&gt;You can do that by creating a little python program to generate it for you when you need it:&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;app_client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;secret_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;digestmod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SECRET HASH:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;secret_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;len sys.argv: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;username&amp;gt; &amp;lt;app_client_id&amp;gt; &amp;lt;app_client_secret&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file someplace that you can execute it from like &lt;code&gt;~/bin/app-client-secret-hash&lt;/code&gt; and make it executable (&lt;code&gt;chmod a+x ~/bin/app-client-secret-hash&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app-client-id&lt;/code&gt; - The &lt;code&gt;client-id&lt;/code&gt; of the &lt;code&gt;app client&lt;/code&gt; you are using&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app-client-secret&lt;/code&gt; - The secret of the &lt;code&gt;app client&lt;/code&gt; you are using (its on the App Client page of the User Pool)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;USERNAME&lt;/code&gt; - The Username normally used to login to your Amplify app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/bin/app-client-secret-hash  &amp;lt;username&amp;gt; &amp;lt;app_client_id&amp;gt; &amp;lt;app_client_secret&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where of  course you replace the arguments with the actual values. &lt;/p&gt;

&lt;p&gt;The result is a &lt;code&gt;secret-hash&lt;/code&gt; you will use in the following command to get the actual JWT credentials&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp admin-initiate-auth &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; us-east-1_XXXXXXXXXX &lt;span class="nt"&gt;--auth-flow&lt;/span&gt; ADMIN_USER_PASSWORD_AUTH &lt;span class="nt"&gt;--client-id&lt;/span&gt; XXXXXXXXXXXXX &lt;span class="nt"&gt;--auth-parameters&lt;/span&gt; &lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;username3,PASSWORD&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'secret password'&lt;/span&gt;,SECRET_HASH&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'secret-hash'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; creds.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could do the same thing with &lt;code&gt;USER_PASSWORD_AUTH&lt;/code&gt; if you nee that instead&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp initiate-auth &lt;span class="nt"&gt;--auth-flow&lt;/span&gt; USER_PASSWORD_AUTH &lt;span class="nt"&gt;--client-id&lt;/span&gt; XXXXXXXXXXXXX &lt;span class="nt"&gt;--auth-parameters&lt;/span&gt; &lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rob+admin,PASSWORD&lt;span class="o"&gt;=&lt;/span&gt;XXXXXXXXX,SECRET_HASH&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'secret-hash'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; creds.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the Credentials
&lt;/h2&gt;

&lt;p&gt;How you use these credentials depends on what tool or  how you are trying to access your AppSync APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  From some Javascript
&lt;/h3&gt;

&lt;p&gt;You can just add in the &lt;code&gt;IdToken&lt;/code&gt; from the &lt;code&gt;creds.json&lt;/code&gt; as an &lt;code&gt;Authorization&lt;/code&gt; header when you build the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;graphQLFetcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graphQLParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;APPSYNC_API_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TYPE_YOUR_APPSYNC_URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credentialsAppSync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eyJraWQiOiI1dVUwMld...&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;APPSYNC_API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&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;credentialsAppSync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graphQLParams&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;omit&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;If you are using some GraphQL tool that needs to access your AppSync APIs. The tool should have a way that you can supply the token and it will add it as an &lt;code&gt;Authorization&lt;/code&gt; header for its own requests.&lt;/p&gt;

&lt;p&gt;Do let me know if you have some examples of tools that would make use of this.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/appsync-graphiql-local/" rel="noopener noreferrer"&gt;Explore AWS AppSync APIs with GraphiQL from your local machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/cognito-unable-to-verify-secret-hash/" rel="noopener noreferrer"&gt;How do I troubleshoot "Unable to verify secret hash for client " errors from my Amazon Cognito user pools API?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>appsync</category>
      <category>graphql</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS Amplify and Clojurescript Re-Frame Part 2</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Tue, 21 Jan 2020 19:31:08 +0000</pubDate>
      <link>https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-2-47gk</link>
      <guid>https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-2-47gk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Now that our Re-Frame app is running, let's deploy it to "production" on AWS &lt;a href="https://aws.amazon.com/cloudfront/"&gt;Cloudfront&lt;/a&gt; using &lt;a href="https://aws.amazon.com/amplify/console/"&gt;Amplify Console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Amplify Console allows you to do a CI style deployment from your git repo with almost no work on your part.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/welcome.html"&gt;AWS Amplify Console Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup Amplify Console
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Connect your repo to the Amplify Console
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use your web browser to go to &lt;a href="https://console.aws.amazon.com/amplify/home"&gt;https://console.aws.amazon.com/amplify/home&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you have no previous Amplify Deployments you will see:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8cd3sabZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/amplify-initial-page.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8cd3sabZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/amplify-initial-page.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should select the &lt;strong&gt;Get Started&lt;/strong&gt; under &lt;strong&gt;Deploy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Select the appropriate Git service (We used Github)&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fVQQAqBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/amplify-select-git-provider.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fVQQAqBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/amplify-select-git-provider.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It will then go thru the process of allowing you to authenticate that the
Amplify service can have the appropriate access to your github repos for the
github account/organization you enable&lt;/li&gt;
&lt;li&gt;Will then prompt you for the actual repo and branch you want to build and deployments
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BW8UHETG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/add-repository-branch.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;NEXT&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: The previous article &lt;a href="https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-1-3d3f"&gt;Add Serverless AWS Amplify Authentication to a Clojurescript Re-Frame App&lt;/a&gt; and this current article are based on branch &lt;code&gt;basic-amplify-with-cloudfront&lt;/code&gt; of the &lt;code&gt;omnyway-labs/amplify_cljs_example&lt;/code&gt; repo&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Basic and Backend build settings
&lt;/h3&gt;

&lt;p&gt;The Backend is the AWS services that were created using the Amplify CLI. For us that was the Authentication service.&lt;/p&gt;

&lt;p&gt;The Frontend is our Web App.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will see the following:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vd1c45hv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/configure-build-settings-1.png" alt=""&gt;

&lt;ul&gt;
&lt;li&gt;Change the &lt;code&gt;App name&lt;/code&gt; if you want&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Backend Deployment Options
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;You have the choice under &lt;code&gt;Would you like Amplify Console to deploy changes to these resources with your frontend?&lt;/code&gt; to:

&lt;ul&gt;
&lt;li&gt;Not have it automatically deploy the backend along with your frontend

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;No - only deploy my frontend&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Deploy the same backend for prod as you are using in the dev environment 

&lt;ul&gt;
&lt;li&gt;Select &lt;code&gt;dev&lt;/code&gt; and not &lt;code&gt;Create new environment&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Deploy a different copy of the backend from your dev environment

&lt;ul&gt;
&lt;li&gt;Select &lt;code&gt;Create new environment&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fill in the name of the new environment. In our case &lt;code&gt;prod&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Service Role
&lt;/h4&gt;

&lt;p&gt;This is the role that the Amplify Console runs as when setting up your backend services on AWS&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If this is the first time thru, you should 

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Create new role&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Go thru the pages and accept the pre-selected defaults on each page and create the new service role&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Refresh&lt;/strong&gt; option&lt;/li&gt;
&lt;li&gt;Use the pulldown to select the role you created&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If it wasn't the first time, select the role that already exists&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Build Settings
&lt;/h4&gt;

&lt;p&gt;This is the CI Commands to build and deploy your app. It has the default YAML file that at a minimum requires you to set the Frontend build commands and the build output directory. &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CvIeMPlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/build-settings.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CvIeMPlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/build-settings.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Backend commands are fine, but we need to do several things to be able to build our Clojurescript application. You can edit it on the Amplify Console webpage (by clicking on &lt;code&gt;Edit&lt;/code&gt;) and / or locally by downloading it and editing locally. In ether case we'll want to download it and put the &lt;code&gt;amplify.yml&lt;/code&gt; file in the root directory of our repo.&lt;/p&gt;

&lt;p&gt;(Note: Its not clear to me if we really need the file on the console if we have it in our repo. It seems that the one that is in your repo will supersede the one here on the Amplify Console)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install

&lt;ul&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;Leiningen&lt;/li&gt;
&lt;li&gt;shadow-cljs&lt;/li&gt;
&lt;li&gt;Add the following to the frontend prebuild commands:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Set the base directory to where shadow-cljs puts the build assets&lt;/li&gt;
&lt;li&gt;Set the frontend build command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frontend:
  phases:
    preBuild:
      commands:
        - amazon-linux-extras enable corretto8
        - yum -y install java-1.8.0-amazon-corretto-devel
        - java -version
        - curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein &amp;gt; lein
        - mv lein /usr/local/bin/lein
        - chmod a+x /usr/local/bin/lein
        - lein version
        - npm install -g shadow-cljs
        - npm ci
    build:
      commands: [lein prod]
  artifacts:
    baseDirectory: resources/public/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If all went well, you will eventually have a successful build / deploy
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hiJclvkq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/successful_deploy.png" alt=""&gt;

&lt;ul&gt;
&lt;li&gt;Click on the Domain link to open the application
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qe9lyMhg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/prod-app-running.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The full features of the Authentication Lifecycle are now available

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Create account&lt;/code&gt; to create an account
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nkHQSsdG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/create-new-account.png" alt=""&gt;

&lt;ul&gt;
&lt;li&gt;The email needs to be a real email address you will receive as confirmation codes will be sent there
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z6X-Uduh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/confirm-signup.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Sign-in then with the credentials you had just created
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UVrLpfWg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/sign-in.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;And then you are in the app with the option to logout
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e2B_1v-R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/in-the-app.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;If you use AWS &lt;a href="https://aws.amazon.com/route53/"&gt;Route53&lt;/a&gt; for your DNS&lt;br&gt;
management, the Amplify console can set up a custom DNS Domain and SSL&lt;br&gt;
Certificate to go with your new app!&lt;/p&gt;

&lt;p&gt;If you don't use Route53, there is a way to insert a custom DNS but will not show how in this doc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the Domain Management option for the application you just created in the Amplify Console
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nzI7N6Nl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/domian-management-1.png" alt=""&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Add Domain&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Enter the root domain of the route53 zone you want to add this domain to

&lt;ul&gt;
&lt;li&gt;We are using a domain zone (&lt;code&gt;lab.omnyway.net&lt;/code&gt;) that is a subdomain

&lt;ul&gt;
&lt;li&gt;The important point is it should be the domain that is a zone in your route53&lt;/li&gt;
&lt;li&gt;The Route53 zone probably has to be the same AWS account as the App
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r_iWwhek--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/add-domain-1.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Configure domain&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If this is the first time you use this feature, it has to first try some things to get authenticated. 

&lt;ul&gt;
&lt;li&gt;It will add a weird CNAME to the zone automatically. You don't have to do anything&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;You could use the defaults if you want

&lt;ul&gt;
&lt;li&gt;The root domain to resolve to your webapp&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;www.yourdomain&lt;/code&gt; to resolve to your webapp
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RNPuOqEm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/initial-add-domain.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;But we have other things in this domain so we want it to just have a single domainname not &lt;code&gt;www&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;code&gt;Exclude root&lt;/code&gt; so it turns to &lt;code&gt;Include root&lt;/code&gt; and dims the pulldown &lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;www&lt;/code&gt; with the domain hostname you want.

&lt;ul&gt;
&lt;li&gt;In this case the name of the app &lt;code&gt;amplify_cljs_example&lt;/code&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DfnGCx1I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/final-add-domain.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Assuming it all works

&lt;ul&gt;
&lt;li&gt;It will add the CNAME to your route53 zone&lt;/li&gt;
&lt;li&gt;It will create an SSL Certificate  and wire it all up&lt;/li&gt;
&lt;li&gt;Will get the domain fully activated&lt;/li&gt;
&lt;li&gt;You can then use the new url shown to access the webapp&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;You now have an example of how to create and deploy a Clojurescript Re-Frame SPA with really nice Authentication capabilities with almost no coding. &lt;/p&gt;

&lt;p&gt;The next article (Not yet written) will show how to use the AWS Amplify Authentication services to allow access to other standard AWS services.&lt;/p&gt;

</description>
      <category>clojurescript</category>
      <category>reframe</category>
      <category>awsamplify</category>
      <category>spa</category>
    </item>
    <item>
      <title>AWS Amplify and Clojurescript Re-Frame Part 1</title>
      <dc:creator>Robert J. Berger</dc:creator>
      <pubDate>Tue, 21 Jan 2020 19:30:35 +0000</pubDate>
      <link>https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-1-3d3f</link>
      <guid>https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-1-3d3f</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Amplify
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/amplify/"&gt;AWS Amplify&lt;/a&gt; makes it super simple to build Web and Mobile Apps that use the entire suite of AWS Services with minimal coding by the app developer. Clojurescript and Re-Frame make creating SPA apps a joy. The combination of all three is truly a superpower!&lt;/p&gt;

&lt;p&gt;The AWS Amplify CLI makes it easy to setup various AWS services and make them available via the Amplify SDK to your Web or Mobile App. It also has great support for React based apps.&lt;/p&gt;

&lt;p&gt;The goal of this project is to show how to be able to use AWS Amplify with Clojurescript / Re-Frame Web Apps.&lt;/p&gt;

&lt;p&gt;The initial AWS feature to incorporate is the Amplify Authentication service which has AWS Cognito at its core. Amplify offers a React &lt;a href="https://reactjs.org/docs/higher-order-components.html"&gt;Higher Order Component&lt;/a&gt; that allows you to wrap your JS app with Authentication.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensures that your App can only be accessed when authenticated&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Handles all the UI and API integration with Cognito&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You do not have to write any of the code&lt;/li&gt;
&lt;li&gt;Sign-up

&lt;ul&gt;
&lt;li&gt;Verify phone_number or email address&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Sign-in&lt;/li&gt;
&lt;li&gt;Sign-out&lt;/li&gt;
&lt;li&gt;Forgot Password&lt;/li&gt;
&lt;li&gt;Password reset&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Optionally (and not described in this first article but easy to add)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customize Authenticator UI&lt;/li&gt;
&lt;li&gt;Use your own UI components&lt;/li&gt;
&lt;li&gt;AWS Auth for AWS services which require signing requests.&lt;/li&gt;
&lt;li&gt;Social Provider Federation / OAuth&lt;/li&gt;
&lt;li&gt;Force new password&lt;/li&gt;
&lt;li&gt;Multi-Factor Authentication&lt;/li&gt;
&lt;li&gt;User Attributes&lt;/li&gt;
&lt;li&gt;Lambda Triggers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: This article and its followup &lt;a href="https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-2-47gk"&gt;AWS Amplify and Clojurescript Re-Frame Part 2: Deploy to Production Cloudfront&lt;/a&gt; are based on branch &lt;code&gt;basic-amplify-with-cloudfront&lt;/code&gt; of the &lt;code&gt;omnyway-labs/amplify_cljs_example&lt;/code&gt; repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Re-Frame / Clojurescript / Shadow-cljs
&lt;/h3&gt;

&lt;p&gt;The main goal of this project is to show how to leverage Amplify in a &lt;a href="https://clojurescript.org/"&gt;Clojurescript&lt;/a&gt; Single Page App (&lt;a href="https://en.wikipedia.org/wiki/Single-page_application"&gt;SPA&lt;/a&gt;). In particular, using the &lt;a href="https://github.com/Day8/re-frame"&gt;re-frame&lt;/a&gt; Clojurescript Library.&lt;/p&gt;

&lt;p&gt;Re-frame is a functional framework for building SPAs leveraging &lt;a href="http://reagent-project.github.io/"&gt;Reagent&lt;/a&gt;. Reagent is a minimalistic interface between ClojureScript and React.&lt;/p&gt;

&lt;p&gt;Clojurescript is hosted on Javascript and can naturally interoperate with Javascript in general. This project uses &lt;a href="http://shadow-cljs.org/"&gt;shadow-cljs&lt;/a&gt; as the buildtool. Its major superpower is to make it pretty easy to leverage node.js packages. The main features it offers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good configuration defaults so you don't have to sweat the details&lt;/li&gt;
&lt;li&gt;Seamless npm integration&lt;/li&gt;
&lt;li&gt;Fast builds, reliable caching, ...&lt;/li&gt;
&lt;li&gt;Supporting various targets :browser, :node-script, :npm-module, :react-native, :chrome-extension, ...&lt;/li&gt;
&lt;li&gt;Live Reload (CLJS + CSS)&lt;/li&gt;
&lt;li&gt;CLJS REPL&lt;/li&gt;
&lt;li&gt;Code splitting (via :modules)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Put together, these tools allows one to build SPAs in a functional / Clojure centric way, but still leverage all the wonders of React and node.&lt;/p&gt;

&lt;p&gt;That being said. There are a lot of learning curves that also need to be climbed to make it all work together.&lt;/p&gt;

&lt;p&gt;This project is an attempt to make it easier for folks to jump right in. It can be used as a starting point to build your own SPA with sophisticated Authentication services.&lt;/p&gt;

&lt;p&gt;This article and the associated github repo &lt;a href="https://github.com/omnyway-labs/amplify_cljs_example"&gt;omnyway-labs/amplify_cljs_example&lt;/a&gt; should tell you everything you need from scratch to get you to a working basis of a re-frame SPA running on your local machine with full AWS Cognito Simple Authentication backend. The amount of code you have to write to make it work is surprisingly trivial due to the scaffolding features of re-frame and the AWS generation / auto-configuration of Amplify.&lt;/p&gt;

&lt;p&gt;The second article in this series &lt;a href="https://github.com/omnyway-labs/amplify_cljs_example/wiki/2.-Deploy-to-CloudFront-with-Amplify-Console"&gt;Deploy to CloudFront with Amplify Console&lt;/a&gt; shows how to deploy the SPA to AWS CloudFront with minimal effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;p&gt;You have already installed the following on your laptop / dev machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://aws.amazon.com"&gt;AWS Account&lt;/a&gt; that you have administrative access to&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt; (Ideally have a &lt;a href="https://github.com/"&gt;github&lt;/a&gt; account)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://adoptopenjdk.net/"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; and &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://clojure.org/"&gt;Clojure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://clojurescript.org/"&gt;Clojurscript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://shadow-cljs.org/"&gt;shadow-cljs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leiningen.org/"&gt;leiningen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Have an &lt;a href="http://spacemacs.org/"&gt;editor&lt;/a&gt; and know how to use it &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Initial Setup
&lt;/h2&gt;

&lt;p&gt;We're using the &lt;a href="https://github.com/Day8/re-frame-template"&gt;re-frame scaffold template&lt;/a&gt; to create the initial app. We're not going to add much to the app in this article other than the Amplify Authentication wrapper.&lt;/p&gt;

&lt;p&gt;Later articles will add more functionality so we're including some extras like &lt;a href="https://github.com/noprompt/garden"&gt;garden&lt;/a&gt; for doing CSS in clojure. More importantly we're including &lt;a href="https://re-com.day8.com.au/"&gt;re-com&lt;/a&gt; Which is a library of ClojureScript UI components, built on top of Reagent. It integrates with re-frame in a very functional / clojure way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Day8/re-frame-10x"&gt;10x&lt;/a&gt; is a very nice debugging dashboard for re-frame. Again, won't be using it until later articles but its something you will want to have if you use this for building on top of.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate the scaffold
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;lein new re-frame amplify_cljs_example +10x +cider +re-com +test +garden
&lt;span class="nb"&gt;cd &lt;/span&gt;amplify_cljs_example
lein garden once
git init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update .gitignore by adding:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/resources/public/css/screen.css
.shadow-cljs
node_modules
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;More setup; build and run the local instance of the initial scaffolded SPA
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&lt;/span&gt;
git commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit after lein new re-frame amplify_cljs_example +10x +cider +re-com +test +garden"&lt;/span&gt;
npm &lt;span class="nb"&gt;install
&lt;/span&gt;git add package-lock.json
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"package-lock for npm"&lt;/span&gt; package-lock.json
lein garden once
lein dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;lein dev&lt;/code&gt; will build and run the application locally. It doesn't exit, it keeps running, monitoring the app files. If there are any changes it will automatically hot load the application. Lein is effectively running &lt;a href="https://shadow-cljs.github.io/docs/UsersGuide.html#_hot_code_reload"&gt;shadow-cljs watch app&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;lein dev&lt;/code&gt; probably added more packages for npm and so you should update your git in another shell window in the same directory
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"lein updates to package.json"&lt;/span&gt; package-lock.json  package.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Ensure the basics are working
&lt;/h2&gt;

&lt;p&gt;So if the &lt;code&gt;lein dev&lt;/code&gt; is running properly, it will end with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[:app] Build completed. (1659 files, 1658 compiled, 0 warnings, 46.89s)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At that point, you should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;code&gt;http://localhost:8280&lt;/code&gt; in a browser (Chrome is optimal)&lt;/li&gt;
&lt;li&gt;Should look something like:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--84EyJghk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/initial_working_display.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--84EyJghk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/initial_working_display.png" alt="Initial Working Display"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up AWS Amplify
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You should be in the top level of the &lt;code&gt;amplify_cljs_example&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Assuming you don’t already have AWS Amplify installed on your dev machine:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @aws-amplify/cli
amplify configure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Follow the instructions as described in &lt;a href="https://youtu.be/fWbM5DLh25U"&gt;Installing &amp;amp; Configuring the AWS Amplify CLI - YouTube&lt;/a&gt; to configure you local dev amplify environment to manage your AWS environment&lt;/li&gt;
&lt;li&gt;Initialize the Amplify project.

&lt;ul&gt;
&lt;li&gt;Mostly accept the defaults or as appropriate for you&lt;/li&gt;
&lt;li&gt;Start command : &lt;code&gt;lein dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AWS Profile yes and use the one you created when you did &lt;code&gt;amplify configure&lt;/code&gt; which by default is &lt;code&gt;default&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; amplify init
Scanning for plugins...
Plugin scan successful
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project reframerecomamplifye
? Enter a name for the environment dev
? Choose your default editor: Emacs (via Terminal, Mac OS only)
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path:  src
? Distribution Directory Path: build
? Build Command:  lein prod
? Start Command: lein dev
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default
⠧ Initializing project in the cloud...

CREATE_IN_PROGRESS AuthRole                                AWS::IAM::Role             Fri Oct 04 2019 22:50:40 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS DeploymentBucket                        AWS::S3::Bucket            Fri Oct 04 2019 22:50:40 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UnauthRole                              AWS::IAM::Role             Fri Oct 04 2019 22:50:40 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS AuthRole                                AWS::IAM::Role             Fri Oct 04 2019 22:50:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS DeploymentBucket                        AWS::S3::Bucket            Fri Oct 04 2019 22:50:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UnauthRole                              AWS::IAM::Role             Fri Oct 04 2019 22:50:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 22:50:36 GMT-0700 (Pacific Daylight Time) User Initiated
⠴ Initializing project in the cloud...

CREATE_COMPLETE UnauthRole AWS::IAM::Role Fri Oct 04 2019 22:50:52 GMT-0700 (Pacific Daylight Time)
⠏ Initializing project in the cloud...

CREATE_COMPLETE AuthRole AWS::IAM::Role Fri Oct 04 2019 22:50:52 GMT-0700 (Pacific Daylight Time)
⠹ Initializing project in the cloud...

CREATE_COMPLETE DeploymentBucket AWS::S3::Bucket Fri Oct 04 2019 22:51:01 GMT-0700 (Pacific Daylight Time)
⠇ Initializing project in the cloud...

CREATE_COMPLETE reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 22:51:03 GMT-0700 (Pacific Daylight Time)
✔ Successfully created initial AWS cloud resources for deployments.
✔ Initialized provider successfully.
Initialized your environment successfully.

Your project has been successfully initialized and connected to the cloud!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add the Amplify Authentication service

&lt;ul&gt;
&lt;li&gt;Details at &lt;a href="https://aws-amplify.github.io/docs/js/authentication"&gt;AWS Amplify Authentication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign-in: Selecting 'Email' and/or 'Phone Number' will allow end users to sign-up using these values.  Selecting 'Username' will require a unique username for users.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; amplify add auth
Using service: Cognito, provided by: awscloudformation

 The current configured provider is Amazon Cognito.

 Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections.
 How do you want users to be able to sign in? Email
 Do you want to configure advanced settings? No, I am done.
Successfully added resource reframerecomamplifye16cd456a locally
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Push the auth feature / config to AWS
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; amplify auth push

Current Environment: dev

| Category | Resource name                | Operation | Provider plugin   |
| -------- | ---------------------------- | --------- | ----------------- |
| Auth     | reframerecomamplifye16cd456a | Create    | awscloudformation |
? Are you sure you want to continue? Yes
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionRole          AWS::IAM::Role             Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS authreframerecomamplifye16cd456a        AWS::CloudFormation::Stack Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS authreframerecomamplifye16cd456a        AWS::CloudFormation::Stack Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionRole          AWS::IAM::Role             Fri Oct 04 2019 23:08:00 GMT-0700 (Pacific Daylight Time)
UPDATE_IN_PROGRESS reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 23:07:57 GMT-0700 (Pacific Daylight Time) User Initiated
⠇ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS reframerecomamplifye-dev-20191004225035-authreframerecomamplifye16cd456a-19BSJYQRK36 AWS::CloudFormation::Stack Fri Oct 04 2019 23:08:01 GMT-0700 (Pacific Daylight Time) User Initiated
⠋ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS SNSRole AWS::IAM::Role Fri Oct 04 2019 23:08:08 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS SNSRole AWS::IAM::Role Fri Oct 04 2019 23:08:07 GMT-0700 (Pacific Daylight Time)
⠴ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE UpdateRolesWithIDPFunctionRole AWS::IAM::Role Fri Oct 04 2019 23:08:15 GMT-0700 (Pacific Daylight Time)
⠋ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE SNSRole AWS::IAM::Role Fri Oct 04 2019 23:08:20 GMT-0700 (Pacific Daylight Time)
⠙ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    UserPool AWS::Cognito::UserPool Fri Oct 04 2019 23:08:27 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPool AWS::Cognito::UserPool Fri Oct 04 2019 23:08:27 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPool AWS::Cognito::UserPool Fri Oct 04 2019 23:08:24 GMT-0700 (Pacific Daylight Time)
⠇ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    UserPoolClientWeb AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:33 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClientWeb AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:33 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_COMPLETE    UserPoolClient    AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:33 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClient    AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:32 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientWeb AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:31 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClient    AWS::Cognito::UserPoolClient Fri Oct 04 2019 23:08:31 GMT-0700 (Pacific Daylight Time)
⠙ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientRole AWS::IAM::Role Fri Oct 04 2019 23:08:36 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientRole AWS::IAM::Role Fri Oct 04 2019 23:08:36 GMT-0700 (Pacific Daylight Time)
⠏ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE UserPoolClientRole AWS::IAM::Role Fri Oct 04 2019 23:08:48 GMT-0700 (Pacific Daylight Time)
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    UserPoolClientLambda AWS::Lambda::Function Fri Oct 04 2019 23:08:52 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClientLambda AWS::Lambda::Function Fri Oct 04 2019 23:08:52 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientLambda AWS::Lambda::Function Fri Oct 04 2019 23:08:52 GMT-0700 (Pacific Daylight Time)
⠸ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientLambdaPolicy AWS::IAM::Policy Fri Oct 04 2019 23:08:57 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientLambdaPolicy AWS::IAM::Policy Fri Oct 04 2019 23:08:56 GMT-0700 (Pacific Daylight Time)
⠼ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE UserPoolClientLambdaPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:02 GMT-0700 (Pacific Daylight Time)
⠋ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientLogPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:06 GMT-0700 (Pacific Daylight Time)
⠸ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientLogPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:06 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠸ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE UserPoolClientLogPolicy AWS::IAM::Policy Fri Oct 04 2019 23:09:12 GMT-0700 (Pacific Daylight Time)
⠸ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientInputs Custom::LambdaCallout Fri Oct 04 2019 23:09:15 GMT-0700 (Pacific Daylight Time)
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS IdentityPool         AWS::Cognito::IdentityPool Fri Oct 04 2019 23:09:23 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE    UserPoolClientInputs Custom::LambdaCallout      Fri Oct 04 2019 23:09:19 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UserPoolClientInputs Custom::LambdaCallout      Fri Oct 04 2019 23:09:19 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠼ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    IdentityPool AWS::Cognito::IdentityPool Fri Oct 04 2019 23:09:25 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS IdentityPool AWS::Cognito::IdentityPool Fri Oct 04 2019 23:09:24 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠋ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS IdentityPoolRoleMap AWS::Cognito::IdentityPoolRoleAttachment Fri Oct 04 2019 23:09:28 GMT-0700 (Pacific Daylight Time)
⠴ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    reframerecomamplifye-dev-20191004225035-authreframerecomamplifye16cd456a-19BSJYQRK36 AWS::CloudFormation::Stack               Fri Oct 04 2019 23:09:32 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE    IdentityPoolRoleMap                                                                  AWS::Cognito::IdentityPoolRoleAttachment Fri Oct 04 2019 23:09:30 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS IdentityPoolRoleMap                                                                  AWS::Cognito::IdentityPoolRoleAttachment Fri Oct 04 2019 23:09:30 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
⠧ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE authreframerecomamplifye16cd456a AWS::CloudFormation::Stack Fri Oct 04 2019 23:09:36 GMT-0700 (Pacific Daylight Time)
⠏ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionOutputs Custom::LambdaCallout Fri Oct 04 2019 23:09:41 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE    UpdateRolesWithIDPFunction        AWS::Lambda::Function Fri Oct 04 2019 23:09:39 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS UpdateRolesWithIDPFunction        AWS::Lambda::Function Fri Oct 04 2019 23:09:39 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
CREATE_IN_PROGRESS UpdateRolesWithIDPFunction        AWS::Lambda::Function Fri Oct 04 2019 23:09:38 GMT-0700 (Pacific Daylight Time)
⠹ Updating resources in the cloud. This may take a few minutes...

UPDATE_COMPLETE                     reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 23:09:47 GMT-0700 (Pacific Daylight Time)
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS reframerecomamplifye-dev-20191004225035 AWS::CloudFormation::Stack Fri Oct 04 2019 23:09:47 GMT-0700 (Pacific Daylight Time)
CREATE_COMPLETE                     UpdateRolesWithIDPFunctionOutputs       Custom::LambdaCallout      Fri Oct 04 2019 23:09:45 GMT-0700 (Pacific Daylight Time)
CREATE_IN_PROGRESS                  UpdateRolesWithIDPFunctionOutputs       Custom::LambdaCallout      Fri Oct 04 2019 23:09:44 GMT-0700 (Pacific Daylight Time) Resource creation Initiated
✔ All resources are updated in the cloud
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install the Amplify Node modules we’ll be using for Authentication
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm add aws-amplify aws-amplify-react
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Copy the css for the amplify UI widgets into the proper resource for our app’s
css to be picked up

&lt;ul&gt;
&lt;li&gt;Note: I was looking for a better way to do this that is more via
configuration. If anyone knows a better way please let me know
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;node_modules/@aws-amplify/ui/dist/style.css resources/public/css/aws-amplify-ui-style.css
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update &lt;code&gt;resources/public/index.html&lt;/code&gt; to incorporate that css by adding the following line in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/aws-amplify-ui-style.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Get the changes into git
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git add amplify resources/public/css/aws-amplify-ui-style.css
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Added and configured Amplify"&lt;/span&gt; .gitignore package-lock.json package.json amplify resources/public/index.html resources/public/css
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Your application at &lt;code&gt;http://localhost:8280&lt;/code&gt; should still be running fine
assuming you still have the &lt;code&gt;lein dev&lt;/code&gt; process running&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Update the code to enable the Authenticator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Edit &lt;code&gt;src/cljs/re_frame_re_com_amplify_exp/core.cljs&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add the following lines to the require block. This is what will import the node js into your Clojurescript
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"aws-amplify"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amp&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="s"&gt;"aws-amplify-react"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withAuthenticator&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="s"&gt;"/aws-exports.js"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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;ul&gt;
&lt;li&gt;Add the following def after &lt;code&gt;dev-setup&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where you are wrapping the Amplify HOC around your application. The &lt;code&gt;withAuthenticator&lt;/code&gt; is the HOC function being pulled in from the Amplify Auth SDK. It ensures that your app only is accessed after authentication and it handles all the Frontend UI and communication with the Backend to implement the full suite of Authentication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root-view&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reagent/adapt-react-class&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withAuthenticator&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reagent/reactify-component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;views/main-panel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&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;ul&gt;
&lt;li&gt;Change the function &lt;code&gt;mount-root&lt;/code&gt; to be:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="no"&gt;:dev/after-load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mount-root&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="nf"&gt;re-frame/clear-subscription-cache!&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="nf"&gt;.configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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="nf"&gt;re-frame/dispatch-sync&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;::events/initialize-db&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="nf"&gt;reagent/render&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;root-view&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="nf"&gt;.getElementById&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;js/document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"app"&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;ul&gt;
&lt;li&gt;Remove the following line from &lt;code&gt;init&lt;/code&gt; function:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it so that the re-frame db is inited after the cache is cleared. As far as I can tell the Scaffolding puts it in the wrong place&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;re-frame/dispatch-sync&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;::events/initialize-db&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;ul&gt;
&lt;li&gt;The final file should be:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amplify_cljs_example.core&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:require&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;reagent.core&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reagent&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="n"&gt;re-frame.core&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;re-frame&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="n"&gt;amplify_cljs_example.events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&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="n"&gt;amplify_cljs_example.views&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;views&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="n"&gt;amplify_cljs_example.config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&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="s"&gt;"aws-amplify"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amp&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="s"&gt;"aws-amplify-react"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withAuthenticator&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="s"&gt;"/aws-exports.js"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dev-setup&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="nb"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config/debug?&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"dev mode"&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="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root-view&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reagent/adapt-react-class&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withAuthenticator&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reagent/reactify-component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;views/main-panel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="no"&gt;:dev/after-load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mount-root&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="nf"&gt;re-frame/clear-subscription-cache!&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="nf"&gt;.configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws-exports&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="nf"&gt;re-frame/dispatch-sync&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;::events/initialize-db&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="nf"&gt;reagent/render&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;root-view&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="nf"&gt;.getElementById&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;js/document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"app"&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;init&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="nf"&gt;dev-setup&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="nf"&gt;mount-root&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;ul&gt;
&lt;li&gt;The display should look like this:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8hKERcb_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/working_authentication.png" alt="Initial Working Display"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n2F-fAHF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/assets/1/working_authentication.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n2F-fAHF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/assets/1/working_authentication.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should click the button on the debugger to have it be in its own window as it will block the “sign-out” button later
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uGWoUKrL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.omnyway.com/wp-content/uploads/2019/12/move_debugger_window.png" alt="Initial Working Display"&gt;
&lt;/li&gt;
&lt;li&gt;Commit the changes to git
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"App updated to require Cognito Authenticator before any other operation"&lt;/span&gt; src/cljs/re_frame_re_com_amplify_exp/core.cljs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;You now have a template of how to create a basic Re-Frame SPA with authentication from AWS Amplify with almost not coding and without having to set up your own backend authentication infrastructure. You can continue with this to use other AWS Amplify authentication features (Social Provider Federation, Multi-factor auth, etc) as well as the other Amplify services such as graphql access to DynamoDB, Push notifications, etc.). &lt;/p&gt;

&lt;p&gt;The next article, &lt;a href="https://dev.to/rberger/aws-amplify-and-clojurescript-re-frame-part-2-47gk"&gt;AWS Amplify and Clojurescript Re-Frame Part 2: Deploy to Production Cloudfront&lt;/a&gt;, will show how to deploy it to "production" on AWS Cloudfront using Amplify Console so that the Re-Frame SPA can be used on the general Internet also without you having to set up any servers yourself.&lt;/p&gt;

</description>
      <category>clojurescript</category>
      <category>reframe</category>
      <category>awsamplify</category>
      <category>spa</category>
    </item>
  </channel>
</rss>
