<?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: Samira Yusifova</title>
    <description>The latest articles on DEV Community by Samira Yusifova (@tiamatt).</description>
    <link>https://dev.to/tiamatt</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%2F484180%2F3088ae64-52b4-46a9-b9fd-0ccada02adb3.jpg</url>
      <title>DEV Community: Samira Yusifova</title>
      <link>https://dev.to/tiamatt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tiamatt"/>
    <language>en</language>
    <item>
      <title>AWS project - Module 4. Use CloudFront distribution to serve a Static Website Hosted on AWS S3 via CloudFormation</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Thu, 22 Jun 2023 02:26:56 +0000</pubDate>
      <link>https://dev.to/tiamatt/aws-project-module-4-use-cloudfront-distribution-to-serve-a-static-website-hosted-on-aws-s3-via-cloudformation-226m</link>
      <guid>https://dev.to/tiamatt/aws-project-module-4-use-cloudfront-distribution-to-serve-a-static-website-hosted-on-aws-s3-via-cloudformation-226m</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In the past three modules, we successfully created a basic static web app and deployed it on an S3 bucket. We also set up a CodeBuild project to automate the website's build process on AWS S3. Additionally, we configured Route 53 to enable the use of a custom domain for our S3-hosted static website. Now, let's continue enhancing our architecture further.&lt;/p&gt;

&lt;p&gt;In this module we are going to optimize our application’s performance and security while effectively managing cost by setting up Amazon CloudFront to work with your S3 bucket to serve and protect the content. &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%2Fi6e5vmb0yujnubxh1gqg.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%2Fi6e5vmb0yujnubxh1gqg.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Agenda&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this module I'll show you how to configure CloudFront distribution to serve a simple static website in ten steps via CloudFormation:&lt;br&gt;
✳️ Step 1. Register a custom domain name&lt;br&gt;
✳️ Step 2. Create S3 buckets for your root domain and subdomain&lt;br&gt;
✳️ Step 3. Create Route 53 hosted zone and configure your domain&lt;br&gt;
✳️ Step 4: Request public SSL/TLS certificate from AWS Certificate Manager (ACM) for our domain name and all its subdomains &lt;br&gt;
✳️ Step 5. Create an Origin Access Identity (OAI) as special CloudFront user&lt;br&gt;
✳️ Step 6. Create CloudFront distribution for subdomain that contains static website&lt;br&gt;
✳️ Step 7. Create a policy for S3 bucket for subdomain to allow OAI to access bucket content&lt;br&gt;
✳️ Step 8. Create another CloudFront distribution for root domain that redirects requests to S3 bucket for subdomain&lt;br&gt;
✳️ Step 9. Create a Route 53 record set to route DNS traffic for root domain and subdomain to CloudFront domain&lt;br&gt;
✳️ Step 10. Test your static website&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The high-level architecture for our project is illustrated in the diagram 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%2Fq310reo394suguhddayx.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%2Fq310reo394suguhddayx.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Source code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Source code for this project is available on my GitHub profile in a public repository named &lt;strong&gt;StaticWebsiteHostingToS3&lt;/strong&gt; (check it &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;br&gt;
👉 Frontend  source code either &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/cloudformation/module4/frontend" rel="noopener noreferrer"&gt;simple project&lt;/a&gt; or &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/frontend/app-for-aws" rel="noopener noreferrer"&gt;Angular project&lt;/a&gt;&lt;br&gt;
👉 Module 4 CloudFormation templates &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/cloudformation/module4" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;List of CloudFormation templates:&lt;br&gt;
1️⃣ Template for S3 buckets &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/1-s3-buckets-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
2️⃣ Template for Route 53 hosted zone &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/2-route53-hosted-zone-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
3️⃣ Template for ACM certification &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/3-acm-certification-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
4️⃣ Template for CloudFront distribution and OAI &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
5️⃣ Template for Route 53 record set &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/5-route53-record-set-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Initial Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Create required accounts:&lt;br&gt;
👉 AWS account&lt;br&gt;
👉 GitHub account&lt;/p&gt;

&lt;p&gt;Install required tools locally: &lt;br&gt;
👉 any IDE (personally I prefer Visual Studio Code) or any text editor &lt;br&gt;
👉 git&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;AWS Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the list of AWS resources that we are going to provision with CloudFormation:&lt;br&gt;
👉 S3 buckets&lt;br&gt;
👉 S3 bucket policy&lt;br&gt;
👉 ACM certificate&lt;br&gt;
👉 CloudFront OAI&lt;br&gt;
👉 CloudFront distributions&lt;br&gt;
👉 Route53 hosted zone&lt;br&gt;
👉 Route53 record set group&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why use CloudFront?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Amazon CloudFront is a &lt;strong&gt;CDN (Content Delivery Network)&lt;/strong&gt; provided by AWS team. CDNs are primarily used for caching, and many customers also use AWS CloudFront as a security layer, or use it to handle network spikes. &lt;/p&gt;

&lt;p&gt;With CloudFront, when a user requests a webpage or an image, the request is routed to one of Amazon’s 400+ &lt;strong&gt;edge locations&lt;/strong&gt; (caching servers). If the edge server already has the resource cached, it’s served to the client. If the resource isn’t on the edge server, it makes a request to an origin server that hosts your content (S3 bucket in our case).&lt;/p&gt;

&lt;p&gt;The edge server then saves a copy of the response locally so it can handle the next request without pestering the origin server. &lt;/p&gt;

&lt;p&gt;This reduces load on the origin server, helping you keep the instance housing your application small, and is able to reduce latency for clients by moving commonly requested resources closer to the requestor. &lt;/p&gt;

&lt;p&gt;"Amazon S3 and CloudFront — is a match made in the cloud!" it said. Using CloudFront for our project offers three primary benefits: enhanced security, improved performance, and cost efficiency.&lt;/p&gt;

&lt;h4&gt;
  
  
  ✳️ Enhanced security
&lt;/h4&gt;

&lt;p&gt;If you followed &lt;a href="https://dev.to/tiamatt/aws-project-module-3-use-your-custom-domain-for-static-website-on-aws-s3-via-route-53-and-cloudformation-34cn"&gt;Module 3&lt;/a&gt;, you may have noticed that in order to access our website hosted on S3 bucket, we granted public read access to it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

 &lt;span class="c1"&gt;# Create a policy for myS3BucketForRootDomain to let Route53 access website content&lt;/span&gt;
  &lt;span class="na"&gt;myPolicyForS3BucketForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::BucketPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForRootDomain&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublicReadForGetBucketObjects&lt;/span&gt;
            &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${myS3BucketForRootDomain.Arn}/*"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;(check the whole template &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/1-static-website-hosting-stack-v2.yaml#L94" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;However it's always &lt;strong&gt;a best practice to keep your S3 buckets private&lt;/strong&gt; to ensure security. So, a question arises: How can you host the static content of your website from &lt;strong&gt;a private&lt;/strong&gt; S3 bucket? Well, CloudFront comes to the rescue. Later on in this module you can witness firsthand that with a CloudFront Distribution it is possible to serve content from a private S3 bucket.&lt;/p&gt;

&lt;p&gt;Moreover, by configuring HTTPS with CloudFront, you establish secure end-to-end connections to your origin servers. For this purpose we will request public SSL/TLS certificate from AWS Certificate Manager (ACM) for our domain name and all its subdomains.&lt;/p&gt;

&lt;h4&gt;
  
  
  ✳️ Improved performance
&lt;/h4&gt;

&lt;p&gt;The broad network of edge locations and CloudFront caches copies of content close to the end users that results in lowering latency, high data transfer rates and low network traffic. All these make CloudFront fast. &lt;/p&gt;

&lt;p&gt;Here is how it's going to work in our case:&lt;/p&gt;

&lt;p&gt;A user requests website URL such as &lt;code&gt;www.example.com&lt;/code&gt; which we will host on S3 bucket. The request will be routed to our CloudFront Distribution by Route 53. Now, if the requested content can be served from the cache it will be delivered immediately from the edge location closest to the end users. If the content is not cached (in case of initial request or when cache is expired), CloudFront requests the content directly from S3 bucket.&lt;/p&gt;

&lt;h4&gt;
  
  
  ✳️ Cost efficiency
&lt;/h4&gt;

&lt;p&gt;For small data volumes, using S3 is cost-effective due to the free monthly transfer of the first GB. However, as data usage increases, CloudFront becomes more cost-efficient. The pricing difference for data transfer costs widens significantly when transferring terabytes of data, making CloudFront a more favorable option for content delivery compared to serving content directly from S3.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is an excellent article "AWS CloudFront Pricing and Cost Optimization Guide" I highly recommend reading (&lt;a href="https://www.cloudforecast.io/blog/aws-cloudfront-pricing-and-cost-guide/" rel="noopener noreferrer"&gt;link&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&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%2Fe3ze6z8x6vijduac61c4.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%2Fe3ze6z8x6vijduac61c4.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1. Register a custom domain name&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/tiamatt/aws-project-module-3-use-your-custom-domain-for-static-website-on-aws-s3-via-route-53-and-cloudformation-34cn"&gt;Module 3&lt;/a&gt; we covered the topic of domain name and Route 53. If you haven't had the chance to go through Module 3 and confused why use domain name, how to register it, and why use Route 53 with Amazon S3 website endpoint, please refer to Steps 1 and 4 in the following &lt;a href="https://dev.to/tiamatt/aws-project-module-3-use-your-custom-domain-for-static-website-on-aws-s3-via-route-53-and-cloudformation-34cn"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2. Create S3 buckets for your root domain and subdomain&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;📌 When you configure an Amazon S3 bucket for website hosting, you must &lt;strong&gt;give the bucket the same name as root domain/subdomain name&lt;/strong&gt; that you want to use to route traffic to the bucket. For example, if you want to route traffic for &lt;code&gt;www.example.com&lt;/code&gt; to an S3 bucket that is configured for website hosting, the name of the bucket must be &lt;code&gt;www.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you registered a new domain name, we can use it to provision an S3 bucket with the same name.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for S3 buckets from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/1-s3-buckets-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ The following piece of code helps to create a new S3 bucket for subdomain (such as &lt;code&gt;www.example.com&lt;/code&gt;) and configure it to host a static website:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myS3BucketForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;DeletionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt; &lt;span class="c1"&gt;# keep S3 bucket when its stack is deleted&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-subdomain.my-domain-name&lt;/span&gt; &lt;span class="c1"&gt;# use the name of subdomain with domain, such as www.example.com&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;IndexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;ErrorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error.html&lt;/span&gt;
      &lt;span class="na"&gt;VersioningConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# turn versioning on in case we need to rollback newly built files to older version&lt;/span&gt;
        &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;
      &lt;span class="na"&gt;AccessControl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BucketOwnerFullControl&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This S3 bucket for subdomain will contain our static website files. Don't forget to replace &lt;code&gt;my-subdomain.my-domain-name&lt;/code&gt; with your root domain name with subdomain (such as &lt;code&gt;www.example.com&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;2️⃣ If you also want your users to be able to use root domain (such as &lt;code&gt;example.com&lt;/code&gt;), to access your static website, create a second S3 bucket.&lt;/p&gt;

&lt;p&gt;📌 Our second (root domain) bucket will not host a static website. We will keep it empty and configure it to redirect the request to the first bucket.&lt;/p&gt;

&lt;p&gt;The following piece of code helps to create the second S3 bucket for root domain (such as &lt;code&gt;example.com&lt;/code&gt;) and set it up to redirect requests to S3 bucket for subdomain (such as from &lt;code&gt;example.com&lt;/code&gt; to &lt;code&gt;www.example.com&lt;/code&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myS3BucketForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;DeletionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt; &lt;span class="c1"&gt;# keep S3 bucket when its stack is deleted&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-domain-name&lt;/span&gt; &lt;span class="c1"&gt;# use the name of your domain, such as example.com&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;RedirectAllRequestsTo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Configure the bucket to route traffic to the subdomain bucket&lt;/span&gt;
          &lt;span class="na"&gt;HostName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForSubdomain&lt;/span&gt;
          &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
      &lt;span class="na"&gt;AccessControl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BucketOwnerFullControl&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Don't forget to replace &lt;code&gt;my-domain-name&lt;/code&gt; with your root domain name (such as &lt;code&gt;example.com&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;📌 Note: keep both buckets private. Lets leverage CloudFront to secure our S3 buckets and let an Origin Access Identity (OAI) - which is a special CloudFront user - to access S3 buckets safely. A new policy for S3 bucket (for subdomain only) will be created later on, in CloudFormation template for CloudFront (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml#L114" rel="noopener noreferrer"&gt;link&lt;/a&gt;) to let CloudFront OAI access content from a private S3 bucket.&lt;/p&gt;

&lt;p&gt;3️⃣ We are done with Resources, now let's output Amazon S3 website endpoint and regional domain names for our buckets - we are going to use them to configure CloudFront later in this module.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;outputS3WebsiteURLForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Amazon S3 website endpoint for root domain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForRootDomain.WebsiteURL&lt;/span&gt;
  &lt;span class="na"&gt;outputS3RegionalDomainNameForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;Regional domain name of S3 bucket for root domain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForRootDomain.RegionalDomainName&lt;/span&gt; 
  &lt;span class="na"&gt;outputS3WebsiteURLForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Amazon S3 website endpoint for subdomain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForSubdomain.WebsiteURL&lt;/span&gt;
  &lt;span class="na"&gt;outputS3RegionalDomainNameForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;Regional domain name of S3 bucket for subdomain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForSubdomain.RegionalDomainName&lt;/span&gt;  


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

&lt;/div&gt;

&lt;p&gt;4️⃣ We are ready to create and run CloudFormation stack based on our template.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you need to understand how to create a stack in AWS Console, please read &lt;a href="https://dev.to/tiamatt/hands-on-aws-cloudformation-part-1-it-all-starts-here-5153"&gt;Hands-on AWS CloudFormation - Part 1. It All Starts Here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upload our template file for S3 buckets (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/1-s3-buckets-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;) to AWS CloudFormation to create a stack. Specify your domain name and subdomain as parameters values. (I'm going to use domain name I've already registered, which is &lt;code&gt;tiamatt.com&lt;/code&gt;.) Run the stack.&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%2Fxvdnspvyy0wwstyhbnny.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%2Fxvdnspvyy0wwstyhbnny.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see two newly created buckets and the policy under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2Fcbd52y2da0ttwn0ubksh.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%2Fcbd52y2da0ttwn0ubksh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find website endpoint and regional domain names for both domain and subdomain:&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%2Fuk3h6gr0444xdkmcqh6l.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%2Fuk3h6gr0444xdkmcqh6l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5️⃣ Currently, if you navigate to &lt;code&gt;http://my-subdomain.my-domain-name.com.s3-website-us-east-1.amazonaws.com&lt;/code&gt; using Amazon S3 website endpoint for &lt;em&gt;subdomain&lt;/em&gt; then you should get 403 Forbidden page as both our S3 buckets are private.&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%2Fjomi0vf7lzbpwemfl8ju.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%2Fjomi0vf7lzbpwemfl8ju.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you navigate to &lt;code&gt;http://my-domain-name.com.s3-website-us-east-1.amazonaws.com&lt;/code&gt; using Amazon S3 website endpoint for &lt;em&gt;root domain&lt;/em&gt; then it should redirect your request to Amazon S3 website endpoint for &lt;em&gt;subdomain&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;6️⃣  To deploy the static content to our S3 bucket for subdomain you can:&lt;br&gt;
👉 either open S3 Console and manually upload both &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/frontend/index.html" rel="noopener noreferrer"&gt;index.html&lt;/a&gt; and &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/frontend/error.html" rel="noopener noreferrer"&gt;error.html&lt;/a&gt; files &lt;br&gt;
👉 or automate the build of a static website (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/frontend/app-for-aws" rel="noopener noreferrer"&gt;Angular project&lt;/a&gt;) on AWS S3 using CodeBuild (please follow the steps outlined in &lt;a href="https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2"&gt;Module 2&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3. Create Route 53 hosted zone and configure your domain&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/tiamatt/aws-project-module-3-use-your-custom-domain-for-static-website-on-aws-s3-via-route-53-and-cloudformation-34cn"&gt;Module 3&lt;/a&gt; we also covered the topic of Route 53 hosted zone. Please follow Step 4 in the following &lt;a href="https://dev.to/tiamatt/aws-project-module-3-use-your-custom-domain-for-static-website-on-aws-s3-via-route-53-and-cloudformation-34cn"&gt;link&lt;/a&gt; to configure a hosted zone for your domains.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for Route 53 hosted zone from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/2-route53-hosted-zone-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4. Request public SSL/TLS certificate from AWS Certificate Manager (ACM) for our domain name and all its subdomains&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In the previous three modules, you may have observed that both our Amazon S3 website endpoint (e.g., &lt;code&gt;http://example.s3-website-us-east-1.amazonaws.com&lt;/code&gt;) and the custom domain name URL (e.g., &lt;code&gt;http://example.com&lt;/code&gt;) were using an unsecured HTTP protocol. However it's essential to always protect your websites with HTTPS, even if they don't handle sensitive information. &lt;/p&gt;

&lt;p&gt;📌 Note: HTTPS is crucial for protecting the communication between your websites and users' browsers. It prevents intruders, including malicious attackers and intrusive companies, from tampering with the data. Intruders can exploit unprotected communications to deceive users, steal sensitive information, install malware, or inject unwanted ads. They can exploit any resource, such as images, cookies, scripts, or HTML, that travels between your websites and users. These intrusions can happen anywhere in the network, like a user's device, a Wi-Fi hotspot, or a compromised ISP.&lt;/p&gt;

&lt;p&gt;Basically  &lt;strong&gt;HTTPS&lt;/strong&gt; (Hypertext Transfer Protocol Secure) is a &lt;strong&gt;combination of the HTTP with the SSL/TLS&lt;/strong&gt; (Secure Socket Layer / Transport Layer Security) protocol.&lt;/p&gt;

&lt;p&gt;AWS provides &lt;strong&gt;AWS Certificate Manager (ACM)&lt;/strong&gt; service that handles the complexity of creating, storing, and renewing public and private SSL/TLS certificates and keys that protect your AWS websites and applications. You can provide certificates for your integrated AWS services either by issuing them directly with ACM or by importing third-party certificates into the ACM management system.&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%2F8d1jygwkm8wp923tgmgy.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%2F8d1jygwkm8wp923tgmgy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://www.it-cooking.com/tag/ssl/" rel="noopener noreferrer"&gt;IT Cooking website&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's request public SSL/TLS certificate from AWS Certificate Manager (ACM) for our domain name and all its subdomains to use to secure network communications and establish the identity of websites over the Internet.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for ACM certification from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/3-acm-certification-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ The following piece of code helps to create a request:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mySSLCertificate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CertificateManager::Certificate'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
      &lt;span class="na"&gt;SubjectAlternativeNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="s"&gt;.example.com&lt;/span&gt; &lt;span class="c1"&gt;# request a wildcard certificate for all subdomains&lt;/span&gt;
      &lt;span class="na"&gt;DomainValidationOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt; &lt;span class="c1"&gt;# DNS record for the root domain&lt;/span&gt;
          &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-HostedZoneId&lt;/span&gt;
      &lt;span class="na"&gt;ValidationMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DNS&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Before the ACM can issue a certificate for your site, it must verify that you own or control all of the domain names that you specified in your request. You can choose either &lt;strong&gt;email validation&lt;/strong&gt; or &lt;strong&gt;DNS validation&lt;/strong&gt; when you request a certificate. Let's go with &lt;code&gt;DNS validation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;2️⃣ We need to specify certificate ARN as output as we are going to use it during creation of CloudFront distributions (as will be mentioned in Step 8 of this module):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;outputCertificateArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Issued SSL certificate Arn&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;mySSLCertificate&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;3️⃣ Upload our template file for ACM certification &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/3-acm-certification-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt; to AWS CloudFormation to create a stack.&lt;/p&gt;

&lt;p&gt;Once the stack built (you need to wait for a while to let ACM verify your domain/subdomain names), under &lt;em&gt;Resources&lt;/em&gt; tab you should see your SSL certificate:&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%2Ffusl06eqdemkl9a8nh4t.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%2Ffusl06eqdemkl9a8nh4t.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find certificate ARN:&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%2F52zfmomlwivmm4p3x6b1.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%2F52zfmomlwivmm4p3x6b1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you navigate to AWS Certificate Manager (ACM), then go to &lt;em&gt;List certificates&lt;/em&gt;,  you will see your newly created certificate and domains it covers - &lt;code&gt;example.com&lt;/code&gt; and &lt;code&gt;*.example.com&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%2Fbltqpwyqtrkohk1t5zfw.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%2Fbltqpwyqtrkohk1t5zfw.png" alt="Image description"&gt;&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%2Fny5ulqwm4p6z7tbetxyc.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%2Fny5ulqwm4p6z7tbetxyc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5. Create an Origin Access Identity (OAI) as special CloudFront user&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As we mentioned before, we kept our S3 buckets private which means buckets don't have any public access. Thus in order to let CloudFront access static content from S3 bucket, we must either set an Origin Access Identity (OAI) or permit public access to S3 bucket. However, the latter choice poses a significant security risk, which is why we prefer to opt for the OAI approach.&lt;/p&gt;

&lt;p&gt;Basically, Origin Access Identity (OAI)  as special CloudFront user that helps to prevent others from viewing your S3 content by simply using the direct URL for the content. By using OAI, we can prevent others from accessing the files using Amazon S3 URLs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for CloudFront from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following piece of code helps to create an OIA:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCloudFrontOAI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::CloudFrontOriginAccessIdentity'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CloudFrontOriginAccessIdentityConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OAI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;origins'&lt;/span&gt;  &lt;span class="c1"&gt;# a comment to describe the origin access identity&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Easy peasy!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6. Create CloudFront distribution for subdomain that contains static website&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now buckle up for creating CloudFront distribution!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for CloudFront from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following piece of code helps to create a CloudFront distribution for subdomain:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCloudFrontDistributionForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::Distribution'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFront distribution points to S3 bucket for subdomain&lt;/span&gt;
        &lt;span class="na"&gt;Origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# info about origins for this distribution&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-subdomain.my-domain-name.s3.${AWS::Region}.amazonaws.com'&lt;/span&gt; &lt;span class="c1"&gt;# Regional domain name of S3 bucket for subdomain (outputS3RegionalDomainNameForSubomain)&lt;/span&gt;
            &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;S3Origin-my-subdomain-my-domain-name'&lt;/span&gt; &lt;span class="c1"&gt;# unique identifier of an origin access control for this origin&lt;/span&gt;
            &lt;span class="na"&gt;S3OriginConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;OriginAccessIdentity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;origin-access-identity/cloudfront/${myCloudFrontOAI}'&lt;/span&gt;
        &lt;span class="na"&gt;Aliases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# info about CNAMEs (alternate domain names), if any, for this distribution&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-subdomain.my-domain-name&lt;/span&gt;
        &lt;span class="c1"&gt;# let CloudFront replace HTTP status codes in the 4xx and 5xx range with custom error messages before returning the response to the viewer&lt;/span&gt;
        &lt;span class="na"&gt;CustomErrorResponses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ErrorCode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;403&lt;/span&gt; &lt;span class="c1"&gt;# 403 from S3 indicates that the file does not exists&lt;/span&gt;
            &lt;span class="na"&gt;ResponseCode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt; &lt;span class="c1"&gt;# HTTP status code that you want CloudFront to return to the viewer along with the custom error pag&lt;/span&gt;
            &lt;span class="na"&gt;ResponsePagePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/error.html'&lt;/span&gt; &lt;span class="c1"&gt;# path to the custom error page that you want CloudFront to return to a viewer when your origin returns the HTTP status code specified by ErrorCode, for example, /4xx-errors/403-forbidden.html&lt;/span&gt;
            &lt;span class="na"&gt;ErrorCachingMinTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# minimum amount of time, in seconds, that you want CloudFront to cache the HTTP status code specified in ErrorCode&lt;/span&gt;
        &lt;span class="na"&gt;DefaultCacheBehavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AllowedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;OPTIONS&lt;/span&gt;
          &lt;span class="na"&gt;CachedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;OPTIONS&lt;/span&gt;
          &lt;span class="na"&gt;Compress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;DefaultTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt; &lt;span class="c1"&gt;# in seconds, 1 hour&lt;/span&gt;
          &lt;span class="na"&gt;ForwardedValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;QueryString&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="na"&gt;Cookies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Forward&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
          &lt;span class="na"&gt;MaxTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt; &lt;span class="c1"&gt;# in seconds, 24 hours&lt;/span&gt;
          &lt;span class="na"&gt;MinTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# in seconds, 1 min&lt;/span&gt;
          &lt;span class="na"&gt;TargetOriginId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;S3Origin-my-subdomain-my-domain-name'&lt;/span&gt;
          &lt;span class="na"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;redirect-to-https'&lt;/span&gt; &lt;span class="c1"&gt;# 'allow-all'&lt;/span&gt;
        &lt;span class="na"&gt;DefaultRootObject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html'&lt;/span&gt; 
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# enable distribution&lt;/span&gt;
        &lt;span class="na"&gt;HttpVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt; &lt;span class="c1"&gt;# the maximum HTTP version(s) that you want viewers to use to communicate with CloudFront&lt;/span&gt;
        &lt;span class="na"&gt;PriceClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PriceClass_All&lt;/span&gt; &lt;span class="c1"&gt;# allowed values: PriceClass_100 | PriceClass_200 | PriceClass_All&lt;/span&gt;
        &lt;span class="na"&gt;ViewerCertificate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AcmCertificateArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-ACM-certificate-Arn&lt;/span&gt;
          &lt;span class="na"&gt;SslSupportMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sni-only&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let me clarify a few things.&lt;/p&gt;

&lt;p&gt;We pointed &lt;strong&gt;Origins&lt;/strong&gt; to S3 bucket for subdomain (that contains static website, such as &lt;code&gt;www.example.com&lt;/code&gt;) from which CloudFront should get the files to distribute. Here we also specified newly created CloudFront OAI (see Step 5) under &lt;strong&gt;S3OriginConfig&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;CustomErrorResponses&lt;/strong&gt; section we set up CloudFront to replace 403 HTTP status code (that indicates the file does not exists) with 404 code along with the custom error message specified in &lt;code&gt;/error.html&lt;/code&gt; page before returning the response to the viewer. &lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;DefaultCacheBehavior&lt;/strong&gt; section we set up some cache parameters such as &lt;strong&gt;AllowedMethods&lt;/strong&gt; that controls which HTTP methods CloudFront processes and forwards to our S3 bucket; &lt;strong&gt;CachedMethods&lt;/strong&gt; that caches the responses to GET, HEAD, and OPTIONS requests; &lt;strong&gt;ViewerProtocolPolicy&lt;/strong&gt; makes sure a viewer submits an HTTP request, CloudFront redirects the viewer to the new URL with HTTPS protocol.&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;ViewerCertificate&lt;/strong&gt; property please use your ACM certificate ARN (as mentioned in Step 4).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 7. Create a policy for S3 bucket for subdomain to allow OAI to access bucket content&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;While provisioning CloudFront distribution we associated our CloudFront OAI (as mentioned in Step 5) with the origin so that viewers can only access objects in our S3 bucket for subdomain (that contains static website, such as &lt;code&gt;www.example.com&lt;/code&gt;) through CloudFront. However it alone is insufficient. To ensure proper access, we must also provide permission for CloudFront OAI to read the files stored in our S3 bucket for subdomain. This can be achieved by creating a bucket policy.&lt;/p&gt;

&lt;p&gt;Additionally, for the sake of security, let's restrict any access to S3 bucket for non-SSL requests. This is an additional precautionary step as we have already configured the CloudFront distribution to automatically redirect non-SSL requests to HTTPS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for CloudFront from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following piece of code helps to create a policy for S3 bucket for subdomain:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myPolicyForS3BucketForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::BucketPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-subdomain.my-domain-name&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt;
          &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
          &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::my-subdomain.my-domain-name/*'&lt;/span&gt;
          &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;CanonicalUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myCloudFrontOAI.S3CanonicalUserId&lt;/span&gt;
        &lt;span class="c1"&gt;# deny access for non SSL access to S3 bucket&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AllowSSLRequestsOnly&lt;/span&gt; 
          &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deny&lt;/span&gt;
          &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
          &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:*'&lt;/span&gt;
          &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::my-subdomain.my-domain-name'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::my-subdomain.my-domain-name/*'&lt;/span&gt;
          &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Bool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;aws:SecureTransport'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;📌 Note, we don't need to create a policy for S3 bucket for root domain as we keep it empty and has already configured it to redirect the request to S3 bucket for subdomain.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 8. Create another CloudFront distribution for root domain that redirects requests to S3 bucket for subdomain&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This step is similar to Step 6 for creating a CloudFront distribution for a subdomain, but there is one important difference. It is important to note that there is a known issue with CloudFront to S3 redirects that can result in an "Access denied" error.&lt;/p&gt;

&lt;p&gt;📌 Important! If you have configured S3 bucket to redirect the request to another bucket, &lt;strong&gt;avoid using the regional domain name of the bucket&lt;/strong&gt; as the value of &lt;strong&gt;DomainName&lt;/strong&gt; under &lt;strong&gt;Origins&lt;/strong&gt; when configuring your CloudFront distribution. Instead, &lt;strong&gt;use S3 website endpoint&lt;/strong&gt;, as it will save you a significant amount of time and effort.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCloudFrontDistributionForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::Distribution'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# - DomainName: my-domain-name.s3.${AWS::Region}.amazonaws.com # regional domain name of S3 bucket for root domain (outputS3RegionalDomainNameForRootDomain)&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-domain-name.s3-website-${AWS::Region}.amazonaws.com'&lt;/span&gt; &lt;span class="c1"&gt;# use Amazon S3 website endpoint for root domain (outputS3WebsiteURLForRootDomain)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;📌 Important! If you are using S3 website endpoint as the value of &lt;strong&gt;DomainName&lt;/strong&gt; under &lt;strong&gt;Origins&lt;/strong&gt; then make sure to set up  &lt;strong&gt;CustomOriginConfig&lt;/strong&gt; property instead of &lt;strong&gt;S3OriginConfig&lt;/strong&gt;. It resolves the following issue: &lt;em&gt;"Resource handler returned message: Invalid request provided: The parameter Origin DomainName does not refer to a valid S3 bucket."&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCloudFrontDistributionForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::Distribution'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;....&lt;/span&gt;
           &lt;span class="na"&gt;CustomOriginConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;HTTPPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="c1"&gt;# required&lt;/span&gt;
              &lt;span class="na"&gt;HTTPSPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt; &lt;span class="c1"&gt;# required&lt;/span&gt;
              &lt;span class="na"&gt;OriginProtocolPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http-only'&lt;/span&gt;


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for CloudFront from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following piece of code helps to create create a CloudFront distribution for root domain:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCloudFrontDistributionForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::Distribution'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFront distribution points to S3 bucket for root domain&lt;/span&gt;
        &lt;span class="na"&gt;Origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# info about origins for this distribution&lt;/span&gt;
          &lt;span class="c1"&gt;# Important! Known issue with Cloudfront to s3 redirect giving Access denied error&lt;/span&gt;
          &lt;span class="c1"&gt;# see https://stackoverflow.com/questions/22740084/amazon-s3-redirect-and-cloudfront &lt;/span&gt;
          &lt;span class="c1"&gt;# - DomainName: my-domain-name.s3.${AWS::Region}.amazonaws.com # Regional domain name of S3 bucket for root domain (outputS3RegionalDomainNameForRootDomain)&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-domain-name.s3-website-${AWS::Region}.amazonaws.com&lt;/span&gt; &lt;span class="c1"&gt;# use Amazon S3 website endpoint for root domain (outputS3WebsiteURLForRootDomain)&lt;/span&gt;
            &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RedirectS3Origin-my-domain-name'&lt;/span&gt; &lt;span class="c1"&gt;# unique identifier of an origin access control for this origin&lt;/span&gt;
            &lt;span class="c1"&gt;# Important, for Amazon S3 website endpoint as DomainName need to be configured as CustomOriginConfig and NOT as S3OriginConfig&lt;/span&gt;
            &lt;span class="c1"&gt;# It resolves the following issue: "Resource handler returned message: "Invalid request provided: The parameter Origin DomainName does not refer to a valid S3 bucket.""&lt;/span&gt;
            &lt;span class="c1"&gt;# see https://stackoverflow.com/questions/40095803/how-do-you-create-an-aws-cloudfront-distribution-that-points-to-an-s3-static-ho &lt;/span&gt;
            &lt;span class="c1"&gt;# see https://github.com/hashicorp/terraform-provider-aws/issues/7847 &lt;/span&gt;
            &lt;span class="na"&gt;CustomOriginConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;HTTPPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="c1"&gt;# required&lt;/span&gt;
              &lt;span class="na"&gt;HTTPSPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt; &lt;span class="c1"&gt;# required&lt;/span&gt;
              &lt;span class="na"&gt;OriginProtocolPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http-only'&lt;/span&gt; 
        &lt;span class="na"&gt;Aliases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# info about CNAMEs (alternate domain names), if any, for this distribution&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-domain-name'&lt;/span&gt;
        &lt;span class="c1"&gt;# let CloudFront replace HTTP status codes in the 4xx and 5xx range with custom error messages before returning the response to the viewer&lt;/span&gt;
        &lt;span class="na"&gt;CustomErrorResponses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ErrorCode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;403&lt;/span&gt; &lt;span class="c1"&gt;# 403 from S3 indicates that the file does not exists&lt;/span&gt;
            &lt;span class="na"&gt;ResponseCode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt; &lt;span class="c1"&gt;# HTTP status code that you want CloudFront to return to the viewer along with the custom error pag&lt;/span&gt;
            &lt;span class="na"&gt;ResponsePagePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/error.html'&lt;/span&gt; &lt;span class="c1"&gt;# path to the custom error page that you want CloudFront to return to a viewer when your origin returns the HTTP status code specified by ErrorCode, for example, /4xx-errors/403-forbidden.html&lt;/span&gt;
            &lt;span class="na"&gt;ErrorCachingMinTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# minimum amount of time, in seconds, that you want CloudFront to cache the HTTP status code specified in ErrorCode&lt;/span&gt;
        &lt;span class="na"&gt;DefaultCacheBehavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AllowedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;OPTIONS&lt;/span&gt;
          &lt;span class="na"&gt;CachedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;OPTIONS&lt;/span&gt;
          &lt;span class="na"&gt;Compress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;DefaultTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt; &lt;span class="c1"&gt;# in seconds, 1 hour&lt;/span&gt;
          &lt;span class="na"&gt;ForwardedValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;QueryString&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="na"&gt;Cookies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Forward&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
          &lt;span class="na"&gt;MaxTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt; &lt;span class="c1"&gt;# in seconds, 24 hours&lt;/span&gt;
          &lt;span class="na"&gt;MinTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# in seconds, 1 min&lt;/span&gt;
          &lt;span class="na"&gt;TargetOriginId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RedirectS3Origin-my-domain-name'&lt;/span&gt;
          &lt;span class="na"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;redirect-to-https'&lt;/span&gt; &lt;span class="c1"&gt;# 'allow-all'&lt;/span&gt;
        &lt;span class="na"&gt;DefaultRootObject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html'&lt;/span&gt; 
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# enable distribution&lt;/span&gt;
        &lt;span class="na"&gt;HttpVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt; &lt;span class="c1"&gt;# the maximum HTTP version(s) that you want viewers to use to communicate with CloudFront&lt;/span&gt;
        &lt;span class="na"&gt;PriceClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PriceClass_All&lt;/span&gt; &lt;span class="c1"&gt;# allowed values: PriceClass_100 | PriceClass_200 | PriceClass_All&lt;/span&gt;
        &lt;span class="na"&gt;ViewerCertificate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AcmCertificateArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramACMCertificateArn&lt;/span&gt;
          &lt;span class="na"&gt;SslSupportMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sni-only&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The rest configurations are similar to configurations we applied for CloudFront distribution for subdomain (as mentioned in Step 6).&lt;/p&gt;

&lt;p&gt;So far we have added the following resources to our CloudFront template (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;):&lt;br&gt;
👉 a CloudFront Origin Access Identity (OAI)&lt;br&gt;
👉 a CloudFront distribution for subdomain&lt;br&gt;
👉 a policy for S3 bucket for subdomain&lt;br&gt;
👉 a CloudFront distribution for root domain&lt;/p&gt;

&lt;p&gt;We are done with &lt;em&gt;Resources&lt;/em&gt;, now let's output CloudFront distribution domain name for both root domain and subdomain that we are going to use as Route 53 targets (as will be mentioned in Step 9 of this module):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
  &lt;span class="na"&gt;outputCloudFrontDistributionForSubdomainId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFront distribution ID for subdomain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myCloudFrontDistributionForSubdomain&lt;/span&gt;
  &lt;span class="na"&gt;outputCloudFrontDistributionDomainNameForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFront distribution domain name for subdomain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myCloudFrontDistributionForSubdomain.DomainName&lt;/span&gt;
  &lt;span class="na"&gt;outputCloudFrontDistributionForRootDomainId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFront distribution ID for root domain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myCloudFrontDistributionForRootDomain&lt;/span&gt;
  &lt;span class="na"&gt;outputCloudFrontDistributionDomainNameForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFront distribution domain name for root domain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myCloudFrontDistributionForRootDomain.DomainName&lt;/span&gt;  


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

&lt;/div&gt;

&lt;p&gt;We are ready to create and run CloudFormation stack based on our template.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you need to understand how to create a stack in AWS Console, please read &lt;a href="https://dev.to/tiamatt/hands-on-aws-cloudformation-part-1-it-all-starts-here-5153"&gt;Hands-on AWS CloudFormation - Part 1. It All Starts Here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upload our template file for CloudFront (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/4-cloudfront-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;) to AWS CloudFormation to create a stack. Specify your ACM certificate ARN as a parameter value. Also specify your domain name and subdomain as parameters values. (I'm going to use domain name I've already registered, which is &lt;code&gt;tiamatt.com&lt;/code&gt;.) Run the stack.&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%2Felad2139vxz7285r1aey.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%2Felad2139vxz7285r1aey.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see two newly created CloudFront OAI and distributions, and the policy for S3 bucket for subdomain under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2Fdwqmbw48oyu9b7tao83k.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%2Fdwqmbw48oyu9b7tao83k.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find two CloudFront distributions - for domain and subdomain:&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%2Fg62yo3k6mewa2dvm6k8g.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%2Fg62yo3k6mewa2dvm6k8g.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are done with configuring CloudFront resources, now let's move on Route 53 resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 9. Create a Route 53 record set to route DNS traffic for root domain and subdomain to CloudFront domain&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After you create a hosted zone for your domain, such as &lt;code&gt;example.com&lt;/code&gt; (as mentioned in Step 3), you need to create records to tell the Domain Name System (DNS) how you want traffic to be routed for that domain.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for Route 53 record set from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/5-route53-record-set-stack.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ The following piece of code helps to create a Route 53 record set group to route DNS traffic to CloudFront domain for both - domain and subdomain:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# create a Route 53 record set group to route DNS traffic to CloudFront domain for both - domain and subdomain&lt;/span&gt;
  &lt;span class="na"&gt;myRoute53RecordSetGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Route53::RecordSetGroup'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Route53 record for CloudFront distributions for root domain and subdomain&lt;/span&gt;
      &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-HostedZoneId&lt;/span&gt;
      &lt;span class="na"&gt;RecordSets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# for CloudFront distributions subdomain (such as www.example.com)&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-subdomain.my-domain-name.'&lt;/span&gt;  &lt;span class="c1"&gt;# keep the dot&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A&lt;/span&gt; &lt;span class="c1"&gt;# 'A' routes traffic to an IPv4 address and some AWS resources. E.g. if the name of the hosted zone is 'example.com' and you want to use www.example.com to route traffic to your distribution, enter 'www.'&lt;/span&gt;
          &lt;span class="na"&gt;AliasTarget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;DNSName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramCloudFrontDistributionDomainNameForSubdomain&lt;/span&gt;
              &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z2FDTNDATAQYW2&lt;/span&gt; &lt;span class="c1"&gt;# DONT change! It is a magical alphanumeric ID provided by AWS team for CloudFront distribution &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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-domain-name.'&lt;/span&gt;  &lt;span class="c1"&gt;# keep the dot&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A&lt;/span&gt; &lt;span class="c1"&gt;# 'A' routes traffic to an IPv4 address and some AWS resources. E.g. if the name of the hosted zone is 'example.com' and you want to use www.example.com to route traffic to your distribution, enter 'www.'&lt;/span&gt;
          &lt;span class="na"&gt;AliasTarget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;DNSName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramCloudFrontDistributionDomainNameForRootDomain&lt;/span&gt;
              &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z2FDTNDATAQYW2&lt;/span&gt; &lt;span class="c1"&gt;# DONT change! It is a magical alphanumeric ID provided by AWS team for CloudFront distribution &lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;📌 Important! HostedZoneId specified under AliasTarget is different from hosted zone id that we have created in Route 53 for our domain (as mentioned in Step 3). Hosted zone id for CloudFront is a magical alphanumeric ID provided by AWS team. &lt;/p&gt;

&lt;p&gt;See the whole list of hosted zone ids for different AWS services &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget.html#cfn-route53-aliastarget-hostedzoneid" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;2️⃣  Upload our template file for Route 53 record sets (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module4/5-route53-record-set-stack.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;) to AWS CloudFormation to create a stack.&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see a newly created Route 53 record set group under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2F590vd3yozrvogew38jdg.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%2F590vd3yozrvogew38jdg.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find two URLs to our website - with domain name and subdomain:&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%2Fiq96oka6305496yeyfyh.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%2Fiq96oka6305496yeyfyh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are done with provisioning of AWS resources. Our infrastructure is ready. Let's test our website.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 10. Test your static website&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To verify that the website is working correctly, open a web browser and browse to &lt;code&gt;http://my-domain-name&lt;/code&gt; (such as &lt;code&gt;example.com&lt;/code&gt;). I'm using my custom domain name I've already registered, which is &lt;code&gt;tiamatt.com&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%2Fg1utpe9wyc9293566snm.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%2Fg1utpe9wyc9293566snm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please take note of the following:&lt;br&gt;
👉 Your request was redirected from root domain (&lt;code&gt;example.com&lt;/code&gt;) to subdomain (&lt;code&gt;www.example.com&lt;/code&gt;)&lt;br&gt;
👉 the website protocol is changed from HTTP to HTTPS&lt;br&gt;
👉 &lt;code&gt;index.html&lt;/code&gt; was added at the end (if you remove it, it still going to show index page by default)&lt;/p&gt;

&lt;p&gt;Also if you try to use either Amazon S3 website endpoint for &lt;em&gt;root domain&lt;/em&gt; (such as &lt;code&gt;http://example.com.s3-website-us-east-1.amazonaws.com&lt;/code&gt;)  or CloudFront distribution domain name for &lt;em&gt;root domain&lt;/em&gt; (such as &lt;code&gt;abcde12345.cloudfront.net&lt;/code&gt;) then again you will be redirected to &lt;code&gt;https://www.example.com/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you try to use Amazon S3 website endpoint for &lt;em&gt;subdomain&lt;/em&gt; (such as &lt;code&gt;http://www.example.com.s3-website-us-east-1.amazonaws.com&lt;/code&gt;), you should be redirected to &lt;strong&gt;403 Forbidden&lt;/strong&gt; page. &lt;/p&gt;

&lt;p&gt;For CloudFront distribution domain name for &lt;em&gt;subdomain&lt;/em&gt; (such as &lt;code&gt;abcde67890.cloudfront.net&lt;/code&gt;) you should see the index page with CloudFront url:&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%2Fgb09a7ic6oqed5xaiesl.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%2Fgb09a7ic6oqed5xaiesl.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cleanup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You might be charged for running resources. That is why it is important to clean all provisioned resources once you are done with the stack. By deleting a stack, all its resources will be deleted as well.&lt;/p&gt;

&lt;p&gt;📌 Note: CloudFormation won’t delete an S3 bucket that contains objects. First make sure to empty the bucket before deleting the stack. &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%2Fp6plfunlsi3rm1s7at90.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%2Fp6plfunlsi3rm1s7at90.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Congratulations on reaching this milestone!&lt;/p&gt;

&lt;p&gt;In this module, we optimized our application's performance and security while managing costs. Using an Infrastructure as Code approach through CloudFormation, we provisioned all the necessary AWS resources. This streamlined and automated the deployment process, ensuring consistency and ease of management. We set up Amazon CloudFront to work with our S3 bucket for content delivery and protection. The steps involved registering a custom domain, creating S3 buckets, configuring Route 53, obtaining SSL/TLS certificate, creating an Origin Access Identity (OAI), setting up CloudFront distributions, and routing DNS traffic using Route 53. These actions helped us achieve a more efficient and secure application setup.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>cloud</category>
      <category>cloudfront</category>
    </item>
    <item>
      <title>AWS project - Module 3. Use Your Custom Domain for Static Website Hosted on AWS S3 via Route 53 and CloudFormation</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Tue, 30 May 2023 02:04:37 +0000</pubDate>
      <link>https://dev.to/tiamatt/aws-project-module-3-use-your-custom-domain-for-static-website-on-aws-s3-via-route-53-and-cloudformation-34cn</link>
      <guid>https://dev.to/tiamatt/aws-project-module-3-use-your-custom-domain-for-static-website-on-aws-s3-via-route-53-and-cloudformation-34cn</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/tiamatt/aws-project-module-1-host-a-static-website-on-aws-s3-via-cloudformation-2pa2"&gt;Module 1&lt;/a&gt;, we have created a simple static website and hosted it on S3 bucket. To access our website we used Amazon S3 website endpoint which looked like &lt;code&gt;http://example.s3-website-us-east-1.amazonaws.com&lt;/code&gt;. However for users the link is too long and hard to remember. If you want your website appear legitimate to visitors you should use a custom domain name such as &lt;code&gt;http://example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this module I'll show you how to configure Route 53 to use your domain to access the website on S3 bucket in six simple steps via CloudFormation:&lt;br&gt;
✳️ Step 1. Register a custom domain name&lt;br&gt;
✳️ Step 2: Create an S3 bucket for your root domain and (optionally) for subdomain&lt;br&gt;
✳️ Step 3. Build and deploy static files to S3 bucket automatically using CodeBuild&lt;br&gt;
✳️ Step 4. Create Route 53 hosted zone and configure your domain&lt;br&gt;
✳️ Step 5. Create Route 53 record set to route your traffic for your domain to S3 bucket&lt;br&gt;
✳️ Step 6. Test your static website&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The high-level architecture for our project is illustrated in the diagram 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%2Fyq9z2zaddpr3p5b2d4x4.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%2Fyq9z2zaddpr3p5b2d4x4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this module we will focus on configuring S3 buckets and Route 53 records so that:&lt;br&gt;
✳️ your users will be able to use domain name (such as &lt;code&gt;example.com&lt;/code&gt;) to access your static website hosted on S3 bucket for root domain&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%2F53a1r7ffnqnchvrlfsu6.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%2F53a1r7ffnqnchvrlfsu6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✳️ (optionally) your users will be able to use subdomain (such as &lt;code&gt;www.example.com&lt;/code&gt;) to access your static website hosted on S3 bucket for root domain by redirecting here from S3 bucket for subdomain&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%2Fmuly9ixofra3itkqs0xj.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%2Fmuly9ixofra3itkqs0xj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Source code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Source code for this project is available on my GitHub profile in a public repository named &lt;strong&gt;StaticWebsiteHostingToS3&lt;/strong&gt; (check it &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;br&gt;
👉 Frontend  source code &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/frontend/app-for-aws" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
👉 Module 3 CloudFormation templates &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/cloudformation/module3" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;List of CloudFormation templates:&lt;br&gt;
1️⃣ Template for S3 buckets &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/1-static-website-hosting-stack-v2.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
2️⃣ Template for CodeBuild project &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/2-codebuild-stack-v2.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
3️⃣ Template for Route 53 hosted zone &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/3-route53-hosted-zone-stack-v1.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
4️⃣ Template for Route 53 record set &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/4-route53-record-set-stack-v1.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Initial Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Create required accounts:&lt;br&gt;
👉 AWS account&lt;br&gt;
👉 GitHub account&lt;/p&gt;

&lt;p&gt;Install required tools locally: &lt;br&gt;
👉 any IDE (personally I prefer Visual Studio Code) or any text editor &lt;br&gt;
👉 git&lt;br&gt;
❎ no need to install any package locally for our Angular project&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;AWS Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the list of AWS resources that we are going to provision with CloudFormation:&lt;br&gt;
👉 S3 bucket(s)&lt;br&gt;
👉 S3 bucket policy&lt;br&gt;
👉 CodeBuild project&lt;br&gt;
👉 IAM role for CodeBuild project&lt;br&gt;
👉 GitHub Source Credential for CodeBuild project&lt;br&gt;
👉 Route53 hosted zone&lt;br&gt;
👉 Route53 record set group(s)&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Why use domain name?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Why should you get a custom domain? Let's break it down!&lt;/p&gt;

&lt;p&gt;☑️ &lt;strong&gt;Brand Superpowers&lt;/strong&gt;: A custom domain name represents your brand and helps people easily recognize and remember you. Keep it consistent and unleash your brand's power.&lt;/p&gt;

&lt;p&gt;☑️ &lt;strong&gt;Memorable Magic&lt;/strong&gt;: The secret to success is having a domain name that sticks in people's minds like a catchy tune. The easier it is to remember, the more people will visit your site.&lt;/p&gt;

&lt;p&gt;☑️ &lt;strong&gt;Credibility Boost&lt;/strong&gt;: With a custom domain, your website becomes the ultimate credibility booster. It shows visitors that you mean business and adds a professional touch.&lt;/p&gt;

&lt;p&gt;☑️ &lt;strong&gt;Authority Quest&lt;/strong&gt;: As your domain ages, it gains super search engine authority! By creating quality content and building links, you'll level up your domain's power over time.&lt;/p&gt;

&lt;p&gt;☑️ &lt;strong&gt;Email Address&lt;/strong&gt;: The bonus perk of having a custom domain — your very own personalized email address! &lt;/p&gt;

&lt;p&gt;☑️ &lt;strong&gt;Portability&lt;/strong&gt;: With a custom domain, you can seamlessly move to different web hosting providers or website platforms while keeping the same web address. It's the ultimate flexibility that lets you adapt and expand your online presence without losing your trusted brand identity.&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%2Fw9f6ta2vrggxjwir6geh.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%2Fw9f6ta2vrggxjwir6geh.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Few things you should consider when coming up with a domain name: &lt;br&gt;
👉 Keep it short and sweet&lt;br&gt;
👉 Make it unique&lt;br&gt;
👉 Use easy to spell and pronounce name&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1. Register a custom domain name&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So you've decided to pay for a custom domain name. Congratulations on taking a significant first step towards establishing search authority, credibility, and trust!&lt;/p&gt;

&lt;p&gt;To use a domain name (such as &lt;code&gt;example.com&lt;/code&gt;), you must find a domain name that isn't already in use and register it. When you register a domain name, you reserve it for your exclusive use everywhere on the internet, typically for one year. (from AWS Developer Guide)&lt;/p&gt;

&lt;p&gt;Buying a new domain generally costs between $10 and $20 a year. Price differences depend on which registrar you buy your domain name from, and what kind of domain you're buying. Different registrars offer different packages, so it's worth shopping around to find your best fit. &lt;/p&gt;

&lt;p&gt;📌 Note: you can register a new domain with AWS, Google Domains, NameCheap, GoDaddy, HostGator,  or any other registrar. &lt;a href="https://www.forbes.com/advisor/business/software/best-domain-registrar/" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the list of &lt;strong&gt;Best Domain Registrar 2023&lt;/strong&gt; according to Forbes. &lt;/p&gt;

&lt;p&gt;Once you choose a domain registrar, all you need is simply to follow 3 general steps:&lt;/p&gt;

&lt;p&gt;✳️ Step 1. Choose a unique domain name&lt;br&gt;
✳️ Step 2. Choose a domain name registrar&lt;br&gt;
✳️ Step 3. Purchase and register your domain name&lt;/p&gt;

&lt;p&gt;Follow &lt;strong&gt;How To Register A Domain Name (2023 Guide)&lt;/strong&gt;  &lt;a href="https://www.forbes.com/advisor/business/how-register-domain-name/" rel="noopener noreferrer"&gt;link&lt;/a&gt; for general instructions. &lt;/p&gt;

&lt;p&gt;Here are the links to some registrars:&lt;/p&gt;

&lt;p&gt;👉 AWS using Amazon Route 53 &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/getting-started-s3.html#getting-started-find-domain-name" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
👉 Google Domains &lt;a href="https://domains.google/" rel="noopener noreferrer"&gt;link&lt;/a&gt; (my personal choice, I'm paying $12 annually)&lt;br&gt;
👉 GoDaddy &lt;a href="https://www.godaddy.com/domains" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
👉 HostGator &lt;a href="https://www.hostgator.com/domains" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
👉 NameCheap &lt;a href="https://www.namecheap.com/domains/" rel="noopener noreferrer"&gt;link&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%2Fd3zy03wfwld7kkfs3wof.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%2Fd3zy03wfwld7kkfs3wof.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Create an S3 bucket for your root domain and (optionally) for subdomain&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's take a step back to quickly recap domain name structure. &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%2Fse5dnlvsip6r2m0q756t.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%2Fse5dnlvsip6r2m0q756t.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Domain names have a specific structure that consists of several parts. Let's break it down:&lt;/p&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;Top-Level Domain (TLD):&lt;/strong&gt; This is the rightmost part of a domain name and represents the highest level in the domain name system hierarchy. Common examples include &lt;em&gt;.com&lt;/em&gt;, &lt;em&gt;.org&lt;/em&gt;, &lt;em&gt;.net&lt;/em&gt;, &lt;em&gt;.edu&lt;/em&gt;, &lt;em&gt;.gov&lt;/em&gt;, etc.&lt;/p&gt;

&lt;p&gt;2️⃣ &lt;strong&gt;Second-Level Domain (SLD):&lt;/strong&gt; The SLD is the part that comes before the TLD and is the main identifier of your website or brand. It typically represents the name of your organization, business, or the purpose of your website.&lt;/p&gt;

&lt;p&gt;3️⃣ &lt;strong&gt;Subdomain:&lt;/strong&gt; A subdomain is an optional part that appears before the SLD. It allows you to organize and categorize different sections or services of your website. For example, in &lt;code&gt;blog.example.com&lt;/code&gt;, "blog" is a subdomain.&lt;/p&gt;

&lt;p&gt;4️⃣ &lt;strong&gt;Third-Level Domain and beyond:&lt;/strong&gt; It is possible to have additional levels of subdomains. For instance, &lt;code&gt;support.blog.example.com&lt;/code&gt; has two levels of subdomains ("support" and "blog") before the SLD ("example") and the TLD (".com").&lt;/p&gt;

&lt;p&gt;📌 Note: &lt;strong&gt;A domain name&lt;/strong&gt; &lt;em&gt;isn’t the same&lt;/em&gt; thing as a &lt;strong&gt;uniform resource locator (URL)&lt;/strong&gt;. A URL is the full web address of a site, and while it does contain the domain name, it contains other information, too. Each URL includes the internet protocol (most commonly HTTP or HTTPS) being used to call up the page. URLs can also help point browsers to a specific file or folder being hosted on a web server (for example &lt;code&gt;https://www.my-site.com/blog?page=1&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%2F8zkgt2q4mp8rh1max7a1.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%2F8zkgt2q4mp8rh1max7a1.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now enough theory, let's talk about implementation! &lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/tiamatt/aws-project-module-1-host-a-static-website-on-aws-s3-via-cloudformation-2pa2"&gt;Module 1&lt;/a&gt; we have discussed how to provision S3 bucket to host our static website. To configure it with Route 53 we need to make some adjustments. &lt;/p&gt;

&lt;p&gt;📌 When you configure an Amazon S3 bucket for website hosting, you must &lt;strong&gt;give the bucket the same name as the record&lt;/strong&gt; that you want to use to route traffic to the bucket. For example, if you want to route traffic for &lt;code&gt;example.com&lt;/code&gt; to an S3 bucket that is configured for website hosting, the name of the bucket must be &lt;code&gt;example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you registered a new domain name, we can use it to provision an S3 bucket with the same name.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for S3 buckets from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/1-static-website-hosting-stack-v2.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ The following piece of code helps to create a new S3 bucket and configure it to host a static website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myS3BucketForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;DeletionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt; &lt;span class="c1"&gt;# keep S3 bucket when its stack is deleted&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-domain-name&lt;/span&gt; &lt;span class="c1"&gt;# use the name of your domain, such as example.com&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;IndexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;ErrorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error.html&lt;/span&gt;
      &lt;span class="na"&gt;VersioningConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# turn versioning on in case we need to rollback newly built files to older version&lt;/span&gt;
        &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;
      &lt;span class="c1"&gt;# AccessControl: PublicRead # throws an error: Bucket cannot have public ACLs set with BlockPublicAccess enabled&lt;/span&gt;
      &lt;span class="na"&gt;OwnershipControls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ObjectOwnership&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ObjectWriter&lt;/span&gt;
      &lt;span class="na"&gt;PublicAccessBlockConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;IgnorePublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;RestrictPublicBuckets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This S3 bucket for root domain will contain our static website files. Don't forget to replace &lt;code&gt;my-domain-name&lt;/code&gt; with your domain name (such as &lt;code&gt;example.com&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We will push static files to S3 bucket for root domain with the help of CodeBuild in Step 3. &lt;/p&gt;

&lt;p&gt;Once we configure Route 53 in Step 4 and Step 5, your users will be able to use &lt;code&gt;my-domain-name&lt;/code&gt; (such as &lt;code&gt;example.com&lt;/code&gt;) to access your static website hosted on S3 bucket for root domain.&lt;/p&gt;

&lt;p&gt;2️⃣ If you also want your users to be able to use subdomain (such as &lt;code&gt;www.example.com&lt;/code&gt;), to access your static website, create a second S3 bucket. &lt;/p&gt;

&lt;p&gt;📌 Our second (subdomain) bucket will not host a static website. We will keep it empty and configure it to route traffic to the first bucket. &lt;/p&gt;

&lt;p&gt;The following piece of code helps to create the second S3 bucket to redirect the traffic to the first bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myS3BucketForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;DeletionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt; &lt;span class="c1"&gt;# keep S3 bucket when its stack is deleted&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;my-subdomain.my-domain-name&lt;/span&gt; &lt;span class="c1"&gt;# use the name of subdomain with domain, such as www.example.com&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;RedirectAllRequestsTo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Configure the bucket to route traffic to the S3 bucket for root domain&lt;/span&gt;
          &lt;span class="na"&gt;HostName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForRootDomain&lt;/span&gt;
          &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
      &lt;span class="na"&gt;AccessControl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BucketOwnerFullControl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 Note: &lt;code&gt;RedirectAllRequestsTo&lt;/code&gt; property of S3 bucket helps to configure second (subdomain) bucket to route traffic to the first (root domain) bucket.&lt;/p&gt;

&lt;p&gt;You can use CloudFormation &lt;code&gt;Condition&lt;/code&gt; function to provision second (subdomain) bucket only if a subdomain was specified as a parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paramSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OPTIONAL. Specify a subdomain (such as 'www' or 'apex' for www.example.com or apex.example.com). You can leave it empty to skip.&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www&lt;/span&gt;

&lt;span class="na"&gt;Conditions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# HasSubdomainName is false if paramSubdomain value is empty string&lt;/span&gt;
  &lt;span class="na"&gt;HasSubdomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Not&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;!Equals&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;paramSubdomain&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;]]&lt;/span&gt; 

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;myS3BucketForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HasSubdomainName&lt;/span&gt; &lt;span class="c1"&gt;# skip this resource if paramSubdomain value is empty string&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;3️⃣ The next step is to create a bucket policy for the first (root domain) bucket.&lt;/p&gt;

&lt;p&gt;We need to give a public read access to S3 bucket objects. Here is the policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myPolicyForS3BucketForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::BucketPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForRootDomain&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublicReadForGetBucketObjects&lt;/span&gt;
            &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${myS3BucketForRootDomain.Arn}/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4️⃣ We are done with &lt;em&gt;Resources&lt;/em&gt;, now let's output Amazon S3 website endpoint for both root domain and subdomain to make it easier to navigate using the link once the stack is built.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;outputS3WebsiteURLForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Amazon S3 website endpoint for root domain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForRootDomain.WebsiteURL&lt;/span&gt;
  &lt;span class="na"&gt;outputS3WebsiteURLForSubomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HasSubdomainName&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Amazon S3 website endpoint for subdomain&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myS3BucketForSubdomain.WebsiteURL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5️⃣ We are ready to create and run CloudFormation stack based on our template. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you need to understand how to create a stack in AWS Console, please read &lt;a href="https://dev.to/tiamatt/hands-on-aws-cloudformation-part-1-it-all-starts-here-5153"&gt;Hands-on AWS CloudFormation - Part 1. It All Starts Here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upload our template file for S3 buckets (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/1-static-website-hosting-stack-v2.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;) to AWS CloudFormation to create a stack. Specify your domain name and subdomain as parameters values. (I'm going to use domain name I've already registered, which is &lt;code&gt;tiamatt.com&lt;/code&gt;.) Run the stack.&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%2Fozzt6lr8wcrpjpc0npll.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%2Fozzt6lr8wcrpjpc0npll.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see two newly created buckets and the policy under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2F2vph8o41euvbh7o6jpf2.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%2F2vph8o41euvbh7o6jpf2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find two URLs to our website - for domain and subdomain:&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%2F8p6zhwola9gg5stb7n8o.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%2F8p6zhwola9gg5stb7n8o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently, if you navigate to &lt;code&gt;http://my-domain-name.com.s3-website-us-east-1.amazonaws.com&lt;/code&gt; using Amazon S3 website endpoint for root domain then you should get 404 error as we haven't deployed static content to S3 bucket yet.&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%2Fw3jqvmb2h6i4etu5p9hx.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%2Fw3jqvmb2h6i4etu5p9hx.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To resolve this issue we need to deploy the static content to our S3 bucket for root domain (see Step 3).&lt;/p&gt;

&lt;p&gt;If you navigate to &lt;code&gt;http://www.my-domain-name.com.s3-website-us-east-1.amazonaws.com&lt;/code&gt; using Amazon S3 website endpoint for subdomain, then your link will be changed to &lt;code&gt;http://my-domain-name.com&lt;/code&gt; with &lt;em&gt;This site can’t be reached&lt;/em&gt; error. To resolve this issue we need to configure Route 53 (see Step 4 and Step 5).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3. Build and deploy static files to S3 bucket automatically using CodeBuild&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2"&gt;Module 2&lt;/a&gt; we have discussed how to automate the build of a static website on AWS S3 via CodeBuild step by step. Please follow the guide to provision CodeBuild project and run CloudFormation stack for CodeBuild.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for CodeBuild project from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/2-codebuild-stack-v2.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📌 Note: you don't need to know anything about Angular or install NodeJS, NPM and Angular CLI locally. CodeBuild project will automate the build of Angular project and safe built files for a static website on S3 bucket for root domain.&lt;/p&gt;

&lt;p&gt;1️⃣ When you run the stack, change the value of &lt;em&gt;paramS3BucketNameForRootDomain&lt;/em&gt; input parameter to name that you used building a stack for S3 bucket for root domain (such as &lt;code&gt;example.com&lt;/code&gt;). Then run the stack. And don't forget to pass your GitHub access token as value of &lt;em&gt;paramPersonalGitHubAccessToken&lt;/em&gt; input parameter.&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%2F54bhpasbqlujad8cthtz.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%2F54bhpasbqlujad8cthtz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see a newly created CodeBuild project and its role under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2Fyyei3vge9cfr5zx25qzl.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%2Fyyei3vge9cfr5zx25qzl.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find our newly create CodeBuild project under CodeBuild -&amp;gt; Build projects:&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%2Fxga7prg9ij60eilclexj.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%2Fxga7prg9ij60eilclexj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2️⃣ Update frontend source code and watch how it will be built automatically. &lt;/p&gt;

&lt;p&gt;Open any IDE or notepad on your machine and make some changes in &lt;code&gt;frontend\app-for-aws\src\app\app.component.html&lt;/code&gt; file (it's totally up to you). I want to change the title 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="c"&gt;&amp;lt;!-- Resources --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Good news, everyone! Testing Module 3!&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fe7ndx2e370c4l91skm5y.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%2Fe7ndx2e370c4l91skm5y.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Push the changes to remote repo. Then navigate to our CodeBuild project on AWS Console, you should see that build was triggered automatically and its status is &lt;em&gt;in progress&lt;/em&gt;. Wait till the status has changed to &lt;em&gt;succeeded&lt;/em&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%2Fgxh8q2gnxdcgjrwwbu97.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%2Fgxh8q2gnxdcgjrwwbu97.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to the browser and use Amazon S3 website endpoint for root domain to navigate to the website:&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%2Fny7emiz1jenou5ikcxu8.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%2Fny7emiz1jenou5ikcxu8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📌 Note: Amazon S3 website endpoint for subdomain will not work as we haven't configured Route 53 yet (see Step 4 and Step 5).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4. Create Route 53 hosted zone and configure your domain&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;📌 Note:  When you register your domain using AWS registrar, Route 53 automatically creates a hosted zone with the same name. Skip this step if you have already registered your domain with AWS.&lt;/p&gt;

&lt;p&gt;Let's take a step back to quickly recap what Route 53, hosted zones and records are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Route 53&lt;/strong&gt; is &lt;em&gt;Domain Name System (DNS) web service&lt;/em&gt; provided by AWS team. It offers domain registration, DNS routing, and health checking services (from AWS FAQs):&lt;/p&gt;

&lt;p&gt;👉 With Route 53, you can create and manage your public DNS records. Like a phone book, Route 53 lets you manage the IP addresses listed for your domain names in the Internet’s DNS phone book. &lt;/p&gt;

&lt;p&gt;👉 Route 53 also answers requests to translate specific domain names like into their corresponding IP addresses like &lt;em&gt;192.0.2.1&lt;/em&gt;. You can use Route 53 to create DNS records for a new domain or transfer DNS records for an existing domain. The simple, standards-based REST API for Route 53 allows you to easily create, update and manage DNS records. &lt;/p&gt;

&lt;p&gt;👉 Route 53 additionally offers &lt;em&gt;health checks&lt;/em&gt; to monitor the health and performance of your application as well as your web servers and other resources. You can also register new domain names or transfer in existing domain names to be managed by Route 53.&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%2Fb53c1lvpihpvxs7qz0cg.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%2Fb53c1lvpihpvxs7qz0cg.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case we need to configure DNS routing to transfer internet traffic from our domain (such as &lt;code&gt;example.com&lt;/code&gt;) to Amazon S3 website endpoint (such as &lt;code&gt;http://example.s3-website-us-east-1.amazonaws.com&lt;/code&gt;) to access the website on S3.&lt;/p&gt;

&lt;p&gt;In order to configure our domain name to point to Amazon S3 website endpoint, we need to create a public hosted zone and add routing records there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hosted zone&lt;/strong&gt; is an Amazon Route 53 concept. A hosted zone is analogous to a traditional DNS zone file; it represents a &lt;em&gt;collection of records&lt;/em&gt; that can be managed together, belonging to a single parent domain name. All resource record sets within a hosted zone must have the hosted zone's domain name as a suffix. For example, the &lt;code&gt;example.com&lt;/code&gt; hosted zone may contain records named &lt;code&gt;www.example.com&lt;/code&gt;, and &lt;code&gt;www.blog.example.com&lt;/code&gt;, but not a record named &lt;code&gt;www.amazon.com&lt;/code&gt; (from AWS FAQs). &lt;/p&gt;

&lt;p&gt;Each &lt;strong&gt;record&lt;/strong&gt; contains information about how you want Route 53 to route traffic for a specific domain, and its subdomains. For example, you can specify a record to route your traffic for &lt;code&gt;example.com&lt;/code&gt; to S3 bucket, or API Gateway, or Load Balancer, or another AWS resources.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for Route 53 hosted zone from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/3-route53-hosted-zone-stack-v1.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ The following piece of code helps to create a public hosted zone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myRoute53HostedZone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Route53::HostedZone'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;HostedZoneConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My public hosted zone for example.com&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;example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 Note: A hosted zone and the corresponding domain should have &lt;strong&gt;the same name&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2️⃣ Let's output hosted zone ID that we will use for Route 53 record set configuration. Also we can get the list of all name servers (NS) where the traffic will be routed to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;outputRoute53HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Public hosted zone ID (such as Z23ABC4XYZL05B)&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myRoute53HostedZone.Id&lt;/span&gt; &lt;span class="c1"&gt;# the same as !Ref myRoute53HostedZone  &lt;/span&gt;
  &lt;span class="na"&gt;outputRoute53HostedZoneNameServers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;List of name servers for newly created public hosted zone&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="nv"&gt;myRoute53HostedZone.NameServers&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3️⃣ Upload our template file for hosted zone (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/3-route53-hosted-zone-stack-v1.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;) to AWS CloudFormation to create a stack.&lt;/p&gt;

&lt;p&gt;Once the stack built, a new public hosted zone will be provisioned.&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Resources&lt;/em&gt; tab you can find the link to hosted zone:&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%2Fgfeiv7syu6trtsl2lcqc.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%2Fgfeiv7syu6trtsl2lcqc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find the hosted zone ID for our domain and the list of all name servers (NS):&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%2Fhtenm1wq49gfs7nz3t11.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%2Fhtenm1wq49gfs7nz3t11.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you navigate to Route 53 -&amp;gt; Hosted zones, you will see your newly created hosted zone (such as &lt;code&gt;example.com&lt;/code&gt;) with two records of type NS and SOA:&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%2F4lok93tl5v9wtnygvn1b.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%2F4lok93tl5v9wtnygvn1b.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both &lt;strong&gt;a name server (NS)&lt;/strong&gt; record and &lt;strong&gt;a start of authority (SOA)&lt;/strong&gt; record are created by Route 53 automatically while provisioning a new public hosted zone. You rarely need to change these records.&lt;/p&gt;

&lt;p&gt;✳️ &lt;strong&gt;Name server (NS) record&lt;/strong&gt; lists the four name servers that are the authoritative name servers for your hosted zone.&lt;/p&gt;

&lt;p&gt;✳️ &lt;strong&gt;Start of authority (SOA) record&lt;/strong&gt; identifies the base DNS information about the domain.&lt;/p&gt;

&lt;p&gt;📌 Note: When you register your domain outside of AWS Route 53, you need to connect your DNS to Route 53.&lt;/p&gt;

&lt;p&gt;As I registered my domain on Google Domains, I'll show you how I connected my Google domain to Route 53. Similar steps can be applied for other registrar platforms. &lt;/p&gt;

&lt;p&gt;1️⃣ First, go to your Route 53 hosted zone and copy all four NS records from there:&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%2Fnj11jjj989j3rd48wznc.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%2Fnj11jjj989j3rd48wznc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2️⃣ Now, log into your Google Domains account. Then click on &lt;strong&gt;My domains&lt;/strong&gt; -&amp;gt; select &lt;strong&gt;DNS&lt;/strong&gt; from menu -&amp;gt; click on &lt;strong&gt;Custom name servers (Active)&lt;/strong&gt; tab. Click on &lt;strong&gt;Manage name servers&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%2Fw17vqgaz4dvjojwv09ub.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%2Fw17vqgaz4dvjojwv09ub.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Add another name server&lt;/strong&gt; and paste four NS records from the Route 53 Record Sets panel one by one. Don't forget to &lt;strong&gt;Save&lt;/strong&gt; changes:&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%2F6hfx2hc3ck82sssj7aep.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%2F6hfx2hc3ck82sssj7aep.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ta-da! Now our domain has been connected to Route 53.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5. Create Route 53 record set to route your traffic for your domain to S3 bucket&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After you create a hosted zone for your domain, such as &lt;code&gt;example.com&lt;/code&gt;, you need to create records to tell the Domain Name System (DNS) how you want traffic to be routed for that domain.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full CloudFormation template for Route 53 record set from &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/4-route53-record-set-stack-v1.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ In our case we need to create a record to route internet traffic from root domain (such as &lt;code&gt;example.com&lt;/code&gt;) to S3 bucket that hosts static website. &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%2F53a1r7ffnqnchvrlfsu6.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%2F53a1r7ffnqnchvrlfsu6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following piece of code helps to create a new record for S3 bucket for root domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myRoute53RecordSetGroupForRootDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Route53::RecordSetGroup'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramHostedZoneId&lt;/span&gt;
      &lt;span class="na"&gt;RecordSets&lt;/span&gt;&lt;span class="pi"&gt;:&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;example.com&lt;/span&gt; &lt;span class="c1"&gt;# point to an S3 bucket with root domain in the same account (such as example.com)&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A&lt;/span&gt; &lt;span class="c1"&gt;# 'A' routes traffic to an IPv4 address and some AWS resources&lt;/span&gt;
          &lt;span class="na"&gt;AliasTarget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;DNSName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;s3-website-${AWS::Region}.amazonaws.com&lt;/span&gt;
              &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!FindInMap&lt;/span&gt; &lt;span class="c1"&gt;# note, that it is different from paramHostedZoneId - this hosted zone is for region that you created the bucket in!&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RegionMap&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Region'&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;S3HostedZoneId&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 Note: &lt;strong&gt;HostedZoneId&lt;/strong&gt; specified under &lt;strong&gt;AliasTarget&lt;/strong&gt; is different from hosted zone id that we have created in Route 53 for our domain in Step 4. Hosted zone id for S3 is a magical alphanumeric ID provided by AWS team. It varies base on the region that you created the bucket in. That's why we used mapping to map S3 hosted zone id to region:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Mappings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;RegionMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# based on https://docs.aws.amazon.com/general/latest/gr/s3.html#s3_website_region_endpoints&lt;/span&gt;
    &lt;span class="na"&gt;us-east-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z3AQBSTGFYJSTF&lt;/span&gt;
    &lt;span class="na"&gt;us-west-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z2F56UZL2M1ACD&lt;/span&gt;
    &lt;span class="na"&gt;us-west-2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z3BJ6K6RIION7M&lt;/span&gt;
    &lt;span class="na"&gt;eu-central-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z21DNDUVLTQW6Q&lt;/span&gt;
    &lt;span class="na"&gt;eu-west-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z1BKCTXD74EZPE&lt;/span&gt;
    &lt;span class="na"&gt;ap-southeast-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z3O0J2DXBE1FTB&lt;/span&gt;
    &lt;span class="na"&gt;ap-southeast-2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z1WCIGYICN2BYD&lt;/span&gt;
    &lt;span class="na"&gt;ap-northeast-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z2M4EHUR26P7ZW&lt;/span&gt;
    &lt;span class="na"&gt;sa-east-1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;S3HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Z31GFT0UA1I2HV&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the whole list of hosted zone ids for S3 &lt;a href="https://docs.aws.amazon.com/general/latest/gr/s3.html#s3_website_region_endpoints" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;📌 Also note for future reference that hosted zone id depends on your alias target. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;for Global Accelerator accelerator specify Z2BJ6XQ5FK7U4H&lt;/li&gt;
&lt;li&gt;for  CloudFront distribution specify  Z2FDTNDATAQYW2&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the whole list of hosted zone ids for different AWS services &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget.html#cfn-route53-aliastarget-hostedzoneid" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;2️⃣ Optionally, we can create a new record for S3 subdomain  bucket which will redirect traffic to S3 bucket for root domain.&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%2Fmuly9ixofra3itkqs0xj.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%2Fmuly9ixofra3itkqs0xj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following piece of code helps to create a new record for S3 bucket for subdomain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myRoute53RecordSetGroupForSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HasSubdomainName&lt;/span&gt; &lt;span class="c1"&gt;# skip this resource if paramSubdomain value is empty string&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Route53::RecordSetGroup'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramHostedZoneId&lt;/span&gt;
      &lt;span class="na"&gt;RecordSets&lt;/span&gt;&lt;span class="pi"&gt;:&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;www.example.com&lt;/span&gt; &lt;span class="c1"&gt;# point to an S3 bucket with subdomain in the same account (such as www.example.com)&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A&lt;/span&gt; &lt;span class="c1"&gt;# 'A' routes traffic to an IPv4 address and some AWS resources&lt;/span&gt;
          &lt;span class="na"&gt;AliasTarget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;DNSName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;s3-website-${AWS::Region}.amazonaws.com&lt;/span&gt; 
              &lt;span class="na"&gt;HostedZoneId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!FindInMap&lt;/span&gt; &lt;span class="c1"&gt;# note, that it is different from paramHostedZoneId - this hosted zone is for region that you created the bucket in!&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RegionMap&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Region'&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;S3HostedZoneId&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3️⃣ Upload our template file for Route 53 record sets (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module3/4-route53-record-set-stack-v1.yaml" rel="noopener noreferrer"&gt;link&lt;/a&gt;) to AWS CloudFormation to create a stack.&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see two newly created Route 53 record set groups under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2Ffv0g7lrepoko6v6vbqk0.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%2Ffv0g7lrepoko6v6vbqk0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find two URLs to our website - with domain name and subdomain:&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%2F3fcthp82aqknxut5lt5r.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%2F3fcthp82aqknxut5lt5r.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are done with provisioning of AWS resources. Our infrastructure is ready. Let's test our website.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6. Test your static website&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To verify that the website is working correctly, open a web browser and browse to &lt;code&gt;http://my-domain-name&lt;/code&gt; (such as &lt;code&gt;example.com&lt;/code&gt;). I'm using my custom domain name I've already registered, which is &lt;code&gt;tiamatt.com&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%2F389jxqix7t9bcubbhaf8.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%2F389jxqix7t9bcubbhaf8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's check our subdomain. Browse to &lt;code&gt;http://your-subdomain.my-domain-name&lt;/code&gt; (such as &lt;code&gt;www.example.com&lt;/code&gt;). You should see that it will redirect your request to your &lt;code&gt;http://my-domain-name&lt;/code&gt; domain (such as &lt;code&gt;example.com&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cleanup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You might be charged for running resources. That is why it is important to clean all provisioned resources once you are done with the stack. By deleting a stack, all its resources will be deleted as well.&lt;/p&gt;

&lt;p&gt;📌 Note: CloudFormation won’t delete an S3 bucket that contains objects. First make sure to empty the bucket before deleting the stack. &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%2Fjchikoy7jp3z500x0t6e.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%2Fjchikoy7jp3z500x0t6e.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Well done on reaching this point!&lt;/p&gt;

&lt;p&gt;In this module we explained how to configure Route 53 to use a custom domain for your S3-hosted static website. Say goodbye to long, hard-to-remember links! In six simple steps, we registered the domain, set up S3 buckets, deployed files with CodeBuild, configured Route 53, and tested website. &lt;/p&gt;

&lt;p&gt;By using a custom domain, we can boost professionalism and make our site more accessible.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformatio</category>
      <category>cloud</category>
      <category>route53</category>
    </item>
    <item>
      <title>AWS project - Module 2. Automate the build of a Static Website Hosted on AWS S3 via CodeBuild and CloudFormation</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Thu, 18 May 2023 01:27:39 +0000</pubDate>
      <link>https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2</link>
      <guid>https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/tiamatt/aws-project-module-1-host-a-static-website-on-aws-s3-via-cloudformation-2pa2"&gt;Module 1&lt;/a&gt;, we have created a simple static web app and hosted it on S3 bucket. However we took baby steps to deploy our static content to S3 bucket &lt;em&gt;manually&lt;/em&gt;. Ideally we want to use a tool that would rebuild the source code &lt;em&gt;every time&lt;/em&gt; a code change is pushed to the repository and deploy built files to S3 bucket &lt;em&gt;automatically&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this module I'll show you how to automate the build and deployment via AWS CodeBuild in six simple steps:&lt;br&gt;
👉 Step 1. Create buildspec YAML file&lt;br&gt;
👉 Step 2. Provide CodeBuild with access to GitHub repo&lt;br&gt;
👉 Step 3. Configure how AWS CodeBuild builds your source code &lt;br&gt;
👉 Step 4. Create IAM role for CodeBuild project&lt;br&gt;
👉 Step 5. Run the CloudFormation stack&lt;br&gt;
👉 Step 6. Update frontend source code and watch how it will be built automatically&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The high-level architecture for our project is illustrated in the diagram 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%2Fjk5l77vr3ise73jk3zcb.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%2Fjk5l77vr3ise73jk3zcb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Source code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Source code for this project is available on GitHub in a public &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3" rel="noopener noreferrer"&gt;StaticWebsiteHostingToS3&lt;/a&gt; repository.&lt;br&gt;
👉 Check for frontend  source code &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/frontend/app-for-aws" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
👉 Check for Module 2 CloudFormation templates &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/cloudformation/module2" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Initial Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Install required tools: &lt;br&gt;
👉 any IDE (personally I prefer Visual Studio Code) or text editor&lt;br&gt;
👉 git&lt;/p&gt;

&lt;p&gt;Note, you don't need to know anything about Angular or install NodeJS, NPM and Angular CLI locally. We will configure CodeBuild project with all necessary installation packages.  &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;AWS Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the list of AWS resources that we are going to create:&lt;br&gt;
👉 CodeBuild project&lt;br&gt;
👉 IAM role for CodeBuild project&lt;br&gt;
👉 GitHub Source Credential for CodeBuild project&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1. Create buildspec YAML file&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Buildspec&lt;/em&gt; is a collection of build commands and related settings, in YAML format, that CodeBuild uses to run a build. We need to create and include a buildspec YAML file as part of the source code of frontend app. In our case the file is stored in the root directory of Angular app, which is &lt;em&gt;frontend/app-for-aws&lt;/em&gt; folder.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Shortcut: get buildspec YAML file &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/frontend/app-for-aws/buildspec.yml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ &lt;em&gt;Install&lt;/em&gt; phase of buildspec file &lt;/p&gt;

&lt;p&gt;Use the &lt;em&gt;install&lt;/em&gt; phase only for installing packages in the build environment. In order to run the production build of our Angular web app, CodeBuild server needs Angular installation. Meanwhile Angular depends on NodeJS and NPM. Thus we need to specify NodeJS installation as a runtime and add commands to install Angular CLI and node modules based on dependencies specified in &lt;em&gt;package.json&lt;/em&gt; (&lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/frontend/app-for-aws/package.json" rel="noopener noreferrer"&gt;link&lt;/a&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runtime-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;nodejs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18&lt;/span&gt;
    &lt;span class="na"&gt;commands&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 Install Angular CLI and all node_modules&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd $CODEBUILD_SRC_DIR/frontend/app-for-aws&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install &amp;amp;&amp;amp; npm install -g @angular/cli&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;2️⃣ &lt;em&gt;Build&lt;/em&gt; phase of buildspec file &lt;/p&gt;

&lt;p&gt;Use the &lt;em&gt;build&lt;/em&gt; phase for commands that CodeBuild runs during the build. In our case we need to navigate to our frontend folder and run the production build:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&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 Build process started now&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd $CODEBUILD_SRC_DIR/frontend/app-for-aws&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ng build --configuration=production&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;3️⃣ &lt;em&gt;Post build&lt;/em&gt; phase of buildspec file&lt;/p&gt;

&lt;p&gt;Use the &lt;em&gt;post_build&lt;/em&gt; phase for commands that CodeBuild runs after the build. Here we can list all the files of newly created &lt;em&gt;dist/app-for-aws&lt;/em&gt; folder for easy debugging:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;post_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&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 Build process finished, upload artifacts to S3 bucket&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd dist/app-for-aws&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ls -la&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;4️⃣ &lt;em&gt;Artifacts&lt;/em&gt; of buildspec file&lt;/p&gt;

&lt;p&gt;Artifacts represent information about where CodeBuild can find the build output and how CodeBuild prepares it for uploading to the S3 output bucket. Here we need to specify the path to &lt;em&gt;dist&lt;/em&gt; folder so that CodeBuild won't deploy a whole frontend project to S3 bucket but files for prod build only: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

 &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;base-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frontend/app-for-aws/dist*'&lt;/span&gt;
  &lt;span class="na"&gt;discard-paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/*'&lt;/span&gt;  


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;discard-paths&lt;/code&gt; helps to make sure to put all the files in the root folder instead of subfolder. In our case it will take all the files (including &lt;em&gt;index.hmtl&lt;/em&gt; file) from &lt;em&gt;dist/app-for-aws&lt;/em&gt; folder and put them in the root of S3 bucket.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2. Provide CodeBuild with access to GitHub repo&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;1️⃣ In order to access source code for frontend located on your GitHub account, CodeBuild needs some access privilege. The easiest and safest way to grand CodeBuild an access to GitHub is to create a personal access token. &lt;/p&gt;

&lt;p&gt;Navigate to your GitHub and click your profile photo, then click &lt;strong&gt;Settings&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%2Fuk47xvwvsifo3m91rdtf.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%2Fuk47xvwvsifo3m91rdtf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the left sidebar, scroll down and click &lt;strong&gt;Developer settings&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%2Fpiriqhwxsxn9jh4do2ty.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%2Fpiriqhwxsxn9jh4do2ty.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the left sidebar, under &lt;strong&gt;Personal access tokens&lt;/strong&gt;, click &lt;strong&gt;Tokens (classic)&lt;/strong&gt; (since &lt;em&gt;Fine-grained tokens&lt;/em&gt; is in beta and might not be compatible with AWS CodeBuild yet), then click &lt;strong&gt;Generate new token&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%2Fpp54wa2yqtkkxc38nt4z.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%2Fpp54wa2yqtkkxc38nt4z.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the scopes you'd like to grant this token. Note, as I've already generated a token, my screenshots shows &lt;em&gt;Edit&lt;/em&gt;. Please ignore.&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%2Ft16xidbrz31cmkkfahdi.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%2Ft16xidbrz31cmkkfahdi.png" alt="Image description"&gt;&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%2Fofpurojlw44b301ph427.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%2Fofpurojlw44b301ph427.png" alt="Image description"&gt;&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%2Fu1oii2p8oz0d43jb1jmt.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%2Fu1oii2p8oz0d43jb1jmt.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Generate token&lt;/strong&gt;. Copy the new token - we will need it while running a CloudFormation stack.&lt;/p&gt;

&lt;p&gt;2️⃣ In CloudFormation template we are going to create a new &lt;em&gt;AWS::CodeBuild::SourceCredential&lt;/em&gt; resource. Here we provide information about the credentials for a GitHub. &lt;/p&gt;

&lt;p&gt;It is recommend to use AWS Secrets Manager to store our credentials. But for the sake of simplicity (let's take baby steps) we will pass a newly generated GitHub access token as a CloudFormation parameter for now. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paramPersonalGitHubAccessToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;MinLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;ConstraintDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Personal GitHub access token is missing&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provide your personal GitHub access token for&lt;/span&gt; 
&lt;span class="s"&gt;CodeBuild to access your GitHub repo&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildSourceCredential&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::SourceCredential&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AuthType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PERSONAL_ACCESS_TOKEN&lt;/span&gt;
      &lt;span class="na"&gt;ServerType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GITHUB&lt;/span&gt;
      &lt;span class="na"&gt;Token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramPersonalGitHubAccessToken&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3. Configure how AWS CodeBuild builds your source code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;AWS::CodeBuild::Project&lt;/em&gt; resource configures how AWS CodeBuild builds your source code, such as where to get the source code and which build environment to use.&lt;/p&gt;

&lt;p&gt;First, let's give a friendly name and add description for our CodeBuild project:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&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;westworld-codebuild-for-website-hosting&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CodeBuild project for automatically build of static website hosted on s3&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;Source&lt;/strong&gt; code settings for the CodeBuild project we need to specify the &lt;em&gt;GITHUB&lt;/em&gt; as source code's repository type, then add repo location, BuildSpec location and authorization settings for AWS CodeBuild to access the source code to be built. Remember, in Step 2 of this module we have created &lt;code&gt;myCodeBuildSourceCredential&lt;/code&gt; Source Credential resource - we need to use it under Auth Resource so that CodeBuild can access the specified GitHub repo.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="s"&gt;Source&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GITHUB&lt;/span&gt;
        &lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/your-account/StaticWebsiteHostingToS3.git&lt;/span&gt;
        &lt;span class="na"&gt;GitCloneDepth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="na"&gt;BuildSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend/app-for-aws/buildspec.yml&lt;/span&gt;
        &lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myCodeBuildSourceCredential&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OAUTH&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;Triggers&lt;/strong&gt; code settings for the CodeBuild project we want to enable AWS CodeBuild to begin automatically rebuilding the source code every time a code change is pushed to the repository. Basically we configured CodeBuild to listen to any git pushes to &lt;em&gt;main&lt;/em&gt; branch to trigger the build.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="s"&gt;Triggers&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Webhook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;FilterGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EVENT&lt;/span&gt;
              &lt;span class="na"&gt;Pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PUSH&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HEAD_REF&lt;/span&gt;
              &lt;span class="na"&gt;Pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^refs/heads/main&lt;/span&gt; &lt;span class="c1"&gt;# for feature branches use: ^refs/heads/feature/.* &lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;Environment&lt;/strong&gt; code settings for the CodeBuild project we basically configured VM where CodeBuild is going to build the source code. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="s"&gt;Environment&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# use Ubuntu standard v7&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LINUX_CONTAINER&lt;/span&gt;
        &lt;span class="na"&gt;ComputeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BUILD_GENERAL1_SMALL&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;aws/codebuild/standard:7.0&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;But how do you know which container, computer and image you need? Well, based on the code source programming language and framework you can check for Docker images &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; and Available runtimes &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/available-runtimes.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we have specified NodeJS v18 in our buildspec file, we can use either &lt;code&gt;Amazon Linux 2 AArch64 standard:3.0&lt;/code&gt; or &lt;code&gt;Ubuntu standard:7.0&lt;/code&gt; image. &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%2Frrd5y2mkbu9sxb52gt5e.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%2Frrd5y2mkbu9sxb52gt5e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on selected image I can get &lt;em&gt;Image identifier&lt;/em&gt;  from &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html" rel="noopener noreferrer"&gt;this&lt;/a&gt; list.&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%2Fnzuhqqt75vmcghvfdwyi.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%2Fnzuhqqt75vmcghvfdwyi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Artifacts&lt;/strong&gt; code settings for the CodeBuild project we want to specify what to do with build files. In our case we want to put them to our S3 bucket that hosts the static website. Note, it's very important to disable encryption for our website files. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="s"&gt;Artifacts&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# drop the build artifacts of S3 bucket that hosts static website&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3&lt;/span&gt;
        &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt; &lt;span class="c1"&gt;# store the artifact in the root of the output bucket&lt;/span&gt;
        &lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:aws:s3:::westworld-codebuild-for-website-hosting&lt;/span&gt;
        &lt;span class="na"&gt;EncryptionDisabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt; &lt;span class="c1"&gt;#disable the encryption of artifacts in a build to see html pages&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;LogsConfig&lt;/strong&gt; code settings for the CodeBuild project we want to configure CloudWatch logs.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="s"&gt;LogsConfig&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;CloudWatchLogs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
          &lt;span class="na"&gt;GroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;westworld-codebuild-for-website-hosting-CloudWatchLogs&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;ServiceRole&lt;/strong&gt; code settings for the CodeBuild project we need to create a new role with proper access - see Step 4.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="s"&gt;ServiceRole&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myCodeBuildProjectRole&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4. Create IAM role for CodeBuild project&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Final step for our CloudFormation template is to create an appropriate IAM role for CodeBuild project to get an access to S3 bucket where static website is hosted and to CloudWatch logs to stream the logs while building the project. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCodeBuildProjectRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RoleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;role-for-westworld-codebuild-for-website-hosting&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole'&lt;/span&gt;
            &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;codebuild.amazonaws.com&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy-for-westworld-codebuild-for-website-hosting&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="c1"&gt;# statement to create/stream CloudWatch&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:CreateLogGroup&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:CreateLogStream&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:PutLogEvents&lt;/span&gt;
                &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:westworld-codebuild-for-website-hosting-CloudWatchLogs&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:westworld-codebuild-for-website-hosting-CloudWatchLogs:*&lt;/span&gt;
              &lt;span class="c1"&gt;# statement to access S3 bucket that hosts static website (CodeBuild will save Artifacts there)&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:PutObject&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:GetObject&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:GetObjectVersion&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:GetBucketAcl&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:GetBucketLocation&lt;/span&gt;
                &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:s3:::your-bucket-name&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:s3:::your-bucket-name/*&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5. Run the CloudFormation stack&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now we are ready to create and run CloudFormation stack based on our template for CodeBuild (see the whole template &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module2/2-codebuild-stack-v1.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Note, we have already created and run CloudFormation stack for static website and S3 - check &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module2/1-static-website-hosting-stack-v1.yaml" rel="noopener noreferrer"&gt;this&lt;/a&gt; template for provisioning S3 bucket and step by step guide in &lt;a href="https://dev.to/tiamatt/aws-project-module-1-host-a-static-website-on-aws-s3-via-cloudformation-2pa2"&gt;Module 1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I prefer to run the stack from AWS Console as it provides an end-to-end overview of a process flow. But you can always run a stack using the AWS CLI (&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-cli.html" rel="noopener noreferrer"&gt;here is how&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Upload our template file to create a stack.&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%2F8mk2i52d1ud8nzp1i72c.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%2F8mk2i52d1ud8nzp1i72c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Change the value of  &lt;em&gt;paramStaticWebsiteHostingBucketName&lt;/em&gt; input parameter to name that you used building a stack for S3 bucket. Then run the stack. And dont forget to pass your GitHub access token as value of &lt;em&gt;paramPersonalGitHubAccessToken&lt;/em&gt; input parameter.&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see a newly created CodeBuild project and its role under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2F2ha7wfkiabikiydpe5jb.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%2F2ha7wfkiabikiydpe5jb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find our newly create CodeBuild project under &lt;em&gt;CodeBuild&lt;/em&gt; -&amp;gt; &lt;em&gt;Build projects&lt;/em&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%2Fp2k2aaukxdz8t2qmu0k8.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%2Fp2k2aaukxdz8t2qmu0k8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's navigate to our website. Remember, we have created our stack for S3 bucket - under &lt;em&gt;Outputs&lt;/em&gt; tab you can find the link to our website (note, your link might be different from mine):&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%2Fy1n5vhh8iir73mb7tarm.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%2Fy1n5vhh8iir73mb7tarm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila! Our static website is up and running! It was built and deployed to S3 with the initial provisioning of our CodeBuild project.&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%2Flg6rjnqiyj7hag3lhekt.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%2Flg6rjnqiyj7hag3lhekt.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6. Update frontend source code and watch how it will be built automatically&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now it's time to check out build automation. &lt;/p&gt;

&lt;p&gt;1️⃣ Let's make a small change (it's totally up to you) in our source code for static website.  &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%2Fx9tsvbeqzrfet9f3orl4.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%2Fx9tsvbeqzrfet9f3orl4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need to clone frontend project (unless you want to use your own project built in React, Vue or vanilla JavaScript) - check for frontend  source code &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/frontend/app-for-aws" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's make some changes in &lt;code&gt;frontend\app-for-aws\src\app\app.component.html&lt;/code&gt; file. I want to change the title from &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="c"&gt;&amp;lt;!-- Resources --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Good news, everyone!&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;to &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="c"&gt;&amp;lt;!-- Resources --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Good news, everyone! CodeBuild project is up and running!&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Open html file in any IDE or text editor. Make and save the changes.&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%2F9f7y1rbayye7m34p9pwz.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%2F9f7y1rbayye7m34p9pwz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's push the changes to remote repo. Open your terminal and run the following commands:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# navigate to the project&lt;/span&gt;
&lt;span class="s"&gt;cd StaticWebsiteHostingToS3&lt;/span&gt;
&lt;span class="c1"&gt;# stage the changes&lt;/span&gt;
&lt;span class="s"&gt;git add .&lt;/span&gt;
&lt;span class="c1"&gt;# commit the changes&lt;/span&gt;
&lt;span class="s"&gt;git commit -m"Changed the title of static website"&lt;/span&gt;
&lt;span class="c1"&gt;# push the changes to remote repo&lt;/span&gt;
&lt;span class="s"&gt;git push&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;2️⃣ Once you pushed your changes to GitHub and navigate to our CodeBuild project on AWS Console, you should see that build was triggered automatically and its status is &lt;em&gt;in progress&lt;/em&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%2Fmq1d2oy0kr5fpq9mcsgw.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%2Fmq1d2oy0kr5fpq9mcsgw.png" alt="Image description"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Click on Build run and you will see the logs:&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%2Flpceu7y3xofbauq3t5n8.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%2Flpceu7y3xofbauq3t5n8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can navigate to CloudWatch logs by clicking on &lt;em&gt;View entire log&lt;/em&gt; under &lt;em&gt;Build log&lt;/em&gt; tab:&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%2F62ke8xnd39o80rcut90r.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%2F62ke8xnd39o80rcut90r.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note, that all our logs for build were stored under newly created &lt;em&gt;westworld-codebuild-for-website-hosting-CloudWatchLogs&lt;/em&gt; log group.&lt;/p&gt;

&lt;p&gt;Also you can check our S3 bucket to see if the files were updated (look at &lt;em&gt;Last modified&lt;/em&gt; date and time).&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%2Faa55pfvnw0pq4tmfwd67.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%2Faa55pfvnw0pq4tmfwd67.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3️⃣ Once build status has changed to &lt;em&gt;Succeeded&lt;/em&gt;, go ahead and refresh your website link. You should the code changes.&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%2Fgcpfocggm4vholuir86q.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%2Fgcpfocggm4vholuir86q.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note, if you don't see the changes, probably the website was cached. You can either clean the cache or open the website in incognito mode. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cleanup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You might be charged for running resources. That is why it is important to clean all provisioned resources once you are done with the stack. By deleting a stack, all its resources will be deleted as well.&lt;/p&gt;

&lt;p&gt;Note: CloudFormation won’t delete an S3 bucket that contains objects. First make sure to empty the bucket before deleting the stack. Of course, you can automate the process by creating a Lambda function which deletes all object versions and the objects themselves. I'll try to cover that part in further article. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Congratulations on getting this far! &lt;/p&gt;

&lt;p&gt;In this module, we have provisioned CodeBuild project that automate the build of a static website on AWS S3. We took Infrastructure as Code approach to provisioning and managing our AWS resources by writing a template file and running stacks using AWS CloudFormation service.&lt;/p&gt;

&lt;p&gt;Don't forget to do your happy dance!&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%2F5lcqhw408jfcx408yzm5.gif" 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%2F5lcqhw408jfcx408yzm5.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>codebuild</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AWS project - Module 1. Host a Static Website on AWS S3 via CloudFormation</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Thu, 18 May 2023 01:25:52 +0000</pubDate>
      <link>https://dev.to/tiamatt/aws-project-module-1-host-a-static-website-on-aws-s3-via-cloudformation-2pa2</link>
      <guid>https://dev.to/tiamatt/aws-project-module-1-host-a-static-website-on-aws-s3-via-cloudformation-2pa2</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This series of articles provide a step-by-step guide on how to deploy a basic static website to an Amazon S3 (Simple Storage Service) bucket using AWS CloudFormation. Starting with a simple architecture, each module identifies gaps and upcoming issues, and gradually introduces additional layers of complexity to resolve those issues. The steps involved registering a custom domain, creating S3 buckets, configuring Route 53, obtaining SSL/TLS certificate, creating an Origin Access Identity (OAI), setting up CloudFront distributions, and routing DNS traffic using Route 53. By leveraging the power of CloudFormation, developers can automate the infrastructure setup and deployment process, making it easier and more efficient to launch static websites.&lt;/p&gt;

&lt;p&gt;In this module, we are going to deploy a simple static website to S3 bucket via AWS CloudFormation in three simple steps:&lt;br&gt;
👉 Step 1. Create a simple static web app via Angular&lt;br&gt;
👉 Step 2. Create S3 bucket and its policy, and configure it to host a static website&lt;br&gt;
👉 Step 3. Build and deploy static website manually to newly created S3 bucket (optional, you can skip it and go to &lt;a href="https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2"&gt;Module 2&lt;/a&gt; for build automation)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In &lt;a href="https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2"&gt;next&lt;/a&gt; module we will automate the deployment via CodeBuild.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As I’m strongly against managing environments manually and take Infrastructure as Code for granted (if we are not on the same page, I’d suggest you to read &lt;a href="https://www.simplethread.com/why-infrastructure-as-code/" rel="noopener noreferrer"&gt;this&lt;/a&gt; article first), AWS CloudFormation can be a great choice to provision and manage the complete infrastructure and AWS resources in a text file. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The high-level architecture for our project is illustrated in the diagram 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%2Fkncof7jf0fdf4t15wji4.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%2Fkncof7jf0fdf4t15wji4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Source code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Source code for this project is available on GitHub in a public &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3" rel="noopener noreferrer"&gt;StaticWebsiteHostingToS3&lt;/a&gt; repository.&lt;br&gt;
👉 Check for frontend  source code &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/frontend/app-for-aws" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
👉 Check for CloudFormation template &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/cloudformation/module1" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Initial Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Install required tools: &lt;br&gt;
👉 any IDE (personally I prefer Visual Studio Code)&lt;br&gt;
👉 git&lt;/p&gt;

&lt;p&gt;Optional:&lt;br&gt;
I’m going to create a simple static website using Angular. You can use any other framework/library if you want.&lt;br&gt;
👉 Angular, Node.js and NPM - v16 or greater&lt;br&gt;
👉 Angular CLI &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

    &lt;span class="c1"&gt;# run the following command to install Angular CLI globally&lt;/span&gt;
    &lt;span class="s"&gt;npm install -g @angular/cli&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;AWS Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the list of AWS resources that we are going to create:&lt;br&gt;
👉 S3 bucket&lt;br&gt;
👉 S3 bucket policy&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Why host static website on S3?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Why should you host your static content on S3? Let's break it down!&lt;/p&gt;

&lt;p&gt;☑️ Firstly, you no longer need to allocate a specific amount of storage space or plan for it, as S3 buckets automatically scale to accommodate your needs.&lt;/p&gt;

&lt;p&gt;☑️ Moreover, since S3 operates as a serverless service, you are relieved of the burden of managing and patching servers that store your files; you simply upload and retrieve your content. &lt;/p&gt;

&lt;p&gt;☑️ Additionally, even if your application requires a server (such as a dynamic application), it can be smaller because it doesn't need to handle requests for static content.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1. Create a simple static web app&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Shortcut: get the source code for frontend &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/tree/main/frontend/app-for-aws" rel="noopener noreferrer"&gt;here&lt;/a&gt; (but you still need to install Angular to run it locally unless you want to skip manual build and go to &lt;a href="https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2"&gt;Module 2&lt;/a&gt; for build automation).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s create a static Angular app from scratch.&lt;/p&gt;

&lt;p&gt;1️⃣ Firstly, create a new folder and name it &lt;em&gt;StaticWebsiteHostingToS3&lt;/em&gt; which will store two folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;cloudformation&lt;/em&gt; folder (stores CloudFormation templates)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;frontend&lt;/em&gt; folder (stores static website source code)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# create a parent folder &lt;/span&gt;
&lt;span class="s"&gt;mkdir StaticWebsiteHostingToS3&lt;/span&gt;
&lt;span class="s"&gt;cd StaticWebsiteHostingToS3&lt;/span&gt;

&lt;span class="c1"&gt;# initialize a repository&lt;/span&gt;
&lt;span class="s"&gt;git init&lt;/span&gt;

&lt;span class="c1"&gt;# create a folder that stores CloudFormation templates&lt;/span&gt;
&lt;span class="s"&gt;mkdir cloudformation&lt;/span&gt; 

&lt;span class="c1"&gt;# create a folder that stores static website source code&lt;/span&gt;
&lt;span class="s"&gt;mkdir frontend&lt;/span&gt; 


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

&lt;/div&gt;

&lt;p&gt;2️⃣ Secondly, create an initial Angular project by simply running the following commands in terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# navigate to folder that should store frontend &lt;/span&gt;
&lt;span class="s"&gt;cd frontend&lt;/span&gt;

&lt;span class="c1"&gt;# create a new Angular project&lt;/span&gt;
&lt;span class="s"&gt;ng new app-for-aws&lt;/span&gt;
&lt;span class="c1"&gt;# check ‘y’ and ‘SCSS’ to prompt questions such as:&lt;/span&gt;
&lt;span class="c1"&gt;# - Would you like to add Angular routing? (y/N) y&lt;/span&gt;
&lt;span class="c1"&gt;# - Which stylesheet format would you like to use? SCSS&lt;/span&gt;

&lt;span class="c1"&gt;# run the app locally&lt;/span&gt;
&lt;span class="s"&gt;cd app-for-aws&lt;/span&gt;
&lt;span class="s"&gt;ng serve&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;3️⃣ Navigate to &lt;a href="http://localhost:4200/" rel="noopener noreferrer"&gt;http://localhost:4200/&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%2Fbdsc08nce8f0fixybguz.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%2Fbdsc08nce8f0fixybguz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can customize the website as you wish. Now, let's provision some resources in AWS to host our website. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2. Create S3 bucket and its policy, and configure it to host a static website&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Amazon S3 is a perfect AWS service to host a static website.&lt;/p&gt;

&lt;p&gt;In order to host our website on S3 bucket, let’s create and configure all required resources via CloudFormation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Shortcut: get the full CloudFormation template &lt;a href="https://github.com/Tiamatt/StaticWebsiteHostingToS3/blob/main/cloudformation/module1/static-website-hosting-stack-v1.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ The following piece of code helps to create a new S3 bucket and configure it to host a static website:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myStaticWebsiteHostingBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-bucket-name&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;IndexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;ErrorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error.html&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;2️⃣ Next step is to set S3 bucket permissions for website access.&lt;/p&gt;

&lt;p&gt;When you configure a bucket as a static website, if you want your website to be public, you must disable block public access settings for the bucket and write a bucket policy that grants public read access.&lt;/p&gt;

&lt;p&gt;That is why we are going to disable ACLs for our bucket under &lt;em&gt;PublicAccessBlockConfiguration&lt;/em&gt; property and set the ownership of S3 bucket content to &lt;em&gt;Object writer&lt;/em&gt; (i.e. AWS account that uploads an object owns the object and has full control over it).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

      &lt;span class="na"&gt;OwnershipControls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ObjectOwnership&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ObjectWriter&lt;/span&gt;
      &lt;span class="na"&gt;PublicAccessBlockConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;IgnorePublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;RestrictPublicBuckets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Basically we are disabling S3 Block Public Access settings to let users get a public access to the website.&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%2F3fdjb8r1ny01992ff9c7.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%2F3fdjb8r1ny01992ff9c7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One issue I faced with while configuring access settings and building the stack was the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bucket cannot have public ACLs set with BlockPublicAccess enabled (Service: Amazon S3; Status Code: 400; Error Code: InvalidBucketAclWithBlockPublicAccessError)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The root of the issue was in using &lt;em&gt;AccessControl&lt;/em&gt; property  with &lt;em&gt;PublicAccessBlockConfiguration&lt;/em&gt; property in the template. It should work with existing buckets and fail with all new buckets.&lt;/p&gt;

&lt;p&gt;FYI, AWS has changed the default settings of ACLs of S3 bucket:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Starting in April 2023, Amazon S3 will introduce two new default bucket security settings by automatically enabling S3 Block Public Access and disabling S3 access control lists (ACLs) for all new S3 buckets. Once complete, these defaults will apply to all new buckets regardless of how they are created, including AWS CLI, APIs, SDKs, and AWS CloudFormation. These defaults have been in place for buckets created in the S3 management console since the two features became available in 2018 and 2021, respectively, and are recommended security best practices. There is no change for existing buckets. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/12/amazon-s3-automatically-enable-block-public-access-disable-access-control-lists-buckets-april-2023/" rel="noopener noreferrer"&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To resolve the issue, I've commented out &lt;em&gt;AccessControl&lt;/em&gt; property:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

      &lt;span class="c1"&gt;# AccessControl: PublicRead # throws an error: Bucket cannot&lt;/span&gt;
&lt;span class="c1"&gt;# have public ACLs set with BlockPublicAccess enabled&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;3️⃣ Next step is to create an S3 bucket policy &lt;/p&gt;

&lt;p&gt;We need to give a public read access to S3 bucket objects. Here is the policy:  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

   &lt;span class="na"&gt;myStaticWebsiteHostingBucketPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::BucketPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myStaticWebsiteHostingBucket&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublicReadForGetBucketObjects&lt;/span&gt;
            &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt; 
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::'&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myStaticWebsiteHostingBucket&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/*&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We can turn versioning on for our S3 bucket so that in case of bugs in new version we can easily roll back the deployment to previous version. Simply add &lt;em&gt;VersioningConfiguration&lt;/em&gt; under bucket's property:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
  &lt;span class="na"&gt;myStaticWebsiteHostingBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="s"&gt;....&lt;/span&gt; 
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VersioningConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;4️⃣ We are done with &lt;em&gt;Resources&lt;/em&gt;, now let's output the website URL to make it easier to navigate using the link once the stack is built.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;outputWebsiteURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;myStaticWebsiteHostingBucket&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WebsiteURL&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Static website URL&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;5️⃣ We are ready to create and run CloudFormation stack based on our template. &lt;/p&gt;

&lt;p&gt;If you need to understand how to create a stack in AWS Console, please read &lt;a href="https://dev.to/tiamatt/hands-on-aws-cloudformation-part-1-it-all-starts-here-5153"&gt;Hands-on AWS CloudFormation - Part 1. It All Starts Here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I prefer to run the stack from AWS Console as it provides an end-to-end overview of a process flow. But you can always run a stack using the AWS CLI (&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-cli.html" rel="noopener noreferrer"&gt;here is how&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Upload our template file to create a stack. Change the value of  &lt;em&gt;paramStaticWebsiteHostingBucketName&lt;/em&gt; input parameter to something unique. Then run the stack.&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%2Fp8ed4fevt3c8zf6whgtb.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%2Fp8ed4fevt3c8zf6whgtb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the stack built, you should see a newly created bucket and it's policy under &lt;em&gt;Resources&lt;/em&gt; tab:&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%2F3u5o6lvm93aasb9l05l6.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%2F3u5o6lvm93aasb9l05l6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;em&gt;Outputs&lt;/em&gt; tab you can find the link to our website. &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%2F2fyfq4up19xdxwaxmsvi.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%2F2fyfq4up19xdxwaxmsvi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently you should get 404 error as we haven't deployed static content to S3 bucket yet. &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%2F2c3bv5ss94znjpigex8l.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%2F2c3bv5ss94znjpigex8l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3. Build and deploy static website manually to newly created S3 bucket&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This step is optional - you can skip it and go to &lt;a href="https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2"&gt;Module 2&lt;/a&gt; for build automation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1️⃣ First, we need to build the production version of our Angular app locally.&lt;/p&gt;

&lt;p&gt;Open terminal for our StaticWebsiteHostingToS3 app. By default, &lt;em&gt;ng build&lt;/em&gt; command uses the production build configuration:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="s"&gt;cd frontend/app-for-aws&lt;/span&gt;
&lt;span class="s"&gt;ng build&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;A new &lt;em&gt;dist&lt;/em&gt; folder should be autogenerated with static content.&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%2F39ev7ch410x8v6w3g0dw.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%2F39ev7ch410x8v6w3g0dw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2️⃣ In AWS Console navigate to our S3 bucket and upload all the files from &lt;em&gt;dist/app-for-aws&lt;/em&gt; folder:&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%2Fsx8f4otda63i0vvgskor.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%2Fsx8f4otda63i0vvgskor.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to click on "Upload" button:&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%2F0v6r40u61zqf5449x6ws.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%2F0v6r40u61zqf5449x6ws.png" alt="Image description"&gt;&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%2F5s3da03r7a8e17e0cmm9.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%2F5s3da03r7a8e17e0cmm9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3️⃣ Now refresh the website link - our Angular app should be up and running:&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%2F7u3hc8d451ub80quf7iw.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%2F7u3hc8d451ub80quf7iw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cleanup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You might be charged for running resources. That is why it is important to clean all provisioned resources once you are done with the stack. By deleting a stack, all its resources will be deleted as well.&lt;/p&gt;

&lt;p&gt;Note: CloudFormation won’t delete an S3 bucket that contains objects. First make sure to empty the bucket before deleting the stack. Of course, you can automate the process by creating a Lambda function which deletes all object versions and the objects themselves. I'll try to cover that part in further article. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this module, we have created a simple static web app and hosted it on S3 bucket. However we took baby steps to deploy our static content to S3 bucket &lt;em&gt;manually&lt;/em&gt;.  Ideally we want to use a tool that would rebuild the source code &lt;em&gt;every time&lt;/em&gt; a code change is pushed to the repository and deploy built files to S3 bucket &lt;em&gt;automatically&lt;/em&gt;. In &lt;a href="https://dev.to/tiamatt/aws-project-module-2-automate-the-build-of-a-static-website-on-aws-s3-via-codebuild-and-cloudformation-nc2"&gt;Module 2&lt;/a&gt; I'll show you step by step how to automate the build and deployment via AWS CodeBuild. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>s3</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AWS project - Building a serverless microservice with Lambda</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Thu, 28 Jan 2021 21:14:05 +0000</pubDate>
      <link>https://dev.to/tiamatt/aws-project-building-a-serverless-microservice-with-lambda-1pa3</link>
      <guid>https://dev.to/tiamatt/aws-project-building-a-serverless-microservice-with-lambda-1pa3</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this article, we are going to build a simple serverless microservice on AWS that enables users to create and manage movies data. We will set up an entire backend using API Gateway, Lambda and DynamoDB. Then we will use SwaggerHub as an integrated design environment for our APIs:&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%2Fi%2Fiepg75jn316dyt8yj31a.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%2Fi%2Fiepg75jn316dyt8yj31a.png" alt="Alt Text"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The goal of this project is to learn how to create an application composed of small, easily deployable, loosely coupled, independently scalable, serverless components.&lt;/p&gt;

&lt;p&gt;As I’m strongly against managing environments manually and take Infrastructure as Code for granted (if we are not on the same page, I’d suggest you to read &lt;a href="https://www.simplethread.com/why-infrastructure-as-code/" rel="noopener noreferrer"&gt;this&lt;/a&gt; article first), AWS SAM will be a great fit for our serverless application. As a result, the entire application should be deployed in any AWS account with a single CloudFormation template.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;1️⃣ A user requests to the server by calling APIs from SwaggerHub UI. User's request which includes all necessary information is sent to Amazon API Gateway restful service.&lt;br&gt;
2️⃣ API Gateway transfers the collected user information to a particular AWS Lambda function based on user's request.&lt;br&gt;
3️⃣ AWS Lambda function executes event-based logic calling DynamoDB database.&lt;br&gt;
4️⃣ DynamoDB provides a persistence layer where data can be stored/retrieved by the API's Lambda function.&lt;/p&gt;

&lt;p&gt;The high-level architecture for the serverless microservice is illustrated in the diagram 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%2Fi%2Fjhs39tv5ocybjdckhjus.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%2Fi%2Fjhs39tv5ocybjdckhjus.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Source code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;All source code for this project is available on GitHub in a public &lt;a href="https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda" rel="noopener noreferrer"&gt;AwsServerlessMicroserviceWithLambda&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;To clone the GitHub repository, execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cd my-folder&lt;/span&gt;
&lt;span class="s"&gt;git clone https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Initial Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To codify, build, package, deploy, and manage our AWS resources in a fully automated fashion, we will use:&lt;/p&gt;

&lt;p&gt;👉 AWS SAM&lt;br&gt;
👉 AWS Cloud​Formation&lt;br&gt;
👉 AWS CLI&lt;br&gt;
👉 AWS SDK for Python (boto3)&lt;br&gt;
👉 Docker&lt;/p&gt;

&lt;p&gt;If you don't want to install or maintain a local IDE, use AWS Cloud9 instead (you can find how to set up AWS Cloud9 &lt;a href="https://dev.to/tiamatt/serverless-create-debug-and-deploy-lambda-and-api-gateway-via-aws-sam-and-aws-cloud9-5158"&gt;here&lt;/a&gt;). Otherwise you might need to install the latest &lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html" rel="noopener noreferrer"&gt;AWS SAM&lt;/a&gt; and &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;Python&lt;/a&gt; on your development machine.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;AWS Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the list of AWS resources that we are going to create:&lt;/p&gt;

&lt;p&gt;✔️ AWS Lambda&lt;br&gt;
✔️ Amazon DynamoDB&lt;br&gt;
✔️ Amazon API Gateway&lt;br&gt;
✔️ AWS IAM&lt;br&gt;
✔️ Amazon S3 (that is where your CloudFormation template will be stored)&lt;/p&gt;

&lt;p&gt;In this project, we will be building Lambda functions with Python 3.8.&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%2Fi%2Fmony7f66essycq77cvtv.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%2Fi%2Fmony7f66essycq77cvtv.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1. Generate an AWS SAM template&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Download &lt;strong&gt;AWS SAM Hello World&lt;/strong&gt; template which implements a basic API backend based on Amazon API Gateway endpoint and AWS Lambda function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cd my-folder&lt;/span&gt;
&lt;span class="s"&gt;sam init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're feeling lost, please, follow &lt;strong&gt;Step 2. Create a SAM application&lt;/strong&gt; described in my &lt;a href="https://dev.to/tiamatt/serverless-create-debug-and-deploy-lambda-and-api-gateway-via-aws-sam-and-aws-cloud9-5158"&gt;previous&lt;/a&gt; article. &lt;/p&gt;

&lt;p&gt;AWS SAM should create a directory with all necessary files and folders:&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%2Fi%2Fo1wtmpx7f2fe4pjezshp.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%2Fi%2Fo1wtmpx7f2fe4pjezshp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2. Create a DynamoDb table&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Rewrite pre-generated "template.yaml" with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SAM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Template&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ServerlessMicroserviceApp'&lt;/span&gt;

&lt;span class="c1"&gt;# ======================== RESOURCES ======================== #&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MoviesTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::SimpleTable&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;movies-table'&lt;/span&gt;
      &lt;span class="na"&gt;PrimaryKey&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;uuid&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AWS::Serverless::SimpleTable&lt;/code&gt; is AWS SAM syntax for DynamoDB table creation with a single attribute primary key named &lt;code&gt;uuid&lt;/code&gt;. Note, if you want to add &lt;em&gt;sort key&lt;/em&gt; or use other advanced functionality of DynamoDB, then you may want to consider using AWS CloudFormations's &lt;code&gt;AWS::DynamoDB::Table&lt;/code&gt; resource instead (yes, you can use AWS CloudFormation syntax in AWS SAM template, combining the best of both worlds 💙). &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3. Create an API Gateway&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Next step is to define API Gateway in "template.yaml":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;MoviesApi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Api&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;StageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prod&lt;/span&gt;
      &lt;span class="c1"&gt;# DefinitionUri: ./swagger.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AWS::Serverless::Api&lt;/code&gt; is AWS SAM syntax for a collection of Amazon API Gateway resources. FYI, there is no need to explicitly add &lt;code&gt;Api&lt;/code&gt; to the template - AWS SAM can implicitly create it based on &lt;code&gt;fromRestApiId&lt;/code&gt; attribute defined on &lt;code&gt;AWS::Serverless::Function&lt;/code&gt; resources (see Step 4 below). However I prefer an explicit definition as &lt;code&gt;AWS::Serverless::Api&lt;/code&gt; has a lot of useful &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html" rel="noopener noreferrer"&gt;properties&lt;/a&gt; I might use in the future (without actially refactoring the template).&lt;/p&gt;

&lt;p&gt;Amazon API Gateway acts as the interface layer between the frontend (SwaggerHub) and AWS Lambda, which calls the backend (DynamoDB database). Below are the different APIs that we are about to define in &lt;code&gt;Events&lt;/code&gt; attribute of each Lambda function in Step 4:&lt;br&gt;
👉 POST /movie (CreateMovie)&lt;br&gt;
👉 GET /movie/{uuid} (GetMovie)&lt;br&gt;
👉 GET /movie/list (GetMovies)&lt;br&gt;
👉 PUT /movie (UpdateMovie)&lt;br&gt;
👉 DELETE /movie/{uuid} (DeleteMovie)&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4. Create Lambda functions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We are going to create five lambda functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CreateMovieFunction&lt;/strong&gt; (create_movie/app.py)

&lt;ul&gt;
&lt;li&gt;triggered by &lt;em&gt;POST /movie&lt;/em&gt; API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GetMovieFunction&lt;/strong&gt; (get_movie/app.py)

&lt;ul&gt;
&lt;li&gt;triggered by &lt;em&gt;GET /movie/{uuid}&lt;/em&gt; API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GetMoviesFunction&lt;/strong&gt; (get_movies/app.py)

&lt;ul&gt;
&lt;li&gt;triggered by &lt;em&gt;GET /movie/list&lt;/em&gt; API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UpdateMovieFunction&lt;/strong&gt; (update_movie/app.py)

&lt;ul&gt;
&lt;li&gt;triggered by &lt;em&gt;PUT /movie&lt;/em&gt; API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeleteMovieFunction&lt;/strong&gt; (delete_movie/app.py)

&lt;ul&gt;
&lt;li&gt;triggered by &lt;em&gt;DELETE /movie/{uuid}&lt;/em&gt; API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start with &lt;strong&gt;CreateMovieFunction&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;1️⃣ Rename "hello_world" folder to "create_movie".&lt;/p&gt;

&lt;p&gt;2️⃣ Clear the content of "create_movie/requirements.txt" file (no need in any Python dependency).&lt;/p&gt;

&lt;p&gt;3️⃣ Open "create_movie/app.py" file and add the following code:&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;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;botocore.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt;

&lt;span class="c1"&gt;# Extract movie object from request body and insert it into DynamoDB table
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;or&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;httpMethod&lt;/span&gt;&lt;span class="sh"&gt;'&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;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&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;body&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Bad Request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;new_movie&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;loads&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;add_movie_to_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_movie&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;A new movie was saved successfully in database&lt;/span&gt;&lt;span class="sh"&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;# Insert a new item into DynamoDB table
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_movie_to_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_movie&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;new_movie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uuid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid1&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;movies-table&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;new_movie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Error&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;Message&lt;/span&gt;&lt;span class="sh"&gt;'&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function helps to add a new item to the DynamoDB's &lt;code&gt;movies-table&lt;/code&gt;. &lt;code&gt;title&lt;/code&gt; field is the only required property of &lt;code&gt;body&lt;/code&gt; object passed by a user. &lt;code&gt;uuid&lt;/code&gt; field, as a primary key that uniquely identifies each movie, should be automatically generated by &lt;code&gt;uuid.uuid1()&lt;/code&gt; Python function.  &lt;/p&gt;

&lt;p&gt;If you want to debug the code, please follow &lt;strong&gt;Step 3. Debug your function locally&lt;/strong&gt; described in my &lt;a href="https://dev.to/tiamatt/serverless-create-debug-and-deploy-lambda-and-api-gateway-via-aws-sam-and-aws-cloud9-5158"&gt;previous&lt;/a&gt; article. &lt;/p&gt;

&lt;p&gt;4️⃣ Add lambda function to "template.yaml":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ======================== GLOBAL ======================== #&lt;/span&gt;
&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.8&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app.lambda_handler&lt;/span&gt;
    &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# default is 3 seconds the function can run before it is stopped&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MoviesTable&lt;/span&gt;

&lt;span class="c1"&gt;# ======================== RESOURCES ======================== #&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other resources ...&lt;/span&gt;
  &lt;span class="na"&gt;CreateMovieFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CreateMovieFunction'&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create_movie/&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# follow the principle of least privilege&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DynamoDBCrudPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# AWS SAM policy&lt;/span&gt;
            &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MoviesTable&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWSLambdaBasicExecutionRole&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;CreateMovieApi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Api&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;RestApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MoviesApi&lt;/span&gt;
            &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/movie&lt;/span&gt;
            &lt;span class="na"&gt;Method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Globals&lt;/strong&gt; - resources that have common configurations (think of it as a &lt;em&gt;variable&lt;/em&gt; in programming). As we are going to create four more functions with the same &lt;code&gt;Runtime&lt;/code&gt;, &lt;code&gt;Handler&lt;/code&gt;, &lt;code&gt;Timeout&lt;/code&gt; and &lt;code&gt;Environment&lt;/code&gt; values, we can put all these attributes into &lt;code&gt;Global&lt;/code&gt; section following a good old DRY (Don't Repeat Yourself) principle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CodeUri&lt;/strong&gt; - the Lambda function's path (might be Amazon S3 URI, local file path, or FunctionCode object). Here we are adding a reference to a &lt;em&gt;create_movie&lt;/em&gt; folder. Note, in &lt;code&gt;Global&lt;/code&gt; section's &lt;code&gt;Handler&lt;/code&gt; attribute we have already specified &lt;code&gt;app&lt;/code&gt; file name (without &lt;em&gt;py&lt;/em&gt; file extension) and &lt;code&gt;lambda_handler&lt;/code&gt; function name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Policies&lt;/strong&gt; - one or more policies that will be appended to the default role (a new IAM role will be created) for a Lambda function. Here we are giving our function permission to access our DynamoDB table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Events&lt;/strong&gt; - events that trigger the Lambda function. In our case it is &lt;em&gt;POST /movie&lt;/em&gt; API event.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the same way as above, we need to create four more Lambda functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GetMovieFunction - get the code from GitHub &lt;a href="https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda/blob/main/get_movie/app.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GetMoviesFunction - get the code from GitHub &lt;a href="https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda/blob/main/get_movies/app.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;UpdateMovieFunction - get the code from GitHub &lt;a href="https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda/blob/main/update_movie/app.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DeleteMovieFunction - get the code from GitHub &lt;a href="https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda/blob/main/delete_movie/app.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The final version of the CloudFormation  template - get the code from GitHub &lt;a href="https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda/blob/main/template.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end we should get the following files and folders:&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%2Fi%2F50ogyb79ep3izw03dxfr.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%2Fi%2F50ogyb79ep3izw03dxfr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5. Build and deploy the project to AWS Cloud&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Build the project inside a Docker container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cd my-folder&lt;/span&gt;
&lt;span class="s"&gt;sam build --use-container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS SAM builds any dependencies that your application has, and copies your application source code to folders under &lt;code&gt;.aws-sam/build&lt;/code&gt; to be zipped and uploaded to Lambda.&lt;/p&gt;

&lt;p&gt;Then deploy your application using the following AWS SAM command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;sam deploy --guided&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please follow &lt;strong&gt;Step 5. Deploy the project to the AWS Cloud&lt;/strong&gt; described in my &lt;a href="https://dev.to/tiamatt/serverless-create-debug-and-deploy-lambda-and-api-gateway-via-aws-sam-and-aws-cloud9-5158"&gt;previous&lt;/a&gt; article for a detailed explanation.&lt;/p&gt;

&lt;p&gt;AWS SAM deploys the application using AWS CloudFormation. You can navigate to AWS Management Console -&amp;gt; CloudFormation to view the list of newly generated resources:&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%2Fi%2Foflblwduws6ruwxlszed.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%2Fi%2Foflblwduws6ruwxlszed.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6. SwaggerHub integration&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of logging into AWS Management Console and navigating from one API Gateway endpoint to another, we will use SwaggerHub to manage all your APIs in one place and to document them all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SwaggerHub&lt;/strong&gt; is an integrated API development platform that brings together all the core capabilities of the open source Swagger framework, along with additional advanced capabilities to build, document, manage, and deploy your APIs. &lt;/p&gt;

&lt;p&gt;All you need is to create a free account on &lt;a href="https://swagger.io/tools/swaggerhub/" rel="noopener noreferrer"&gt;Swagger&lt;/a&gt;, then navigate to &lt;code&gt;Create New&lt;/code&gt; -&amp;gt; &lt;code&gt;Create New API&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%2Fi%2F8kx85nm7i5qjm9ssd3q3.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%2Fi%2F8kx85nm7i5qjm9ssd3q3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give a name to your template:&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%2Fi%2Fbfa0auc0lc03mcsfseop.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%2Fi%2Fbfa0auc0lc03mcsfseop.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, modify information of Petstore APIs with our own APIs. For example, for &lt;em&gt;POST /movie/{uuid}&lt;/em&gt; API the template should look like this one below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;swagger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2.0'&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;This is  a swagger for an AWS serverless microservice built with API Gateway, Lambda and DynamoDB.&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Swagger for AWS serverless microservice&lt;/span&gt;
  &lt;span class="na"&gt;termsOfService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://swagger.io/terms/&lt;/span&gt;
  &lt;span class="na"&gt;license&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;Apache &lt;/span&gt;&lt;span class="m"&gt;2.0&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://www.apache.org/licenses/LICENSE-2.0.html&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&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;movie&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CRUD operations for movie domain&lt;/span&gt;
  &lt;span class="na"&gt;externalDocs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Find out about the project&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/Tiamatt/AWSServerlessMicroserviceWithLambda&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/movie/{uuid}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;movie&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Find a movie by uuid&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Returns a single movie&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;getMovieByUuid&lt;/span&gt;
      &lt;span class="na"&gt;produces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;application/xml&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&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;uuid&lt;/span&gt;
        &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UUID of movie to return&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;200&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;successful operation&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/definitions/Movie'&lt;/span&gt;
        &lt;span class="na"&gt;400&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invalid UUID supplied&lt;/span&gt;
        &lt;span class="na"&gt;404&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Movie not found&lt;/span&gt;
&lt;span class="na"&gt;definitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Director&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;firstname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Francis&lt;/span&gt;
      &lt;span class="na"&gt;lastname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Coppola&lt;/span&gt;
    &lt;span class="na"&gt;xml&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;Director&lt;/span&gt;
  &lt;span class="na"&gt;Movie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;title&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;year&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Godfather&lt;/span&gt;
      &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;int32&lt;/span&gt;
        &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1972&lt;/span&gt;
      &lt;span class="na"&gt;director&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/definitions/Director'&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;United States&lt;/span&gt;
    &lt;span class="na"&gt;xml&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;Movie&lt;/span&gt;   
&lt;span class="na"&gt;externalDocs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Find out more about Swagger&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://swagger.io&lt;/span&gt;
&lt;span class="na"&gt;schemes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
&lt;span class="c1"&gt;# Added by API Auto Mocking Plugin&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;my-id&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.execute-api.us-east-1.amazonaws.com&lt;/span&gt;
&lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/Prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get the final version of the Swagger template from GitHub &lt;a href="https://github.com/Tiamatt/AwsServerlessMicroserviceWithLambda/blob/main/swagger.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You need to replace &lt;code&gt;host&lt;/code&gt; value with your &lt;strong&gt;API Gateway invoke url&lt;/strong&gt;, which you can find in &lt;em&gt;Outputs&lt;/em&gt; section of AWS CloudFormation Clonsole:&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%2Fi%2Fsrvvrzpfq4905dzityad.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%2Fi%2Fsrvvrzpfq4905dzityad.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And don't forget to save your changes! Then click on &lt;code&gt;View Documentation&lt;/code&gt; icon: &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%2Fi%2Fxnjirxgifreywu3wzelm.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%2Fi%2Fxnjirxgifreywu3wzelm.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 7. Results&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Finally, it's time to play with our serverless microservice using beautiful SwaggerHub UI:&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%2Fi%2Fmopu3jut4wm44n101br5.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%2Fi%2Fmopu3jut4wm44n101br5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎉 Now, let's create a new movie:&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%2Fi%2Fmiqjzzxddy3jqptkhll3.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%2Fi%2Fmiqjzzxddy3jqptkhll3.png" alt="Alt Text"&gt;&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%2Fi%2Fgp6uvocc3y4vsneg85sv.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%2Fi%2Fgp6uvocc3y4vsneg85sv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to add some more movies.&lt;/p&gt;

&lt;p&gt;🎉 Let's get a list of all movie:&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%2Fi%2Ftbpvprl3ah9twmws2p4m.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%2Fi%2Ftbpvprl3ah9twmws2p4m.png" alt="Alt Text"&gt;&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%2Fi%2Ftnfwu2pdy7dg8zwiokbt.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%2Fi%2Ftnfwu2pdy7dg8zwiokbt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy &lt;code&gt;uuid&lt;/code&gt; of 'Godfather' movie for our next steps!&lt;/p&gt;

&lt;p&gt;🎉 Get a movie by uuid:&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%2Fi%2Flxchosh4877whsa7oe7x.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%2Fi%2Flxchosh4877whsa7oe7x.png" alt="Alt Text"&gt;&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%2Fi%2Fm0v371pqmo75gxyxbk4u.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%2Fi%2Fm0v371pqmo75gxyxbk4u.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎉 Delete a movie by uuid:&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%2Fi%2Fulmzch4apwpe63caqgnu.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%2Fi%2Fulmzch4apwpe63caqgnu.png" alt="Alt Text"&gt;&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%2Fi%2Fq5b3sbw547l6ki6dokmq.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%2Fi%2Fq5b3sbw547l6ki6dokmq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎉 And finally, let's update an existing movie:&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%2Fi%2Fi5infrb625vsozaesysc.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%2Fi%2Fi5infrb625vsozaesysc.png" alt="Alt Text"&gt;&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%2Fi%2Fefkkmeoe3ei0e28jdqvy.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%2Fi%2Fefkkmeoe3ei0e28jdqvy.png" alt="Alt Text"&gt;&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%2Fi%2Fv44i2po70p7j6pikbqmc.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%2Fi%2Fv44i2po70p7j6pikbqmc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 8. Cleanup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Use the AWS CloudFormation command to delete the stack along with all the resources it created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws cloudformation delete-stack --stack-name aws-serverless-microservice-app-stack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Congratulations on getting this far!&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%2Fi%2F2ihewytdl7m1cdevy90m.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%2Fi%2F2ihewytdl7m1cdevy90m.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you know how to create a simple CRUD (create, read, update, delete) app, and to set the foundational services, components, and plumbing needed to get a basic AWS serverless microservice up and running! &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>serverless</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Serverless - Create, debug and deploy Lambda and API Gateway via AWS SAM and AWS Cloud9</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Wed, 20 Jan 2021 20:14:59 +0000</pubDate>
      <link>https://dev.to/tiamatt/serverless-create-debug-and-deploy-lambda-and-api-gateway-via-aws-sam-and-aws-cloud9-5158</link>
      <guid>https://dev.to/tiamatt/serverless-create-debug-and-deploy-lambda-and-api-gateway-via-aws-sam-and-aws-cloud9-5158</guid>
      <description>&lt;p&gt;Let’s say you want to create and deploy a very basic serverless architecture - an API backend that consists of an Amazon API Gateway endpoint and an AWS Lambda function.&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%2Fi%2Fsroqd8rpvnlzxfkxzcqx.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%2Fi%2Fsroqd8rpvnlzxfkxzcqx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For that you need to provision the following resources:&lt;br&gt;
👉 API Gateway&lt;br&gt;
👉 Lambda function&lt;br&gt;
👉 IAM role&lt;/p&gt;

&lt;p&gt;What is the fastest and the most convenient way to provision and deploy these serverless resources using AWS native framework? &lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;AWS Serverless Application Model&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;And the answer is AWS SAM. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS SAM (Serverless Application Model)&lt;/strong&gt; is meant to build &lt;em&gt;serverless&lt;/em&gt; applications. Built on AWS CloudFormation, AWS SAM provides &lt;em&gt;shorthand syntax&lt;/em&gt; to declare serverless resources. &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%2Fi%2F3ysntxksjt3aoqo1u1y6.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%2Fi%2F3ysntxksjt3aoqo1u1y6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These 20 lines of code turn into the following serverless architecture:&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%2Fi%2F5z76snf46hagwntaheb9.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%2Fi%2F5z76snf46hagwntaheb9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I took these screenshots from Eric Johnson’s awesome &lt;a href="https://www.youtube.com/watch?v=JCqpS2RuayM&amp;amp;ab_channel=AWSOnlineTechTalks" rel="noopener noreferrer"&gt;tech talk&lt;/a&gt; &lt;br&gt;
which I would highly recommend to watch.&lt;/p&gt;

&lt;p&gt;During deployment, AWS SAM transforms the serverless resources into CloudFormation syntax, enabling you to build serverless applications faster.&lt;/p&gt;

&lt;p&gt;With that being said, let’s get our hands dirty!&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1. Set up AWS Cloud9 environment&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In order to write, run, and debug applications with just a browser, without needing to install or maintain a local IDE, we’ll go with &lt;strong&gt;AWS Cloud9&lt;/strong&gt; IDE. It preconfigures the development environment with all the SDKs, libraries, and plug-ins needed for serverless development. It also provides an &lt;strong&gt;AWS Toolkit&lt;/strong&gt; extension for locally testing and debugging AWS Lambda functions and API Gateway.&lt;/p&gt;

&lt;p&gt;First step is to set up a new AWS Cloud9 environment. Sign in to &lt;code&gt;AWS Management Console&lt;/code&gt;, go to &lt;code&gt;AWS Cloud9&lt;/code&gt; and click on &lt;code&gt;Create environment&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%2Fi%2Fbwvywbliqz8n0u98nokx.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%2Fi%2Fbwvywbliqz8n0u98nokx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give a name to your new environment (you can optionally add description) and go to the next step:&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%2Fi%2Ftg247ndtlosf4uljwu3f.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%2Fi%2Ftg247ndtlosf4uljwu3f.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve selected &lt;em&gt;t2.micro&lt;/em&gt; instance type as it is covered by the AWS Free Tier. &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%2Fi%2F90a8odjpgsi20tzp2whg.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%2Fi%2F90a8odjpgsi20tzp2whg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are done with environment configurations, review your settings and submit. It should take a couple of minutes to launch a new EC2 instance for a new Cloud9 environment. As you can check, there are a lot of preinstalled runtimes and package managers, such as Docker, AWS CLI, pip:&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%2Fi%2Fl4ity0gbha35hid3vfye.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%2Fi%2Fl4ity0gbha35hid3vfye.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can run the following command to get a list of all available packages (it’s a pretty long list!):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;yum list installed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2. Create a SAM application&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;On the upper left hand side of the screen you should see AWS Toolkit's &lt;code&gt;AWS&lt;/code&gt; icon. Click on it to open &lt;code&gt;AWS:Explorer&lt;/code&gt; window. Expand your region and right-click on &lt;code&gt;Lambda&lt;/code&gt;, then &lt;code&gt;Create new SAM Application&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%2Fi%2F5bc3c46n1mfxmaboqtxj.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%2Fi%2F5bc3c46n1mfxmaboqtxj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloud9 should walk you through the following steps:&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%2Fi%2Fzcx1e5ly4uk746lnfffu.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%2Fi%2Fzcx1e5ly4uk746lnfffu.png" alt="Alt Text"&gt;&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%2Fi%2Fqw0u1r9vuc97jyadgvnf.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%2Fi%2Fqw0u1r9vuc97jyadgvnf.png" alt="Alt Text"&gt;&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%2Fi%2F9bjfqh147a3ursrjkp6f.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%2Fi%2F9bjfqh147a3ursrjkp6f.png" alt="Alt Text"&gt;&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%2Fi%2Fwf0z14uqb1cs6gs5wswb.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%2Fi%2Fwf0z14uqb1cs6gs5wswb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila! AWS SAM creates a directory with the name that you provided as the project name with all necessary files and folders:&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%2Fi%2Fahyygmr98ef64fr6k77f.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%2Fi%2Fahyygmr98ef64fr6k77f.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;hello_world&lt;/strong&gt; - code for the application's Lambda function&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;events&lt;/strong&gt; - invocation events that you can use to invoke the function&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tests&lt;/strong&gt; - unit tests for the application code &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;template.yaml&lt;/strong&gt; - a template that defines the application's AWS resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3. Debug your function locally&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Navigate to &lt;code&gt;hello_world/app.py&lt;/code&gt; to see AWS Lambda handler logic. We can make a little clean up and uncomment &lt;em&gt;location&lt;/em&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%2Fi%2Fk56zantohd5qpk1yoa6w.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%2Fi%2Fk56zantohd5qpk1yoa6w.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;Ctrl + S&lt;/strong&gt; button to save changes in &lt;code&gt;app.py&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Make sure that you've specified &lt;em&gt;requests&lt;/em&gt; dependency in &lt;code&gt;requirements.txt&lt;/code&gt; (if you use Node.js functions then check &lt;code&gt;package.json&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%2Fi%2Fcbf5qqsldlfwtt74mxn2.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%2Fi%2Fcbf5qqsldlfwtt74mxn2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s leverage AWS Toolkit to make debugging easier! AWS Toolkit is a very powerful tool that will invoke SAM CLI to build the serverless project, deploy the code to a local Docker instance, invoke the Lambda in Docker, allowing you to step-through your Lambda code. To run the function AWS Toolkit needs launch configuration file. It should be auto-generated during a new SAM app creation - check &lt;code&gt;launch.json&lt;/code&gt; file under the &lt;code&gt;.c9&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;By the way, if you don't see &lt;code&gt;.c9&lt;/code&gt; folder and some other folders/files then you might need to turn on &lt;code&gt;Show Hidden Files&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%2Fi%2Feiojrj20q2p3d30zu3cv.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%2Fi%2Feiojrj20q2p3d30zu3cv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, back to launch configuration that gives us more flexibility to configure launchers for our SAM template. As it was mentioned before,  &lt;code&gt;launch.json&lt;/code&gt; file can be found under the &lt;code&gt;.c9&lt;/code&gt; folder. You can also open the file by &lt;code&gt;Editing Launch Configurations&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%2Fi%2F98ttk2bybebe15zfw594.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%2Fi%2F98ttk2bybebe15zfw594.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you are missing &lt;code&gt;launch.json&lt;/code&gt; file or if it is empty, open your template file, and click on &lt;code&gt;Add Debug Configuration&lt;/code&gt; link. It should automatically set launch configurations for you.&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%2Fi%2F9zioviifoowx3ustdghx.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%2Fi%2F9zioviifoowx3ustdghx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once launch configuration file is set you should see your function in debugging dropdown:&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%2Fi%2F9l3tlz5hb15eaj28ttn3.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%2Fi%2F9l3tlz5hb15eaj28ttn3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set some breakpoints in source code (&lt;code&gt;hello_world/app.py&lt;/code&gt;), then select &lt;code&gt;MyFirstSamApp:HelloWorldFunction&lt;/code&gt; option from dropdown and click on &lt;code&gt;Run&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%2Fi%2Fe0so6hm0d6l0raicfxrf.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%2Fi%2Fe0so6hm0d6l0raicfxrf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note, in case AWS Toolkit fails to run the function, throwing &lt;code&gt;RuntimeError: Container does not exist&lt;/code&gt;, you would need to increase the size of your EBS volume. See how to fix the issue &lt;a href="https://dev.to/tiamatt/troubleshooting-aws-cloud9-runtimeerror-container-does-not-exist-1oap"&gt;here&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%2Fi%2Fsolhxyvcxreu8ehhyoqk.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%2Fi%2Fsolhxyvcxreu8ehhyoqk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step-through debugging makes it easier to understand what the code is doing. Thanks to AWS Toolkits it's easy to set breakpoints, execute code line by line, and inspect the values of variables:&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%2Fi%2Fqw3tpbebnx2u8nlbawwx.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%2Fi%2Fqw3tpbebnx2u8nlbawwx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After debugging you should get the final response in AWS Toolkit window:&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%2Fi%2Fqec28j85r30beewthoap.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%2Fi%2Fqec28j85r30beewthoap.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4. Build the project&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Building your serverless application involves taking your AWS SAM template file, application code, and any applicable language-specific files and dependencies, and placing all build artifacts in the proper format and location for subsquent steps in your workflow.&lt;/p&gt;

&lt;p&gt;First, change into the project directory, where the &lt;code&gt;template.yaml&lt;/code&gt; file for the sample application is located.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cd MyFirstSamApp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then build the project inside a Docker container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;sam build --use-container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS SAM builds any dependencies that your application has, and copies your application source code to folders under &lt;code&gt;.aws-sam/build&lt;/code&gt; to be zipped and uploaded to Lambda.&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%2Fi%2Fbwwfz2uxywy6cs298ust.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%2Fi%2Fbwwfz2uxywy6cs298ust.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5. Deploy the project to the AWS Cloud&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After you develop and build your serverless application, you can deploy your application using the following AWS SAM command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;sam deploy --guided&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you specify the &lt;code&gt;--guided&lt;/code&gt; flag, the AWS SAM zips your application artifacts, uploads them to Amazon S3, and then deploys your application to the AWS Cloud.&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%2Fi%2Fohe6qtfaki3gp9d9m1dq.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%2Fi%2Fohe6qtfaki3gp9d9m1dq.png" alt="Alt Text"&gt;&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%2Fi%2Fn32l794ndaya5ltas2vl.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%2Fi%2Fn32l794ndaya5ltas2vl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS SAM deploys the application using AWS CloudFormation. In the output you can see the changes being made to your AWS CloudFormation stack.&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%2Fi%2Fg388hz4fmdl7bij11yo1.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%2Fi%2Fg388hz4fmdl7bij11yo1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you've created an AWS CloudFormation stack, you can use the &lt;code&gt;AWS Management Console&lt;/code&gt; to view its data and resources:&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%2Fi%2Fpvxcnj1c2m1pxro51yrc.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%2Fi%2Fpvxcnj1c2m1pxro51yrc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Results&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Time to call our API Gateway endpoint! Go to &lt;code&gt;Outputs&lt;/code&gt; sections and click on the link for &lt;code&gt;HelloWorldApi&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%2Fi%2F4jsbjki1nadwzx0jpjjv.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%2Fi%2F4jsbjki1nadwzx0jpjjv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you send a GET request to the API Gateway endpoint, the Lambda function is invoked. This function should return a &lt;em&gt;hello world&lt;/em&gt; message with the public IP address:&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%2Fi%2Fxgymrfhw3zz120hxzf2z.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%2Fi%2Fxgymrfhw3zz120hxzf2z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, we successfully invoke the function and got the expected response with status code 200.&lt;/p&gt;

&lt;p&gt;Now, you know how to configure and use AWS SAM and Cloud9 to build the serverless applications for AWS. Congratulations!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Clean Up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You might be charged for running resources. That is why it is important to clean all provisioned resources once you are done with the stack. Use the AWS CloudFormation command to delete the stack along with all the resources it created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws cloudformation delete-stack --stack-name my-first-sam-apps-stack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Using AWS SAM for serverless application development saves a lot of time by eliminating much of the boilerplate that AWS CloudFormation templates require. It extends AWS CloudFormation with new resource types that follow AWS best practices and are more comfortable to use. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>serverless</category>
      <category>cloud9</category>
    </item>
    <item>
      <title>Troubleshooting AWS Cloud9 - RuntimeError: Container does not exist / No space left on device</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Tue, 19 Jan 2021 04:58:01 +0000</pubDate>
      <link>https://dev.to/tiamatt/troubleshooting-aws-cloud9-runtimeerror-container-does-not-exist-1oap</link>
      <guid>https://dev.to/tiamatt/troubleshooting-aws-cloud9-runtimeerror-container-does-not-exist-1oap</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Issue description&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Recently I got a runtime error in AWS Cloud9 while trying to build my serverless application as a Docker container image. I used AWS SAM &lt;code&gt;build&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;sam build --use-container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result I got the following error issue:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RuntimeError: Container does not exist. Cannot get logs for this container&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It turned out, AWS tried to create &lt;code&gt;amazon/aws-sam-cli-build-image-python3.8&lt;/code&gt; Docker container image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--whu506Yw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/hvwklilt7jxridqtf09w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--whu506Yw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/hvwklilt7jxridqtf09w.png" alt="Alt Text" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;but failed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bIF3KR5N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fw1s2c6e9akjqmnw8yg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bIF3KR5N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fw1s2c6e9akjqmnw8yg1.png" alt="Alt Text" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I tried to pull the image manually using docker command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;docker pull amazon/aws-sam-cli-build-image-python3.8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but it looks like there is not enough disk space:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nbVBC1rB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/5wlf5fdh4hb4fxpg2w5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nbVBC1rB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/5wlf5fdh4hb4fxpg2w5l.png" alt="Alt Text" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the following command to verify that the root volume mounted under "/" is full:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;df -h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an output with 95% out of 10 GiB of EBS storage (/dev/xvda1) is used: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lhfhixxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/42vusqblka31qcv1femx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lhfhixxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/42vusqblka31qcv1femx.png" alt="Alt Text" width="556" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To check whether the EBS volume has a partition that must be extended, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;lsblk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It displays information about the block devices attached to your instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Mu9bpVZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/ec8yuzh6zvm0rlz8xnzk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Mu9bpVZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/ec8yuzh6zvm0rlz8xnzk.png" alt="Alt Text" width="511" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Solution&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once the problem is understood, improvement comes naturally. All I need is to increase the size of my EBS volume. &lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Step 1.&lt;/strong&gt; Open a new file in Cloud9&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Qg1ipo7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/4eu5cry4sxuso7xqaia4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Qg1ipo7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/4eu5cry4sxuso7xqaia4.png" alt="Alt Text" width="433" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Step 2.&lt;/strong&gt; Add the script below to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c1"&gt;# Specify the desired volume size in GiB as a command-line argument. If not specified, default to 20 GiB.&lt;/span&gt;
&lt;span class="s"&gt;SIZE=${1:-20}&lt;/span&gt;

&lt;span class="c1"&gt;# Get the ID of the environment host Amazon EC2 instance.&lt;/span&gt;
&lt;span class="s"&gt;INSTANCEID=$(curl http://169.254.169.254/latest/meta-data/instance-id)&lt;/span&gt;

&lt;span class="c1"&gt;# Get the ID of the Amazon EBS volume associated with the instance.&lt;/span&gt;
&lt;span class="s"&gt;VOLUMEID=$(aws ec2 describe-instances \&lt;/span&gt;
  &lt;span class="s"&gt;--instance-id $INSTANCEID \&lt;/span&gt;
  &lt;span class="s"&gt;--query "Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId" \&lt;/span&gt;
  &lt;span class="s"&gt;--output text)&lt;/span&gt;

&lt;span class="c1"&gt;# Resize the EBS volume.&lt;/span&gt;
&lt;span class="s"&gt;aws ec2 modify-volume --volume-id $VOLUMEID --size $SIZE&lt;/span&gt;

&lt;span class="c1"&gt;# Wait for the resize to finish.&lt;/span&gt;
&lt;span class="s"&gt;while [ \&lt;/span&gt;
  &lt;span class="s"&gt;"$(aws ec2 describe-volumes-modifications \&lt;/span&gt;
    &lt;span class="s"&gt;--volume-id $VOLUMEID \&lt;/span&gt;
    &lt;span class="s"&gt;--filters Name=modification-state,Values="optimizing","completed" \&lt;/span&gt;
    &lt;span class="s"&gt;--query "length(VolumesModifications)"\&lt;/span&gt;
    &lt;span class="s"&gt;--output text)" != "1" ]; do&lt;/span&gt;
&lt;span class="s"&gt;sleep &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="s"&gt;done&lt;/span&gt;

&lt;span class="c1"&gt;#Check if we're on an NVMe filesystem&lt;/span&gt;
&lt;span class="s"&gt;if [ $(readlink -f /dev/xvda) = "/dev/xvda" ]&lt;/span&gt;
&lt;span class="s"&gt;then&lt;/span&gt;
  &lt;span class="s"&gt;# Rewrite the partition table so that the partition takes up all the space that it can.&lt;/span&gt;
  &lt;span class="s"&gt;sudo growpart /dev/xvda &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

  &lt;span class="c1"&gt;# Expand the size of the file system.&lt;/span&gt;
  &lt;span class="c1"&gt;# Check if we are on AL2&lt;/span&gt;
  &lt;span class="s"&gt;STR=$(cat /etc/os-release)&lt;/span&gt;
  &lt;span class="s"&gt;SUB="VERSION_ID=\"2\""&lt;/span&gt;
  &lt;span class="s"&gt;if [[ "$STR" == *"$SUB"* ]]&lt;/span&gt;
  &lt;span class="s"&gt;then&lt;/span&gt;
    &lt;span class="s"&gt;sudo xfs_growfs -d /&lt;/span&gt;
  &lt;span class="s"&gt;else&lt;/span&gt;
    &lt;span class="s"&gt;sudo resize2fs /dev/xvda1&lt;/span&gt;
  &lt;span class="s"&gt;fi&lt;/span&gt;

&lt;span class="s"&gt;else&lt;/span&gt;
  &lt;span class="s"&gt;# Rewrite the partition table so that the partition takes up all the space that it can.&lt;/span&gt;
  &lt;span class="s"&gt;sudo growpart /dev/nvme0n1 &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

  &lt;span class="c1"&gt;# Expand the size of the file system.&lt;/span&gt;
  &lt;span class="c1"&gt;# Check if we're on AL2&lt;/span&gt;
  &lt;span class="s"&gt;STR=$(cat /etc/os-release)&lt;/span&gt;
  &lt;span class="s"&gt;SUB="VERSION_ID=\"2\""&lt;/span&gt;
  &lt;span class="s"&gt;if [[ "$STR" == *"$SUB"* ]]&lt;/span&gt;
  &lt;span class="s"&gt;then&lt;/span&gt;
    &lt;span class="s"&gt;sudo xfs_growfs -d /&lt;/span&gt;
  &lt;span class="s"&gt;else&lt;/span&gt;
    &lt;span class="s"&gt;sudo resize2fs /dev/nvme0n1p1&lt;/span&gt;
  &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;span class="s"&gt;fi&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 &lt;strong&gt;Step 3.&lt;/strong&gt; Save the file as &lt;code&gt;resize.sh&lt;/code&gt; (press &lt;strong&gt;Ctrl + S&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GymXa8m1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/guh1kbh10kud84qw2and.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GymXa8m1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/guh1kbh10kud84qw2and.png" alt="Alt Text" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Step 4.&lt;/strong&gt; Run sh-file (shell script):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;sh resize.sh &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get detailed information about data block changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7nnee1Jz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/jysp1r8eie17vk72f1bz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7nnee1Jz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/jysp1r8eie17vk72f1bz.png" alt="Alt Text" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the link to &lt;a href="https://docs.aws.amazon.com/cloud9/latest/user-guide/move-environment.html#move-environment-resize"&gt;AWS instuction&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Results&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To verify a new EBS volume size, run &lt;code&gt;df -h&lt;/code&gt; command again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6fo5ojbu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/s529vv406lg6y97pluap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6fo5ojbu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/s529vv406lg6y97pluap.png" alt="Alt Text" width="455" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once there is enough disk space for Docker, my container issue is resolved:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_m6dhCkG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/446rqx6849015xkjop0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_m6dhCkG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/446rqx6849015xkjop0x.png" alt="Alt Text" width="784" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully, this solution could save you both some time and headaches.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>cloud9</category>
    </item>
    <item>
      <title>Quick guide to pass AWS Certified Cloud Practitioner exam</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Tue, 12 Jan 2021 05:33:26 +0000</pubDate>
      <link>https://dev.to/tiamatt/quick-guide-to-pass-aws-certified-cloud-practitioner-certification-39od</link>
      <guid>https://dev.to/tiamatt/quick-guide-to-pass-aws-certified-cloud-practitioner-certification-39od</guid>
      <description>&lt;p&gt;Recently, I passed the AWS Certified Cloud Practitioner exam with a score of 905/1000. With limited cloud experience, I spent 3 weeks studying but there are some shortcuts I wish I knew. In this article, I would like to share with you my exam prep experience. If you have limited study time and are looking for an efficient study plan, this article is for you.&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%2Fi%2Fcz4murvpli3evhcxwyj7.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%2Fi%2Fcz4murvpli3evhcxwyj7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why start with AWS Certified Cloud Practitioner?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;👉 Of course, you can skip it. But IMHO, it is &lt;strong&gt;a must&lt;/strong&gt;. It gives you &lt;strong&gt;a high-level introduction&lt;/strong&gt; to AWS. You don't need to dive into any particular services, instead all you need is &lt;strong&gt;a foundational understanding&lt;/strong&gt; of cloud principles and AWS services and a general overview of how AWS is structured.&lt;/p&gt;

&lt;p&gt;👉 It is the &lt;strong&gt;easiest&lt;/strong&gt; of all the AWS certification exams.&lt;/p&gt;

&lt;p&gt;👉 It is a good place to start if you are a cloud newbie. There are tons of new terminology, approaches, abbreviations, services that you need to get used to. By building a basic AWS knowledge layer you can speed up your next AWS exam preparation.&lt;/p&gt;

&lt;p&gt;👉 It focuses on &lt;strong&gt;billing and pricing&lt;/strong&gt; which is very important to keep in mind while provisioning services. Otherwise, you might spend a fortune on running services. I launched AWS Cloud9 and forgot about it for a couple of days before I realized that AWS kept charging me for the service I didn’t use any more.&lt;/p&gt;

&lt;p&gt;👉 Passing this exam will help you to &lt;strong&gt;grow in confidence&lt;/strong&gt; and familiarize yourself with the general process of passing AWS exams, setting you up for a more successful exam day experience.&lt;/p&gt;

&lt;p&gt;👉 And finally, hey, who doesn't like an extra certification!&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%2Fi%2Fvls1tg4g56zg5a58u39a.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%2Fi%2Fvls1tg4g56zg5a58u39a.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Exam Details&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;you need to score at least &lt;strong&gt;700 out of 1000&lt;/strong&gt; (that’s 70%) to pass the exam&lt;/li&gt;
&lt;li&gt;you have &lt;strong&gt;90 minutes&lt;/strong&gt; to complete all the questions&lt;/li&gt;
&lt;li&gt;the exam contains &lt;strong&gt;65 questions&lt;/strong&gt; including

&lt;ul&gt;
&lt;li&gt;multiple-choice questions: 1 correct response and 3 incorrect responses (distractors)&lt;/li&gt;
&lt;li&gt;multiple-response questions: 2+ correct responses out of 5+ options&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;the exam costs &lt;strong&gt;$100&lt;/strong&gt; (when you pass it, you will get some benefits, including $75 off on your next exam purchase)&lt;/li&gt;

&lt;li&gt;the certificate is valid for &lt;strong&gt;3 years&lt;/strong&gt;
&lt;/li&gt;

&lt;li&gt;you can take a test at the testing center or from home&lt;/li&gt;

&lt;li&gt;download the exam guideline from &lt;a href="https://aws.amazon.com/certification/certified-cloud-practitioner/" rel="noopener noreferrer"&gt;the official AWS website&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Where to start&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;It is easy to get overwhelmed due to the number of online resources and exam preparation guides. For the sake of saving time and money, here are my suggestions:&lt;/p&gt;

&lt;p&gt;1️⃣ There is a great &lt;strong&gt;"AWS Certified Cloud Practitioner" course by Andrew Brown (ExamPro)&lt;/strong&gt;. It is well organized and very informative, giving you just enough to understand the concepts clearly for the exam. You have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;free version on &lt;a href="https://www.youtube.com/watch?v=B4kl23udOKo&amp;amp;list=PLBfufR7vyJJ4fOplWPOtYqRyQ6YPMsBsF&amp;amp;ab_channel=ExamPro" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;$21 course on &lt;a href="https://www.exampro.co/aws-exam-cloud-practitoner-foundational" rel="noopener noreferrer"&gt;ExamPro website&lt;/a&gt; that includes:

&lt;ul&gt;
&lt;li&gt;notes and cheat sheets&lt;/li&gt;
&lt;li&gt;flashcards (a perfect tool for learning faster and reviewing for the exam)&lt;/li&gt;
&lt;li&gt;a lot of quizzes on every lecture &lt;/li&gt;
&lt;li&gt;4 practice exams (55 questions each)&lt;/li&gt;
&lt;li&gt;note, once you’ve purchased the course you have only 1-year access :-( &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;2️⃣  A free 6-hour &lt;strong&gt;"AWS Cloud Practitioner Essentials" course by AWS&lt;/strong&gt; official &lt;a href="https://aws.amazon.com/training/course-descriptions/cloud-practitioner-essentials/" rel="noopener noreferrer"&gt;website&lt;/a&gt; is also a good source of fundamental knowledge.&lt;/p&gt;

&lt;p&gt;3️⃣  Tests, tests, tests! By solving practice tests you develop a needed speed and accuracy. Try to solve as many practice tests as possible!  &lt;strong&gt;"AWS Certified Cloud Practitioner: 6 Full Practice Exams 2020" by Mozdora Education&lt;/strong&gt; on &lt;a href="https://www.udemy.com/course/aws-certified-cloud-practitioner-practice-test/" rel="noopener noreferrer"&gt;Udemy&lt;/a&gt; is a very useful one! Worth every penny! It contains 6 practice tests, 65 questions each (mocks a real exam). It costs $9.99-$11.99 when goes on sale (wait for Udemy's sale campaigns).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Optional sources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;4️⃣ (extra) If you need more tests, you can try &lt;strong&gt;"AWS Certified Cloud Practitioner 500 Practice Exam Questions" by Neal Davis&lt;/strong&gt; on &lt;a href="https://www.udemy.com/course/aws-certified-cloud-practitioner-practice-exams-c/" rel="noopener noreferrer"&gt;Udemy&lt;/a&gt;. It contains 6 practice tests, 65 questions each (mocks a real exam). And it costs $9.99-$11.99 when goes on sale.&lt;/p&gt;

&lt;p&gt;5️⃣ (extra) You might find useful some &lt;strong&gt;cheat sheets&lt;/strong&gt; by &lt;a href="https://digitalcloud.training/certification-training/aws-certified-cloud-practitioner/" rel="noopener noreferrer"&gt;DigitalCloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;6️⃣ (extra) AWS advises a list of &lt;strong&gt;white papers&lt;/strong&gt; to read but I skipped them all as I found them boring. You can find these papers under the "Foundational-level AWS Certification" section &lt;a href="https://aws.amazon.com/certification/certification-prep/" rel="noopener noreferrer"&gt;here&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%2Fi%2Fljf9k1sdvn7rvcjfl95e.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%2Fi%2Fljf9k1sdvn7rvcjfl95e.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Studying tips&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;🎯 First, focus on &lt;strong&gt;minimum valuable knowledge&lt;/strong&gt;, i.e. the most important contents required by the exam as soon as possible, so you can prioritize your study time to focus on your weakest areas.&lt;/p&gt;

&lt;p&gt;🎯 Once you get a high-level understanding of the major services it is time to &lt;strong&gt;switch to practice tests&lt;/strong&gt;. Start with your first exam, and don’t worry if your score isn’t high. The goal is to learn which areas you should spend more time focusing on. &lt;/p&gt;

&lt;p&gt;🎯 Solve in &lt;strong&gt;a real exam-like environment&lt;/strong&gt; which means put your mobile away, don’t take any break (remember, you are not allowed to leave a real exam to go to the bathroom!), don’t put your test on pause (a real exam doesn’t have a pause button).&lt;/p&gt;

&lt;p&gt;🎯 &lt;strong&gt;Revisit&lt;/strong&gt; both correct and incorrect questions to understand the logic behind answers.&lt;/p&gt;

&lt;p&gt;🎯 &lt;strong&gt;Repeat all the tests until you get 90% or more on each one&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🎯 It’s important to &lt;strong&gt;create a detailed plan&lt;/strong&gt; for what you’re going to study and for how long. It’s up to you how many hours you are ready to spend studying (you may also have to juggle other responsibilities such as family, work, sports, etc). But you have to be &lt;strong&gt;persistently consistent&lt;/strong&gt;. Set some short-term goals for studying like "learn 10 lectures of ExamPro course per day" or "take two practice exams per day with detailed analysis of your results".&lt;/p&gt;

&lt;p&gt;🎯 &lt;strong&gt;Avoid the trap of perfectionism&lt;/strong&gt; when studying. If you wait to feel 100% confident before your exam, you’ll never get it done. Create a good knowledge base of AWS, summarise, then practice. Once you get 90% or more on each practice test, go ahead and schedule your exam.&lt;/p&gt;

&lt;p&gt;🎯 Remember, tomorrow never comes, &lt;strong&gt;start studying today&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What to Focus On&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here are my suggestions to focus your attention on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;AWS Well-Architected Framework (&lt;strong&gt;must know!&lt;/strong&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 Cloud Architecture technologies: Availability, Scalability, Elasticity, Fault Tolerance, Disaster Recovery&lt;/li&gt;
&lt;li&gt;5 pillars of AWS Well-Architected Framework, see the &lt;a href="https://aws.amazon.com/blogs/apn/the-5-pillars-of-the-aws-well-architected-framework/" rel="noopener noreferrer"&gt;link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Six Advantages of Cloud Computing (Global reach, economy of scale, agility and elasticity, etc)&lt;/li&gt;
&lt;li&gt;Shared responsibility model &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Storages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3, Glacier, S3 classes (3-4 questions)&lt;/li&gt;
&lt;li&gt;EBS (1-2 questions)&lt;/li&gt;
&lt;li&gt;Storage Gateway (1-2 questions)&lt;/li&gt;
&lt;li&gt;EFS and FSx&lt;/li&gt;
&lt;li&gt;Snow Family (Snowmobile, Snowball, Snowcone)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RDS (including Aurora)&lt;/li&gt;
&lt;li&gt;DynamoDB&lt;/li&gt;
&lt;li&gt;ElastiCache&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Compute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2&lt;/li&gt;
&lt;li&gt;EC2 pricing models (3-4 questions)&lt;/li&gt;
&lt;li&gt;Lambda&lt;/li&gt;
&lt;li&gt;Elastic Beanstalk&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Management and Governance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch&lt;/li&gt;
&lt;li&gt;CloudTrail&lt;/li&gt;
&lt;li&gt;Control Tower  (1 question)&lt;/li&gt;
&lt;li&gt;Trusted Advisor&lt;/li&gt;
&lt;li&gt;Personal Health Dashboard&lt;/li&gt;
&lt;li&gt;Well-Architectured Tool  (1 question)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;App Integration: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SNS&lt;/li&gt;
&lt;li&gt;SES&lt;/li&gt;
&lt;li&gt;SQS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Cost Management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cost Explorer&lt;/li&gt;
&lt;li&gt;Cost and Usage Report&lt;/li&gt;
&lt;li&gt;Budgets&lt;/li&gt;
&lt;li&gt;Organization and consolidation bills (2-3 questions)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Network and Content Delivery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudFront (1-2 questions)&lt;/li&gt;
&lt;li&gt;DirectConnect, AWS VPN, PrivateLink (3-4 questions)&lt;/li&gt;
&lt;li&gt;NACL and security groups &lt;/li&gt;
&lt;li&gt;Global Accelerator&lt;/li&gt;
&lt;li&gt;ELB&lt;/li&gt;
&lt;li&gt;Route53 &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Security, Identity &amp;amp; Compliance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM (3-4 questions), roles, groups&lt;/li&gt;
&lt;li&gt;Inspector&lt;/li&gt;
&lt;li&gt;Artifacts and compliance&lt;/li&gt;
&lt;li&gt;Cognito&lt;/li&gt;
&lt;li&gt;GuardDuty&lt;/li&gt;
&lt;li&gt;Macie&lt;/li&gt;
&lt;li&gt;Shield&lt;/li&gt;
&lt;li&gt;WAF&lt;/li&gt;
&lt;li&gt;AWS Penetration Testing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Polly (Text-to-Speech Service) (1 question)&lt;/li&gt;
&lt;li&gt;CodeDeploy (1 question)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Good luck!&lt;/strong&gt;
&lt;/h2&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%2Fi%2Fnbtmynbdy6qugn0mkvta.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%2Fi%2Fnbtmynbdy6qugn0mkvta.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
    </item>
    <item>
      <title>Hands-on AWS CloudFormation - Part 5. IAM users, groups and roles</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Mon, 04 Jan 2021 07:02:14 +0000</pubDate>
      <link>https://dev.to/tiamatt/hands-on-aws-cloudformation-part-5-iam-users-groups-and-roles-5d9f</link>
      <guid>https://dev.to/tiamatt/hands-on-aws-cloudformation-part-5-iam-users-groups-and-roles-5d9f</guid>
      <description>&lt;p&gt;In the “Hands-on AWS CloudFormation” series we continue to create small templates by provisioning different types of AWS resources with AWS CloudFormation. In the end of this series we can turn the small templates into building blocks for &lt;em&gt;full stack templates&lt;/em&gt;. For example, in &lt;a href="https://dev.to/tiamatt/hands-on-aws-cloudformation-part-4-create-vpc-with-private-and-public-subnets-85d"&gt;Part 4&lt;/a&gt; we’ve learned how to create a VPC with private and public subnets - ultimately, it will help us to create a secure, highly reliable and fault-tolerant system using multiple EC2 instances in a private network and ancillary services such as Auto Scaling and Elastic Load Balancing. But to get to the final results we need to take a few baby steps at a time. That being said, for today’s lesson we will cover the IAM part of AWS using CloudFormation.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Don’t ignore IAM&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AWS IAM is something you need to take seriously if you are working in the AWS space. Think of it as an essential gate-keeper of AWS. This is the place where you would administer authentication and authorization for AWS’s environments and services.&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%2Fi%2Fwewxsyxuvgn7gfxy9wnt.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%2Fi%2Fwewxsyxuvgn7gfxy9wnt.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s admirable that AWS IAM follows &lt;em&gt;an incredibly granular approach&lt;/em&gt; in providing permissions and access control within your environments. With that, let's take advantage of the great cloud platform and  create the basic components of IAM with CloudFormation, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Policies&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;li&gt;Groups&lt;/li&gt;
&lt;li&gt;Roles&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Creating IAM policies&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We manage access in AWS by creating policies and attaching them to &lt;strong&gt;IAM identities&lt;/strong&gt; (users, groups of users, and roles) or AWS resources. &lt;strong&gt;Policies&lt;/strong&gt; specify a set of permissions. Permissions in the policies determine whether the request is allowed or denied. &lt;/p&gt;

&lt;p&gt;There are three types of IAM policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Managed Policy&lt;/li&gt;
&lt;li&gt;Customer Managed Policy&lt;/li&gt;
&lt;li&gt;Inline Policy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS Managed Policy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AWS Managed Policy&lt;/strong&gt; is a standalone policy that is created and administered &lt;strong&gt;by AWS&lt;/strong&gt;. AWS managed policies could be reused between IAM entities (users, groups, or roles) and &lt;strong&gt;cannot be modified&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Here is an example of how you can attach AWS managed policy to a new role:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# a new role&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="c1"&gt;# ... some code here ...&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of ARNs of IAM managed policies that you want to attach to the role&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::aws:policy/AWSCloud9Administrator&lt;/span&gt; &lt;span class="c1"&gt;# provides administrator access to AWS Cloud9&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You can get a needed AWS managed policy via AWS Management Console by navigating to IAM -&amp;gt; Policies, then filter by Policy type checking ‘AWS managed’ checkbox:&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%2Fi%2Fffispi2uc8gvif30nzju.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%2Fi%2Fffispi2uc8gvif30nzju.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or you can use AWS CLI command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="s"&gt;aws iam list-policies --scope AWS&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Check out &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html" rel="noopener noreferrer"&gt;this link&lt;/a&gt; if you need to install AWS CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Managed Policy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Customer Managed Policy&lt;/strong&gt; is a standalone policy  that is created &lt;strong&gt;by a user&lt;/strong&gt;. Customer managed policies could be reused between IAM entities and &lt;strong&gt;can be modified&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s create a customer managed policy that gives read only access to EC2 instance:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myCustomerManagedPolicyForEC2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::ManagedPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;customerManagedEC2ReadOnlyPolicy&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this policy&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Customer managed policy for read only access to EC2 instance&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (required) JSON policy document &lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# allow read only access to EC2 instance&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ec2:Describe'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
      &lt;span class="c1"&gt;# IAM entities (Groups, Roles, and Users) are optional properties&lt;/span&gt;
      &lt;span class="na"&gt;Users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this policy to the list of existing users&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;userA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;userB&lt;/span&gt;
      &lt;span class="na"&gt;Groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this policy to the list of existing groups&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupB&lt;/span&gt;
      &lt;span class="na"&gt;Roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this policy to the list of existing roles&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;roleA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;roleB&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Policies-CustomerManagedPolicy.yaml" rel="noopener noreferrer"&gt;GitHub ‘CustomerManagedPolicy’ template&lt;/a&gt;. Also check out &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-managedpolicy.html" rel="noopener noreferrer"&gt;AWS link&lt;/a&gt; for documentation.&lt;/p&gt;

&lt;p&gt;Note, &lt;strong&gt;PolicyDocument&lt;/strong&gt; is the only required field in Properties. It is simply a policy (a JSON document). &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_examples.html" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is my favorite link to the great list of example policies. Usually you need to provide policies in JSON format in IAM. However, for AWS CloudFormation templates formatted in YAML, you can provide the policy in JSON or YAML format. AWS CloudFormation always &lt;em&gt;converts a YAML policy to JSON format&lt;/em&gt; before submitting it to IAM.&lt;/p&gt;

&lt;p&gt;What is the &lt;strong&gt;Path&lt;/strong&gt;? If you are using the IAM API or AWS CLI to create IAM resources, you can also give some resources an optional path, e.g. ‘&lt;em&gt;/companyA/departmentB/projectC/&lt;/em&gt;’ to match your company's organizational structure. You could then create a policy to allow all users in that path to access the policy simulator API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline Policy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Inline Policy&lt;/strong&gt; is a policy that is created &lt;strong&gt;by a user&lt;/strong&gt; and &lt;strong&gt;embedded directly&lt;/strong&gt; to IAM entities. Inline policies &lt;strong&gt;cannot be reused&lt;/strong&gt; in different IAM entities as it emphasizes direct one-to-one relationship between entity and the policy itself. Once the entity is deleted, inline policies attached to it get removed as well. &lt;/p&gt;

&lt;p&gt;Let’s create an inline policy that gives read only access to all S3 buckets:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myInlinePolicyForS3ReadOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Policy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inlineS3ReadOnlyPolicy&lt;/span&gt; &lt;span class="c1"&gt;# (required) give a name to this policy&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (required) JSON policy document&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# allow read only access to all S3 buckets&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:Get*'&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:List*'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
      &lt;span class="c1"&gt;# Note, Groups, Roles, and Users fields are optional. However, you must specify at least one of these fields&lt;/span&gt;
      &lt;span class="na"&gt;Users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this policy to the list of existing users&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;userA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;userB&lt;/span&gt;
      &lt;span class="na"&gt;Groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this policy to the list of existing groups&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupB&lt;/span&gt;
      &lt;span class="na"&gt;Roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this policy to the list of existing roles&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;roleA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;roleB&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Policies-InlinePolicy.yaml" rel="noopener noreferrer"&gt;GitHub ‘InlinePolicy’ template&lt;/a&gt;. Also check out &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-policy.html" rel="noopener noreferrer"&gt;AWS link&lt;/a&gt; for documentation.&lt;/p&gt;

&lt;p&gt;Note, &lt;strong&gt;PolicyName&lt;/strong&gt; and &lt;strong&gt;PolicyDocument&lt;/strong&gt; are the only required fields in Properties. &lt;strong&gt;Roles&lt;/strong&gt;, &lt;strong&gt;Users&lt;/strong&gt;, and &lt;strong&gt;Groups&lt;/strong&gt; fields are optional. But you must specify &lt;strong&gt;at least one&lt;/strong&gt; of these fields. Thus, if you run the stack ignoring all three fields (Groups, Roles, and Users), CloudFormation will roll it back notifying of an error:&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%2Fi%2F26rxyne6mxqpdnabdpcp.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%2Fi%2F26rxyne6mxqpdnabdpcp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check out the library of  IAM identity-based policies &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_examples.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; - it can help you to find the JSON policy document as a template for your own policies.&lt;/li&gt;
&lt;li&gt;Check out &lt;a href="https://policysim.aws.amazon.com" rel="noopener noreferrer"&gt;IAM Policy Simulator&lt;/a&gt; which can be used to test and troubleshoot IAM and resource based policies.&lt;/li&gt;
&lt;li&gt;Take a look at a formal &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_grammar.html" rel="noopener noreferrer"&gt;Grammar&lt;/a&gt; for the language used to create JSON policies in IAM.&lt;/li&gt;
&lt;li&gt;If you need to use the guide for a visual editor to create and modify your IAM policies, check out this &lt;a href="https://aws.amazon.com/blogs/security/use-the-new-visual-editor-to-create-and-modify-your-aws-iam-policies/" rel="noopener noreferrer"&gt;article&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Creating IAM users and groups&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;IAM user&lt;/strong&gt; is a person that needs to interact with your AWS resources or services either from the AWS Console or with the AWS CLI. When you create a new user, no credentials are assigned, and the user does not have any permission to access your AWS resources.&lt;/p&gt;

&lt;p&gt;Now, let’s create some IAM users with AWS Cloudformation:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::User'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;UserName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;userA&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this user&lt;/span&gt;
      &lt;span class="na"&gt;LoginProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# specify a password for this user&lt;/span&gt;
        &lt;span class="na"&gt;Password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pa$$w0rd&lt;/span&gt;
        &lt;span class="na"&gt;PasswordResetRequired&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# make this user to set a new password on next sign-in&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
      &lt;span class="na"&gt;Groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this user to the list of existing groups&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupB&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Users-User.yaml" rel="noopener noreferrer"&gt;GitHub User template&lt;/a&gt;.  Also check out &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html" rel="noopener noreferrer"&gt;AWS link&lt;/a&gt; for documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LoginProfile&lt;/strong&gt; contains the user's password. You can also use &lt;strong&gt;PasswordResetRequired&lt;/strong&gt; to specify whether the user is required to set a new password on next sign-in.&lt;/p&gt;

&lt;p&gt;As mentioned before, by default any new IAM user is created with &lt;strong&gt;no access&lt;/strong&gt; to any AWS services (non-explicit deny). You can set permissions by adding needed policies to the user (please make sure to follow the standard security advice of granting least privilege).&lt;/p&gt;

&lt;p&gt;In order to attach IAM managed policies to a user, use &lt;strong&gt;ManagedPolicyArns&lt;/strong&gt; field:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::User'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;UserName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;userB&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this user&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of ARNs of IAM managed policies that you want to attach to the user&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::aws:policy/AWSCloud9Administrator&lt;/span&gt; &lt;span class="c1"&gt;# provides administrator access to AWS Cloud9&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::111111111111:policy/myCustomerManagedPolicy&lt;/span&gt; &lt;span class="c1"&gt;# use your own customer managed policy&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Also you can add an inline policy document that is embedded in the specified IAM user, using &lt;strong&gt;Policies&lt;/strong&gt; field:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::User'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;UserName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;userC&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this user&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of inline policy documents that are embedded in the user&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inlineS3ReadOnlyPolicy&lt;/span&gt; &lt;span class="c1"&gt;# give a unique name to this policy&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# JSON policy document&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# allow read only access to all S3 buckets&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:Get*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:List*'&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Users-UserWithPolicies.yaml" rel="noopener noreferrer"&gt;GitHub UserWithPolicies template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what if you need to give Admin permissions to a hundred of users? Are you going to attach a new policy to each and every user? If so, what if tomorrow you want to change Admin permissions to  something else? The easiest way to do that is to create an IAM group called Admins and give that group the types of permissions that administrators typically need. Then add users to that group. Those added users will then automatically have all of their group’s permissions. Without a group, listing permissions for every single user will be a huge hassle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Group&lt;/strong&gt; is a collection of IAM users. Groups are useful when we want to manage permissions for a group of users.&lt;/p&gt;

&lt;p&gt;If you already have some groups, you can attach them to &lt;em&gt;userA&lt;/em&gt; specifying a list of groups in &lt;strong&gt;Groups&lt;/strong&gt; field. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::User'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;UserName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;userD&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this user&lt;/span&gt;
      &lt;span class="na"&gt;Groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# attach this user to the list of existing groups&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;groupB&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If not, then let’s create a new group:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Group'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApiDevelopers&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this group&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of ARNs of IAM managed policies that you want to attach to the group&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::aws:policy/AWSCloud9Administrator&lt;/span&gt; &lt;span class="c1"&gt;# provide administrator access to AWS Cloud9&lt;/span&gt;
        &lt;span class="c1"&gt;# - arn:aws:iam::111111111111:policy/customerManagedBlahBlahPolicy # use your own customer managed policy specifying its ARN&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of inline policy documents that are embedded in the group&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inlineCloudWatchLogsPolicy&lt;/span&gt; &lt;span class="c1"&gt;# give a unique name to this policy&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# JSON policy document&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# provide write permissions to CloudWatch Logs&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs:CreateLogGroup'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs:CreateLogStream*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs:PutLogEvents'&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Groups-Group.yaml" rel="noopener noreferrer"&gt;GitHub ‘Group’ template&lt;/a&gt;.  Also check out &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html" rel="noopener noreferrer"&gt;AWS link&lt;/a&gt; for documentation.&lt;/p&gt;

&lt;p&gt;You can specify IAM managed policies using &lt;strong&gt;ManagedPolicyArns&lt;/strong&gt; field and inline policies using &lt;strong&gt;Policies&lt;/strong&gt; field.&lt;/p&gt;

&lt;p&gt;The last step is to learn how to attach multiple existing users to an existing group. For that you might need to use &lt;em&gt;AWS::IAM::UserToGroupAddition&lt;/em&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myUserToGroupAddition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::UserToGroupAddition'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;groupB&lt;/span&gt; &lt;span class="c1"&gt;# existing group name&lt;/span&gt;
      &lt;span class="na"&gt;Users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of existing user names&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;userA&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;userB&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Groups-AttachUsersToGroup.yaml" rel="noopener noreferrer"&gt;GitHub ‘AttachUsersToGroup’ template&lt;/a&gt;.  Also check out &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-addusertogroup.html" rel="noopener noreferrer"&gt;AWS link&lt;/a&gt; for documentation.&lt;/p&gt;

&lt;p&gt;Note, a group does not have security credentials as well as cannot access and manage AWS’s resources - it just helps to manage user permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Creating IAM roles&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;IAM roles&lt;/strong&gt; allow you to delegate access to users or services that normally don't have access to your organization's AWS resources. IAM users or AWS services can assume a role to obtain &lt;strong&gt;temporary&lt;/strong&gt; security credentials that can be used to make AWS API calls. Consequently, you don't have to share long-term credentials or define permissions for each entity that requires access to a resource.&lt;/p&gt;

&lt;p&gt;Creating a new role is similar to delegate access permissions to those trusted entities without having to share access keys. A role cannot make direct requests to AWS services, but the entity it attached to.&lt;/p&gt;

&lt;p&gt;Let’s create a simple role for an EC2 instance:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;RoleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roleA&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this role&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IAM role for EC2 instance&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (required) only one trust policy with a role&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="pi"&gt;-&lt;/span&gt; 
            &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ec2.amazonaws.com'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole'&lt;/span&gt;
      &lt;span class="na"&gt;MaxSessionDuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt; &lt;span class="c1"&gt;# in seconds, 1 hour&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Roles-Role.yaml" rel="noopener noreferrer"&gt;GitHub ‘Role’ template&lt;/a&gt;. Also check out &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html" rel="noopener noreferrer"&gt;AWS link&lt;/a&gt; for documentation.&lt;/p&gt;

&lt;p&gt;Note, &lt;strong&gt;AssumeRolePolicyDocument&lt;/strong&gt; is the only required field in Properties. It is the trust policy that is associated with this role. &lt;strong&gt;Trust policies&lt;/strong&gt; define which entities can assume the role. But you can associate &lt;em&gt;only one&lt;/em&gt; trust policy with a role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MaxSessionDuration&lt;/strong&gt; (in seconds) helps you to set the maximum session duration for a new role. If you do not specify a value for this setting, the default maximum of one hour (3600 seconds) is applied. This setting can have a value from 1 hour to 12 hours.&lt;/p&gt;

&lt;p&gt;Similar to users, you can set permissions for the role by adding needed policies to it (and again, please follow the standard security advice of granting least privilege).&lt;/p&gt;

&lt;p&gt;In order to attach IAM managed policies to the role, use &lt;strong&gt;ManagedPolicyArns&lt;/strong&gt; field:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;myRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;RoleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roleB&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this role&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (required) only one trust policy with a role&lt;/span&gt;
      &lt;span class="c1"&gt;# ... some code here ...&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of ARNs of IAM managed policies that you want to attach to the role&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::aws:policy/AWSCloud9Administrator&lt;/span&gt; &lt;span class="c1"&gt;# provides administrator access to AWS Cloud9&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::111111111111:policy/myCustomerManagedPolicy&lt;/span&gt; &lt;span class="c1"&gt;# use your own customer managed policy&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Also you can add an inline policy document that is embedded in the role, using &lt;strong&gt;Policies&lt;/strong&gt; field:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;myRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;RoleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roleB&lt;/span&gt; &lt;span class="c1"&gt;# give a name to this role&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (required) only one trust policy with a role&lt;/span&gt;
      &lt;span class="c1"&gt;# ... some code here …&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# list of inline policy documents that are embedded in the role&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inlineS3ReadOnlyPolicy&lt;/span&gt; &lt;span class="c1"&gt;# give a unique name to this policy&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# JSON policy document&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# allow read only access to all S3 buckets&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:Get*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:List*'&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Roles-RoleWithPolicies.yaml" rel="noopener noreferrer"&gt;GitHub ‘RoleWithPolicies’ template&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Creating Instance Profile&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You should use an &lt;strong&gt;Instance Profile&lt;/strong&gt; to pass an IAM role to an EC2 instance. But what is the difference between an AWS role and an instance profile?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Roles&lt;/strong&gt; are designed to be 'assumed' by other principals which do define 'who am I?', such as users, AWS services, and EC2 instances.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instance profile&lt;/strong&gt;, on the other hand, defines 'who am I?' Just like an IAM user represents a person, an instance profile represents EC2 instances. The only permissions an EC2 instance profile has is the power to assume a role.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how you can create a new instance profile in order to attach your role to EC2 instance:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;myInstanceProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::InstanceProfile'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;InstanceProfileName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;instanceProfileA&lt;/span&gt;
      &lt;span class="na"&gt;Roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (required) existing role name to associate with the instance profile &lt;/span&gt;
      &lt;span class="c1"&gt;# Note: only one role can be assigned to an EC2 instance at a time, but type is 'List of String'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;roleD&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Take a look at this &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/IAM/Roles-InstanceProfile.yaml" rel="noopener noreferrer"&gt;GitHub ‘InstanceProfile’ template&lt;/a&gt;. Also check out &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html" rel="noopener noreferrer"&gt;AWS link&lt;/a&gt; for documentation.&lt;/p&gt;

&lt;p&gt;Note, there are some limitations - an instance profile can contain &lt;em&gt;only one IAM role&lt;/em&gt;, although a role can be included in multiple instance profiles. This limit of one role per instance profile cannot be increased. You can remove the existing role and then add a different role to an instance profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Review&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AWS IAM provides a number of security features to consider as you develop and implement your own security policies.&lt;/p&gt;

&lt;p&gt;In this article, we walked through how to start creating IAM policies and IAM identities (users, groups, and roles) using AWS CloudFormation. You can download and use IAM templates from &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/tree/main/IAM" rel="noopener noreferrer"&gt;my GitHub account&lt;/a&gt; and use them in stacks. Hopefully this hands-on guide will help you excel in the field of AWS CloudFormation.&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%2Fi%2Fnqhbrw19lmsf4dbn3n3y.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%2Fi%2Fnqhbrw19lmsf4dbn3n3y.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>security</category>
      <category>cloudformation</category>
    </item>
    <item>
      <title>Quick guide to start using AWS CodeCommit via AWS CLI</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Mon, 28 Dec 2020 01:41:35 +0000</pubDate>
      <link>https://dev.to/tiamatt/quick-guide-to-start-using-aws-codecommit-via-aws-cli-1jg5</link>
      <guid>https://dev.to/tiamatt/quick-guide-to-start-using-aws-codecommit-via-aws-cli-1jg5</guid>
      <description>&lt;p&gt;I was looking for AWS CodeCommit quick tutorial and ended up watching ACloudGuru’s &lt;a href="https://acloud.guru/overview/7f9e16d6-414d-42fd-ad70-970a24f49c2b?_ga=2.245456836.1979153929.1608879327-652555336.1598814393&amp;amp;_gac=1.123506169.1607550672.CjwKCAiAiML-BRAAEiwAuWVggoVc8w3OZdP0p1SSyEJcIFYXPwO6_ZNyST0_B2Zp07x30rTC6N0ljRoCA-UQAvD_BwE" rel="noopener noreferrer"&gt;“Manage and Deploy Code with AWS Developer Tools”&lt;/a&gt; 8 hour course (BTW, a great one!), reading some articles and AWS documentations. Based on that I’ve decided to create a quick guide to start using AWS CodeCommit via AWS CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is AWS CodeCommit?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Think of it as an AWS version of GitHub. In other words, AWS CodeCommit is a managed source control service that hosts private Git repositories. You can use CodeCommit to store and manage documents, source code, and binary files in the cloud.&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%2Fi%2Fqenl5peplc4h7im3ixrl.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%2Fi%2Fqenl5peplc4h7im3ixrl.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CodeCommit is integrated with a number of AWS services such as  CodeBuild, CodePipeline, Lambda, SNS, Amplify, CloudFormation, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;AWS CodeCommit vs GitHub&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Both can be primarily classified as "Code Collaboration &amp;amp; Version Control" tools. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Similarities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Support code review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use two methods of authentication, SSH and HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use Git repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Based on comparison &lt;a href="https://comparecamp.com/aws-codecommit-vs-github-comparison/" rel="noopener noreferrer"&gt;provided by CompareCamp&lt;/a&gt;, here are some cons and pros of both services:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AWS CodeCommit Pros&lt;/th&gt;
&lt;th&gt;AWS CodeCommit Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;free private repos (for up to 5 users)&lt;/td&gt;
&lt;td&gt;complex menu options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;faster deployments using AWS solutions&lt;/td&gt;
&lt;td&gt;resubmit flow and code review unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;encrypted repo data&lt;/td&gt;
&lt;td&gt;limited triggers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;issue tracker&lt;/td&gt;
&lt;td&gt;lack of CI system integrations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the other hand,&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GitHub Pros&lt;/th&gt;
&lt;th&gt;GitHub  Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ease of use&lt;/td&gt;
&lt;td&gt;notifications are hard to configure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;easy integration with third-party tools&lt;/td&gt;
&lt;td&gt;lack of first-party support for mobile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;code highlighting for ObjectScript&lt;/td&gt;
&lt;td&gt;lack of command-line configuration options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;comprehensive issue tracking&lt;/td&gt;
&lt;td&gt;security scanning problems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prerequisites and setup (using HTTPS)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Of course, you can use AWS CodeCommit console with friendly UI to manage files and repositories directly. But in order  to work with multiple files, files across branches, and also to control multiple AWS services from the command line and automate them through scripts consider setting up your local computer to work with AWS CodeCommit via AWS CLI.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1. Install &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Git&lt;/strong&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Check installation by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;git --version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2. Install &lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;&lt;strong&gt;AWS CLI&lt;/strong&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Check installation by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws --version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3. Create Git credentials for IAM user
&lt;/h4&gt;

&lt;p&gt;Firstly, create a new AWS IAM user and give it a &lt;strong&gt;Programmatic access&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%2Fi%2Fmx7qgo7tp6r0modwmeaz.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%2Fi%2Fmx7qgo7tp6r0modwmeaz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then attach &lt;strong&gt;AWSCodeCommitFullAccess&lt;/strong&gt; policy to the user.&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%2Fi%2Frcmptsuga4j5ui2t8frn.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%2Fi%2Frcmptsuga4j5ui2t8frn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Store a user's credentials in a safe place&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%2Fi%2Fatn0q86kzyuheizw11jx.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%2Fi%2Fatn0q86kzyuheizw11jx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4. Configure &lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;&lt;strong&gt;AWS CLI&lt;/strong&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Use the following command and enter newly created &lt;strong&gt;Access key ID&lt;/strong&gt; and &lt;strong&gt;Secret access key&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws configure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS CLI will also ask you to enter the &lt;strong&gt;Region&lt;/strong&gt; whose servers you want to send your requests to. By default it is typically the region closest to you, but you can enter any other region.&lt;/p&gt;

&lt;p&gt;And finally, you might want to specify &lt;strong&gt;Output format&lt;/strong&gt; how the results are formatted. &lt;em&gt;json&lt;/em&gt; is used as the default, but you can use &lt;em&gt;yaml&lt;/em&gt;, &lt;em&gt;yaml-stream&lt;/em&gt; (streaming allows for faster handling of large data types), &lt;em&gt;text&lt;/em&gt;, and &lt;em&gt;table&lt;/em&gt; formats.&lt;/p&gt;

&lt;p&gt;Alternatively, you can set up AWS CodeCommit using &lt;strong&gt;SSH connections&lt;/strong&gt; for &lt;a href="https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-ssh-windows.html" rel="noopener noreferrer"&gt;Windows&lt;/a&gt; and for &lt;a href="https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-ssh-unixes.html" rel="noopener noreferrer"&gt;Linux, macOS, or Unix&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pricing&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;First 5 active users&lt;/th&gt;
&lt;th&gt;Each additional active user beyond the first 5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;td&gt;$1.00 per month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unlimited repositories&lt;/td&gt;
&lt;td&gt;Unlimited repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 GB-month of storage&lt;/td&gt;
&lt;td&gt;10 GB-month of storage per active user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000 Git requests/month&lt;/td&gt;
&lt;td&gt;2,000 Git requests/month per active user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Check the latest prices &lt;a href="https://aws.amazon.com/codecommit/pricing/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;AWS CodeCommit commands&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/codecommit/index.html" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a full list of AWS CodeCommit commands. In this article, we are going to understand how to use the top command. Just follow along the guide to get a basic experience with AWS CodeCommit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;create a new repository&lt;/strong&gt;, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit create-repository --repository-name MyDemoRepo --repository-description "This is a demo repo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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%2Fi%2F8b9qjf1ctpzkkhb0wz5s.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%2Fi%2F8b9qjf1ctpzkkhb0wz5s.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should find a new repo on AWS CodeCommit Console:&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%2Fi%2Frmr5deyas00mh3fm9cp1.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%2Fi%2Frmr5deyas00mh3fm9cp1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;get a list of all existing repositories&lt;/strong&gt;, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit list-repositories&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi%2Fq72prug5hh0xqldm4v17.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%2Fi%2Fq72prug5hh0xqldm4v17.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;get information about specific repository&lt;/strong&gt;, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit get-repository --repository-name MyDemoRepo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;get information about multiple specific repositories&lt;/strong&gt;, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit batch-get-repositories --repository-names MyDemoRepo MyBestRepo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;update repository’s name&lt;/strong&gt;, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit update-repository-name --old-name MyDemoRepo --new-name MyDemo2Repo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;update repository’s description&lt;/strong&gt;, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit update-repository-description --repository-name  MyDemo2Repo --repository-description "This is updated description"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;clone repository&lt;/strong&gt;, you need to get a url from AWS CodeCommit console 
&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%2Fi%2F0ndxskitzh0kwz4xxgfz.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# navigate to your local directory&lt;/span&gt;
&lt;span class="s"&gt;cd "C:\Users\{userName}\{folder}\{nested-folder}"&lt;/span&gt;
&lt;span class="c1"&gt;# # clone a repository from AWS CodeCommit to your local machine&lt;/span&gt;
&lt;span class="s"&gt;git clone https://git-codecommit.us-east-1.amazonaws.com/v1/repos/MyDemo2Repo localMyDemo2Repo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important! If a Windows Security is popped up asking for username and password, then &lt;strong&gt;cancel it&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%2Fi%2Fm3bgo0i3tfnhz1dxtabs.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%2Fi%2Fm3bgo0i3tfnhz1dxtabs.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my case I still got the warning about an empty repository with a &lt;strong&gt;successfully&lt;/strong&gt; copied repository on my local machine.&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%2Fi%2Frd1nj1x4kpgu7pfuwubt.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%2Fi%2Frd1nj1x4kpgu7pfuwubt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The issue has gone once I've uninstalled and reinstalled Git for Windows with &lt;strong&gt;cleared the check box for the option to install the Git Credential Manager utility&lt;/strong&gt; (&lt;a href="https://docs.aws.amazon.com/codecommit/latest/userguide/troubleshooting-ch.html#troubleshooting-windowshttps" rel="noopener noreferrer"&gt;details&lt;/a&gt;) because the credential manager is not compatible with the credential helper for AWS CodeCommit.&lt;/p&gt;

&lt;p&gt;If you got  different kind of issues while attempting to clone a repository, see AWS's &lt;a href="https://docs.aws.amazon.com/codecommit/latest/userguide/troubleshooting.html" rel="noopener noreferrer"&gt;Troubleshooting section&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;commit and push changes to AWS CodeCommit&lt;/strong&gt;, run Git commands:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# navigate to your local repository&lt;/span&gt;
&lt;span class="s"&gt;cd localMyDemo2Repo&lt;/span&gt;
&lt;span class="c1"&gt;# create a sample text file&lt;/span&gt;
&lt;span class="s"&gt;echo "Greeting, AWS CodeCommit nerds! Let's rock it!" &amp;gt; hello.txt&lt;/span&gt;
&lt;span class="c1"&gt;# stage the file for commit to your local repository&lt;/span&gt;
&lt;span class="s"&gt;git add .&lt;/span&gt;
&lt;span class="c1"&gt;# commit the file that you've staged&lt;/span&gt;
&lt;span class="s"&gt;git commit -m"Add a new hello file"&lt;/span&gt;
&lt;span class="c1"&gt;# push the changes in your local repository to AWS CodeCommit&lt;/span&gt;
&lt;span class="s"&gt;git push origin master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, a new "hello.txt" file should be added to remote repository:&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%2Fi%2Fzmxvb97n8cprse575hd2.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%2Fi%2Fzmxvb97n8cprse575hd2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To manage branches, merges, tags, logs, etc, use &lt;em&gt;Git&lt;/em&gt; commands. You can find the full list of Git commands &lt;a href="https://git-scm.com/docs" rel="noopener noreferrer"&gt;here&lt;/a&gt; and one of my favorite Git cheat sheet &lt;a href="https://about.gitlab.com/images/press/git-cheat-sheet.pdf" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;view information about current repository&lt;/strong&gt;, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;git remote show origin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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%2Fi%2Fmsnam5bg8v74srkv4u7i.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%2Fi%2Fmsnam5bg8v74srkv4u7i.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, it you want to know how to migrating a repository from GitHub into AWS CodeCommit via AWS CLI, check &lt;a href="https://dev.to/tiamatt/migrating-a-repository-from-github-into-aws-codecommit-via-aws-cli-2ne4"&gt;this&lt;/a&gt; article.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Create AWS CodeCommit trigger to send notification via Amazon SNS&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can configure a AWS CodeCommit repository so that code pushes or other events trigger notification from Amazon SNS. Beware of limitations, as AWS allows up to 10 triggers for each CodeCommit repository.&lt;/p&gt;

&lt;p&gt;Let's configure "MyDemo2Repo" repository in AWS CodeCommit so that any event triggers Amazon SNS to send a notification to email.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1. Give an IAM user some permissions to manage Amazon SNS
&lt;/h4&gt;

&lt;p&gt;An IAM user named 'code-commit-user' doesn't have enough permissions to manage an Amazon SNS. In addition to AWSCodeCommitFullAccess policy you might want to attach &lt;strong&gt;AmazonSNSFullAccess&lt;/strong&gt; policy to that user:&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%2Fi%2Fblk6xy35gy6icqvyuccl.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%2Fi%2Fblk6xy35gy6icqvyuccl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2. Create an SNS topic
&lt;/h4&gt;

&lt;p&gt;To create a topic, use the &lt;em&gt;aws sns create-topic&lt;/em&gt; command and specify the name to assign to the topic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws sns create-topic --name my-codecommit-trigger&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a &lt;em&gt;TopicArn&lt;/em&gt; as a response - write it down for step 3:&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%2Fi%2Fx21kcobxqpyndh17vppe.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%2Fi%2Fx21kcobxqpyndh17vppe.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3. Subscribe to a topic
&lt;/h4&gt;

&lt;p&gt;Create a subscription and subscribe to a topic specifying &lt;em&gt;TopicArn&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws sns subscribe --topic-arn arn:aws:sns:{your-region}:{your-accountId}:my-codecommit-trigger --protocol email --notification-endpoint {your-email@blah.blah}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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%2Fi%2F3vz82icsoi6q7cy3eyun.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%2Fi%2F3vz82icsoi6q7cy3eyun.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS immediately sends a confirmation message by email to your address. You need to confirm the subscription.&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%2Fi%2Fuy7n8xkn188jroffeloz.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%2Fi%2Fuy7n8xkn188jroffeloz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once confirmed, your browser should display a notification message with information similar to the following:&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%2Fi%2F1snzugzuiicdpfo1o09h.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%2Fi%2F1snzugzuiicdpfo1o09h.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find a newly created topic on Amazon SNS Console:&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%2Fi%2F4nkhlnjtgpxwbgdyw7f5.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%2Fi%2F4nkhlnjtgpxwbgdyw7f5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4. Create a JSON file with trigger description
&lt;/h4&gt;

&lt;p&gt;Next step is to create a trigger for a CodeCommit repository so that events in that repository trigger notifications from SNS topic. &lt;/p&gt;

&lt;p&gt;Firstly, create a JSON file that specifies SNS topic name, the repository and branches you want to monitor with this trigger, and the events that activate this trigger.&lt;/p&gt;

&lt;p&gt;For example, to create a trigger for a repository named &lt;em&gt;MyDemo2Repo&lt;/em&gt; that publishes &lt;em&gt;all&lt;/em&gt; repository events to an Amazon SNS topic named &lt;em&gt;my-codecommit-trigger&lt;/em&gt; for a &lt;em&gt;master&lt;/em&gt; branch, save the following code in 'my-first-codecommit-trigger.json' file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;repositoryName"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyDemo2Repo"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;triggers"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="pi"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AllEventsTriggerForMyDemo2Repo"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;destinationArn"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:sns:{your-region}:{your-accountId}:my-codecommit-trigger"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customData"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;branches"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;
            &lt;span class="pi"&gt;],&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all"&lt;/span&gt;
            &lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="pi"&gt;}&lt;/span&gt;
    &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our example we used &lt;strong&gt;all&lt;/strong&gt; event type but here is the full list of event types you can create triggers for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;all&lt;/td&gt;
&lt;td&gt;for all events in the specified repository and branches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;updateReference&lt;/td&gt;
&lt;td&gt;for when commits are pushed to the specified repository and branches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;createReference&lt;/td&gt;
&lt;td&gt;for when a new branch or tag is created in the specified repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deleteReference&lt;/td&gt;
&lt;td&gt;for when a branch or tag is deleted in the specified repository&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's test our json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="s"&gt;aws codecommit test-repository-triggers --cli-input-json "file://C:\Users\{your-folders}\my-first-codecommit-trigger.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note, you have to use &lt;strong&gt;file://&lt;/strong&gt; before the json name.&lt;/p&gt;

&lt;p&gt;If successful, this command returns information similar to the following:&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%2Fi%2Fxvmsl3meaxyal56uyc8j.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%2Fi%2Fxvmsl3meaxyal56uyc8j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 5. Create a trigger
&lt;/h4&gt;

&lt;p&gt;Now it's time to create a trigger in AWS CodeCommit that is described in json file. The command is similar to the previous one but instead of &lt;em&gt;'test'&lt;/em&gt; use &lt;em&gt;'put'&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="s"&gt;aws codecommit put-repository-triggers --cli-input-json "file://C:\Users\{your-folders}\my-first-codecommit-trigger.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command returns a configuration ID:&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%2Fi%2Fign9dhoz1rjoo4uleojl.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%2Fi%2Fign9dhoz1rjoo4uleojl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check your email, you should get a very first notification from AWS:&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%2Fi%2Fq8lm3lw04k701mmw62c6.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%2Fi%2Fq8lm3lw04k701mmw62c6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 6. View trigger configuration
&lt;/h4&gt;

&lt;p&gt;If you want to view the configuration of the trigger for target repository, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit get-repository-triggers --repository-name MyDemo2Repo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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%2Fi%2Fdxzll4d3fdy8whdj79l6.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%2Fi%2Fdxzll4d3fdy8whdj79l6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 7. Make some repository changes
&lt;/h4&gt;

&lt;p&gt;To test the functionality of the trigger itself, make and push a commit to the repository where you configured the trigger. You should see a response from the Amazon SNS topic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# navigate to your local repository&lt;/span&gt;
&lt;span class="s"&gt;cd localMyDemo2Repo&lt;/span&gt;
&lt;span class="c1"&gt;# create a text file&lt;/span&gt;
&lt;span class="s"&gt;echo "This is a new file for MyDemo2Repo repository that should trigger a notification from AWS SNS topic" &amp;gt; test-sns.txt&lt;/span&gt;
&lt;span class="c1"&gt;# stage the file for commit to your local repository&lt;/span&gt;
&lt;span class="s"&gt;git add .&lt;/span&gt;
&lt;span class="c1"&gt;# commit the file that you've staged&lt;/span&gt;
&lt;span class="s"&gt;git commit -m"Add a new test-sns file"&lt;/span&gt;
&lt;span class="c1"&gt;# push the changes in your local repository to AWS CodeCommit&lt;/span&gt;
&lt;span class="s"&gt;git push origin master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, a new "test-sns.txt" file should be added to remote repository and a new notification from AWS should be sent to your email:&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%2Fi%2Fm2d4of2xgmieid5si0az.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%2Fi%2Fm2d4of2xgmieid5si0az.png" alt="Alt Text"&gt;&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%2Fi%2F0pc2q3xbpfwoeunoxoa2.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%2Fi%2F0pc2q3xbpfwoeunoxoa2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Clean up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To avoid ongoing charges for resources you created to complete this guide, let's delete our repository and sns topic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# get the list of all subscriptions&lt;/span&gt;
&lt;span class="s"&gt;aws sns list-subscriptions&lt;/span&gt;
&lt;span class="c1"&gt;# from the list above get a target SubscriptionArn and use it in command below to unsubscribe from a topic and stop receiving messages published to that topic&lt;/span&gt;
&lt;span class="s"&gt;aws sns unsubscribe --subscription-arn {your-SubscriptionArn }&lt;/span&gt;
&lt;span class="c1"&gt;# get the list of all sns topics&lt;/span&gt;
&lt;span class="s"&gt;aws sns list-topics&lt;/span&gt;
&lt;span class="c1"&gt;# from the list above get a target TopicArn and use it in command below to delete SNS topic&lt;/span&gt;
&lt;span class="s"&gt;aws sns delete-topic --topic-arn {your-TopicArn}&lt;/span&gt;
&lt;span class="c1"&gt;# get the list of all repositories&lt;/span&gt;
&lt;span class="s"&gt;aws codecommit list-repositories&lt;/span&gt;
&lt;span class="c1"&gt;# from the list above get a target repositoryName and use it in command below to delete repository &lt;/span&gt;
&lt;span class="s"&gt;aws codecommit delete-repository --repository-name MyDemo2Repo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All done! Till the next time.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>git</category>
      <category>cloud</category>
      <category>awscodecommit</category>
    </item>
    <item>
      <title>Migrating a repository from GitHub into AWS CodeCommit via AWS CLI</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Sun, 27 Dec 2020 22:41:31 +0000</pubDate>
      <link>https://dev.to/tiamatt/migrating-a-repository-from-github-into-aws-codecommit-via-aws-cli-2ne4</link>
      <guid>https://dev.to/tiamatt/migrating-a-repository-from-github-into-aws-codecommit-via-aws-cli-2ne4</guid>
      <description>&lt;p&gt;Actually, you can migrate your repository not only from GitHub but also from any &lt;strong&gt;git-based&lt;/strong&gt; source control service such as GitLab, BitBucket, Beanstalk (don’t confuse it with AWS Elastic Beanstalk). In order to migrate repository from &lt;em&gt;non git-based&lt;/em&gt; source control service (such as Subversion, TFS, Perforce) you will have to migrate to a git-based service first. Also you can choose to migrate the whole repository or just some of the branches.&lt;/p&gt;

&lt;p&gt;In this article I'll show how to migrate one of GitHub's &lt;a href="https://github.com/aws-samples/aws-glue-samples" rel="noopener noreferrer"&gt;AWS Samples repositories&lt;/a&gt; to AWS CodeCommit.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1. Create a new repository in AWS CodeCommit
&lt;/h4&gt;

&lt;p&gt;You need to create an empty repository in AWS CodeCommit where you want to migrate a GitHub repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;aws codecommit create-repository --repository-name MyMigratedRepo --repository-description "This is a repo that was migrated from GitHub"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2. Clone a target repository from GitHub to your local machine
&lt;/h4&gt;

&lt;p&gt;First you need to get a target repository’s url from GitHub:&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%2Fi%2Fg0vxroj9uprum92kojqm.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%2Fi%2Fg0vxroj9uprum92kojqm.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# navigate to a directory where you want to save a new repository&lt;/span&gt;
&lt;span class="s"&gt;cd "{my-full-path-to-destination-folder}"&lt;/span&gt;
&lt;span class="c1"&gt;# clone a repository from GitHub to your local machine&lt;/span&gt;
&lt;span class="s"&gt;git clone --mirror https://github.com/aws-samples/aws-glue-samples.git myMigrationRepo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3. Push a cloned repository to AWS CodeCommit
&lt;/h4&gt;

&lt;p&gt;First you need to get a destination repository’s url from AWS CodeCommit:&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%2Fi%2F3i36f48x9dpr6vchyvm7.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%2Fi%2F3i36f48x9dpr6vchyvm7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then run the following commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;git push https://git-codecommit.{my-region}.amazonaws.com/v1/repos/MyMigratedRepo --all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 4. Enjoy the results
&lt;/h4&gt;

&lt;p&gt;Voila! We have just migrated a repository from GitHub into AWS CodeCommit:&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%2Fi%2Flf896jwhjk43f3dp3b9a.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%2Fi%2Flf896jwhjk43f3dp3b9a.png" alt="Alt Text"&gt;&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%2Fi%2F7ksyp9asum0ho2gg3tjx.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%2Fi%2F7ksyp9asum0ho2gg3tjx.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>git</category>
      <category>cloud</category>
      <category>awscodecommit</category>
    </item>
    <item>
      <title>Hands-on AWS CloudFormation - Part 4. Create VPC with private and public subnets</title>
      <dc:creator>Samira Yusifova</dc:creator>
      <pubDate>Tue, 22 Dec 2020 09:19:09 +0000</pubDate>
      <link>https://dev.to/tiamatt/hands-on-aws-cloudformation-part-4-create-vpc-with-private-and-public-subnets-85d</link>
      <guid>https://dev.to/tiamatt/hands-on-aws-cloudformation-part-4-create-vpc-with-private-and-public-subnets-85d</guid>
      <description>&lt;p&gt;Based on the knowledge from all previous parts of &lt;strong&gt;Hands-on AWS CloudFormation&lt;/strong&gt; series let's create something simple, essential and cool!&lt;/p&gt;

&lt;h1&gt;
  
  
  A virtual network in the AWS cloud
&lt;/h1&gt;

&lt;p&gt;When you provision a service within a public cloud, it is effectively open to the world and can be at risk of attacks from the internet. In order to secure resources against attacks from the outside, you lock them within a VPC. VPC restricts what sort of traffic, IP addresses and also the users that can access your resources. In other words, VPC is your network but in the cloud.&lt;/p&gt;

&lt;p&gt;Just to be clear, AWS has already created a &lt;strong&gt;default VPC&lt;/strong&gt; for you to use so that you don't have to create and configure your own VPC. But, by default its subnet in each AZ is a &lt;strong&gt;public&lt;/strong&gt; subnet, because the main route table sends the subnet's traffic that is destined for the internet to the internet gateway. &lt;/p&gt;

&lt;p&gt;This tutorial walks through how to create &lt;em&gt;a fully functional custom VPC from scratch with a pair of public and private subnets spread across two AZs&lt;/em&gt; using AWS CloudFormation. Here are core components that we are going to create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a VPC&lt;/li&gt;
&lt;li&gt;an Internet Gateway&lt;/li&gt;
&lt;li&gt;a custom route table for all public subnets&lt;/li&gt;
&lt;li&gt;two public subnets spread across two AZs&lt;/li&gt;
&lt;li&gt;two NAT Gateways attached to public subnets&lt;/li&gt;
&lt;li&gt;two custom route tables for each of private subnets&lt;/li&gt;
&lt;li&gt;two private subnets spread across two AZs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end we will get the following architecture:&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%2Fi%2Fqox98thhho08dfdtyqud.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%2Fi%2Fqox98thhho08dfdtyqud.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s get started!&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 1. Create VPC and Internet Gateway
&lt;/h1&gt;

&lt;p&gt;Firstly, we start by creating a &lt;strong&gt;VPC&lt;/strong&gt; with a size /16 IPv4 CIDR block. This provides up to 65,536 private IPv4 addresses. We might hard code the CIDR block for VPC but it’s better to define it as an input parameter so a user can either customize the IP ranges or use a default value. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paramVpcCIDR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enter the IP range (CIDR notation) for VPC&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.192.0.0/16&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# a) Create a VPC&lt;/span&gt;
  &lt;span class="na"&gt;myVPC&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::VPC&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramVpcCIDR&lt;/span&gt;
      &lt;span class="na"&gt;EnableDnsSupport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;EnableDnsHostnames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFormationLab&lt;/span&gt;
        &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt; &lt;em&gt;CidrBlock&lt;/em&gt; - the IP address range available to our VPC&lt;/li&gt;
&lt;li&gt; &lt;em&gt;EnableDnsSupport&lt;/em&gt; - if set to true, AWS will resolve DNS hostnames to any instance within the VPC’s IP address&lt;/li&gt;
&lt;li&gt; &lt;em&gt;EnableDnsHostnames&lt;/em&gt; - if set to true, instances get allocated DNS hostnames by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Secondly, we need to create an &lt;strong&gt;Internet Gateway&lt;/strong&gt;. An Internet Gateway is a logical connection between a VPC and the Internet. If there is no Internet Gateway, then there is no connection between the VPC and the Internet.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="c1"&gt;# b) Create a Internet Gateway&lt;/span&gt;
  &lt;span class="na"&gt;myInternetGateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::InternetGateway&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CloudFormationLab&lt;/span&gt;
        &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And finally, let’s attach the Internet Gateway to the VPC&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="c1"&gt;# c) Attach the Internet Gateway to the VPC&lt;/span&gt;
  &lt;span class="na"&gt;myVPCGatewayAttachment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::VPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;InternetGatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myInternetGateway&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;With that, we have already built a very basic VPC. &lt;/p&gt;

&lt;h1&gt;
  
  
  Step 2. Create a public route table and public subnets across two AZs
&lt;/h1&gt;

&lt;p&gt;Placing our instances in multiple AZs increase the availability of our resources and improves the fault tolerance in our applications. If one AZ experiences an outage, you might want to redirect the traffic to the other AZ. Having said that, we are going to follow AWS best practices to create subnets - each in a different AZ.&lt;/p&gt;

&lt;p&gt;Firstly, let’s create a custom route table for all &lt;em&gt;public&lt;/em&gt; subnets and name it &lt;strong&gt;public route table&lt;/strong&gt;. We need it to control the routing for the public subnets which we are about to create. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="c1"&gt;# a) Create a public route table for the VPC (will be public once it is associated with the Internet Gateway)&lt;/span&gt;
  &lt;span class="na"&gt;myPublicRouteTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Secondly, we need to add a new &lt;strong&gt;route&lt;/strong&gt; to the public route table that points all traffic (0.0.0.0/0) to the Internet Gateway.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="c1"&gt;# b) Associate the public route table with the Internet Gateway&lt;/span&gt;
  &lt;span class="na"&gt;myPublicRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myVPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPublicRouteTable&lt;/span&gt;
      &lt;span class="na"&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class="na"&gt;GatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myInternetGateway&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt; &lt;em&gt;DestinationCidrBlock&lt;/em&gt; - it specifies which traffic we want this route to be applied to. In this case, we apply it to all traffic using the 0.0.0.0/0 CIDR block&lt;/li&gt;
&lt;li&gt; &lt;em&gt;GatewayId&lt;/em&gt; - it specifies where traffic matching the CIDR block should be directed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note, when you add a &lt;strong&gt;DependsOn&lt;/strong&gt; attribute to a resource, that resource is created only &lt;em&gt;after&lt;/em&gt; the creation of the resource specified in the DependsOn attribute.&lt;/p&gt;

&lt;p&gt;Thirdly, once we are done with the public route table, time to create &lt;strong&gt;two public subnets&lt;/strong&gt; with a size /24 IPv4 CIDR block in each of two AZs. This provides up to 256 addresses per subnet, a few of which are reserved for AWS use.&lt;/p&gt;

&lt;p&gt;It’s better to define CIDR blocks for both subnets as input parameters so a user can either customize the IP ranges or use a default range.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# etc&lt;/span&gt;
  &lt;span class="na"&gt;paramPublicSubnet1CIDR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enter the IP range (CIDR notation)  for the public subnet in AZ A&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.192.10.0/24&lt;/span&gt;
  &lt;span class="na"&gt;paramPublicSubnet2CIDR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enter the IP range (CIDR notation)  for the public subnet in AZ B&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.192.11.0/24&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Important!&lt;/em&gt; The CIDR blocks of two subnets &lt;strong&gt;must not overlap&lt;/strong&gt; with the CIDR block that's associated with the VPC.&lt;/p&gt;

&lt;p&gt;Subnets must exist within a VPC, so this is how we associate these two subnets within our VPC:  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

   &lt;span class="c1"&gt;# c) Create a public subnet in AZ 1 (will be public once it is associated with public route table)&lt;/span&gt;
  &lt;span class="na"&gt;myPublicSubnet1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;AvailabilityZone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Select&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;!GetAZs&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# AZ 1&lt;/span&gt;
      &lt;span class="na"&gt;CidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramPublicSubnet1CIDR&lt;/span&gt;
      &lt;span class="na"&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;
  &lt;span class="c1"&gt;# Create a public subnet in AZ 2 (will be public once it is associated with public route table)&lt;/span&gt;
  &lt;span class="na"&gt;myPublicSubnet2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;AvailabilityZone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Select&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;!GetAZs&lt;/span&gt;  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# AZ 2&lt;/span&gt;
      &lt;span class="na"&gt;CidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramPublicSubnet2CIDR&lt;/span&gt;
      &lt;span class="na"&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;By setting &lt;em&gt;MapPublicIpOnLaunch&lt;/em&gt; to true instances launched into the subnet will be allocated a public IP address by default. This means that any instances in this subnet will be reachable from the Internet via the Internet Gateway attached to the VPC.&lt;/p&gt;

&lt;p&gt;And finally, it’s important to associate the route table with both public subnets:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="c1"&gt;# d) Associate the public route table with the public subnet in AZ 1&lt;/span&gt;
  &lt;span class="na"&gt;myPublicSubnet1RouteTableAssociation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPublicRouteTable&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPublicSubnet1&lt;/span&gt;
  &lt;span class="c1"&gt;# Associate the public route table with the public subnet in AZ 2&lt;/span&gt;
  &lt;span class="na"&gt;myPublicSubnet2RouteTableAssociation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPublicRouteTable&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPublicSubnet2&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;At that point, we are done with public subnets, now let’s create private subnets&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 3. Create NAT Gateways, private route tables and private subnets across two AZs
&lt;/h1&gt;

&lt;p&gt;We don't want instances within our private subnets to be reachable from the public internet.  But we do want these instances to be able to initiate outbound connections.  Also we want them to be able to do this without having public IP addresses. That is when NAT gateway comes in handy.&lt;/p&gt;

&lt;p&gt;NAT gateway enables instances in a private subnet to connect to the internet or other AWS services, but prevent the internet from initiating a connection with those instances. It will have a public IP address and will be associated with a public subnet.  Private instances in private subnets will be able to use this to initiate outbound connections.  But the NAT will not allow the reverse, a party on the public internet cannot use the NAT to connect to our private instances. &lt;/p&gt;

&lt;p&gt;Thus, we need &lt;strong&gt;an Elastic IP (EIP) address&lt;/strong&gt; and a &lt;strong&gt;NAT gateway&lt;/strong&gt;. Not even one, but two! Because a single NAT Gateway in a single AZ has redundancy within that AZ only, so if there are any zonal issues then instances in other AZs would have no route to the internet. To remain highly available, we need a NAT Gateway in each AZ and &lt;strong&gt;a different route table&lt;/strong&gt; for each AZ.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# a) Specify an Elastic IP (EIP) address for a NAT Gateway in AZ 1&lt;/span&gt;
  &lt;span class="na"&gt;myEIPforNatGateway1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::EIP&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myVPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vpc&lt;/span&gt;
  &lt;span class="c1"&gt;# Specify an Elastic IP (EIP) address for a NAT Gateway in AZ 2&lt;/span&gt;
  &lt;span class="na"&gt;myEIPforNatGateway2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::EIP&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myVPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vpc&lt;/span&gt;

  &lt;span class="c1"&gt;# b) Create a NAT Gateway in the public subnet for AZ 1&lt;/span&gt;
  &lt;span class="na"&gt;myNatGateway1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::NatGateway&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AllocationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myEIPforNatGateway1.AllocationId&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPublicSubnet1&lt;/span&gt;
   &lt;span class="c1"&gt;# Create a NAT Gateway in the public subnet for AZ 2&lt;/span&gt;
  &lt;span class="na"&gt;myNatGateway2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::NatGateway&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AllocationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;myEIPforNatGateway2.AllocationId&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPublicSubnet2&lt;/span&gt;

  &lt;span class="c1"&gt;# c) Create a private route table for AZ 1&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateRouteTable1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;
  &lt;span class="c1"&gt;# Create a private route table for AZ 2&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateRouteTable2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::RouteTable&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;

  &lt;span class="c1"&gt;# d) Associate the private route table with the Nat Gateway in AZ 1&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateRouteForAz1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myVPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPrivateRouteTable1&lt;/span&gt;
      &lt;span class="na"&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class="na"&gt;NatGatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myNatGateway1&lt;/span&gt;       
  &lt;span class="c1"&gt;#  Associate the private route table with the Nat Gateway in AZ 2&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateRouteForAz2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Route&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myVPCGatewayAttachment&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPrivateRouteTable2&lt;/span&gt;
      &lt;span class="na"&gt;DestinationCidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;
      &lt;span class="na"&gt;NatGatewayId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myNatGateway2&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the sample above, we created and specified an Elastic IP address to associate with the NAT gateway for each AZ. After creating a NAT gateway, we created and updated the route table associated with each private subnet to point internet-bound traffic to the NAT gateway. This enables instances in our private subnets to communicate with the internet.&lt;/p&gt;

&lt;p&gt;And our final step is to create &lt;strong&gt;two private subnets&lt;/strong&gt; with a size /24 IPv4 CIDR block in each of two AZs. And then associate the private route table with the private subnet for each AZ.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# etc&lt;/span&gt;
  &lt;span class="na"&gt;paramPrivateSubnet1CIDR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enter the IP range (CIDR notation)  for the private subnet in AZ A&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.192.20.0/24&lt;/span&gt;
  &lt;span class="na"&gt;paramPrivateSubnet2CIDR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enter the IP range (CIDR notation)  for the private subnet in AZ B&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.192.21.0/24&lt;/span&gt;
  &lt;span class="c1"&gt;# e) Create a private subnet in AZ 1&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateSubnet1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;AvailabilityZone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Select&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;!GetAZs&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# AZ 1&lt;/span&gt;
      &lt;span class="na"&gt;CidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramPrivateSubnet1CIDR&lt;/span&gt;
      &lt;span class="na"&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;
  &lt;span class="c1"&gt;# Create a private subnet in AZ 2&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateSubnet2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Subnet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VpcId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
      &lt;span class="na"&gt;AvailabilityZone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Select&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;!GetAZs&lt;/span&gt;  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# AZ 2&lt;/span&gt;
      &lt;span class="na"&gt;CidrBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramPrivateSubnet2CIDR&lt;/span&gt;
      &lt;span class="na"&gt;MapPublicIpOnLaunch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;paramUniqueName&lt;/span&gt;

  &lt;span class="c1"&gt;# f) Associate the private route table with the private subnet in AZ 1&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateSubnet1RouteTableAssociation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPrivateRouteTable1&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPrivateSubnet1&lt;/span&gt;
  &lt;span class="c1"&gt;#  Associate the private route table with the private subnet in AZ 2&lt;/span&gt;
  &lt;span class="na"&gt;myPrivateSubnet2RouteTableAssociation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::SubnetRouteTableAssociation&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RouteTableId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPrivateRouteTable2&lt;/span&gt;
      &lt;span class="na"&gt;SubnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myPrivateSubnet2&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Optionally, we can add an &lt;em&gt;Output&lt;/em&gt; section to get the list of newly created private and public subnets&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;outputVPC&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A reference to the created VPC&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;myVPC&lt;/span&gt;
  &lt;span class="na"&gt;outputPublicSubnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A list of the public subnets&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;myPublicSubnet1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;myPublicSubnet2&lt;/span&gt; &lt;span class="pi"&gt;]]&lt;/span&gt;
  &lt;span class="na"&gt;outputPrivateSubnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A list of the private subnets&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;myPrivateSubnet1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;myPrivateSubnet2&lt;/span&gt; &lt;span class="pi"&gt;]]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/Tiamatt/Mastering-AWS-CloudFormation/blob/main/3-VpsWithPublicAndPrivateSubnets.yaml" rel="noopener noreferrer"&gt;GitHub link&lt;/a&gt; to the whole template.&lt;/p&gt;

&lt;p&gt;Below, we see our AWS CloudFormation stack with the list of newly created resources:&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%2Fi%2Fqrsv9tgjk3c5yjk40uxz.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%2Fi%2Fqrsv9tgjk3c5yjk40uxz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you need to understand how to create a stack in AWS Console, please see &lt;a href="https://dev.to/tiamatt/hands-on-aws-cloudformation-part-1-it-all-starts-here-5153"&gt;Part 1&lt;/a&gt; of this series. &lt;/p&gt;

&lt;p&gt;Look! We have a stack. At the end we got four subnets, including two public and two private within a newly created 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%2Fi%2Flux4h8olnsompwht24nk.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%2Fi%2Flux4h8olnsompwht24nk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;If you made it all the way to the end, congrats, and happy CloudFormation construction! &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%2Fi%2Fwgsl4q3d66f9hsa6hzrp.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%2Fi%2Fwgsl4q3d66f9hsa6hzrp.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our VPC template allows to create two public and two private subnets, in different AZs for redundancy using AWS CloudFormation. It builds a private networking environment in which you can securely run AWS resources, along with related networking resources. &lt;/p&gt;

&lt;p&gt;The ability to create, modify, and delete a stack of resources through AWS CloudFormation is a powerful example of the Infrastructure as Code concept.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>cloudformation</category>
    </item>
  </channel>
</rss>
