<?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: Faizan Raza</title>
    <description>The latest articles on DEV Community by Faizan Raza (@faizanraza_interweave).</description>
    <link>https://dev.to/faizanraza_interweave</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%2F713570%2F40023e77-8864-41a1-8a18-4c4a12cd987d.jpg</url>
      <title>DEV Community: Faizan Raza</title>
      <link>https://dev.to/faizanraza_interweave</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faizanraza_interweave"/>
    <language>en</language>
    <item>
      <title>What Are AWS CDK Constructs, Stacks and How To Use Them</title>
      <dc:creator>Faizan Raza</dc:creator>
      <pubDate>Tue, 17 May 2022 08:21:57 +0000</pubDate>
      <link>https://dev.to/faizanraza_interweave/what-are-aws-cdk-constructs-stacks-and-how-to-use-them-169c</link>
      <guid>https://dev.to/faizanraza_interweave/what-are-aws-cdk-constructs-stacks-and-how-to-use-them-169c</guid>
      <description>&lt;p&gt;When getting started with the AWS CDK, one of the most confusing aspects are the new terminologies of apps, stacks and constructs. I will compare constructs vs stacks vs apps to explain what they are and when to use them.  &lt;/p&gt;

&lt;h2&gt;
  
  
  At a glance
&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%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A03cypdSBTMcSQKwxq7-jhw.jpeg" 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%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A03cypdSBTMcSQKwxq7-jhw.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A table comparing apps vs stacks vs constructs&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%2Fmiro.medium.com%2Fmax%2F1400%2F0%2ARSk7GUi_RtbvfOky.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%2Fmiro.medium.com%2Fmax%2F1400%2F0%2ARSk7GUi_RtbvfOky.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A sample of how constructs, stacks and apps may be organised&lt;/p&gt;

&lt;h2&gt;
  
  
  Constructs
&lt;/h2&gt;

&lt;p&gt;AWS CDK constructs at their core represent one or more AWS resources - however, these come in three common and distinct flavours:&lt;/p&gt;

&lt;h3&gt;
  
  
  Level 1 Constructs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What are they
&lt;/h4&gt;

&lt;p&gt;Level 1 constructs correspond directly to a single AWS resource, exactly how it is described within AWS Cloudformation. Examples of this would a single S3 Bucket, a Lambda function and a DynamoDB table.&lt;/p&gt;

&lt;h4&gt;
  
  
  When to use
&lt;/h4&gt;

&lt;p&gt;When a new AWS service or resource is added to the AWS CDK, it initially is added as a level 1 construct. However, they are usually then followed by superior level 2 constructs described below. Level 1 constructs should primarily be used when level 2 constructs are not available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Level 2 Constructs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What are they
&lt;/h4&gt;

&lt;p&gt;Level 2 constructs are built on top of level 1 constructs, however, these include quality of life and usability improvements around a particular service. The &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html#logretention" rel="noopener noreferrer"&gt;lambda function construct&lt;/a&gt; is a prime example of this. The &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.CfnFunction.html" rel="noopener noreferrer"&gt;level 1 Lambda function construct&lt;/a&gt; requires a pre-existing s3 bucket object, containing the compressed Lambda source code and will only create the Lambda function resource itself.&lt;br&gt;&lt;br&gt;
The level 2 construct, however, requests the location of the uncompressed source code locally, automatically uploads this to an S3 bucket for you and then deploys this within the lambda function. Additionally, it will enable CloudWatch logs out of the box and give the lambda function the correct associated permissions.&lt;/p&gt;

&lt;h4&gt;
  
  
  When to use
&lt;/h4&gt;

&lt;p&gt;Level 2 constructs are what you will mostly be working with when creating your AWS solution. The AWS CDK team put a lot of effort into providing these and are generally of very high calibre. It is possible to write your own but it is unlikely this will be required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Level 3 Constructs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What are they
&lt;/h4&gt;

&lt;p&gt;Level 3 constructs are opinionated units of reusable code which often deliver a service. Open source level 3 constructs are available from &lt;a href="https://docs.aws.amazon.com/solutions/latest/constructs/welcome.html" rel="noopener noreferrer"&gt;AWS Solutions Constructs&lt;/a&gt; - an extension to the CDK, &lt;a href="https://cdkpatterns.com/" rel="noopener noreferrer"&gt;CDK Patterns&lt;/a&gt; - a library of well-architected patterns, &lt;a href="https://constructs.dev/" rel="noopener noreferrer"&gt;Construct Hub&lt;/a&gt; - a library of open-source constructs and some even come out of the box with the CDK - these are appended with 'patterns' to make them identifiable. Two examples are shown 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%2Fmiro.medium.com%2Fmax%2F1400%2F0%2AX6xYSvraGXzsMHks.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%2Fmiro.medium.com%2Fmax%2F1400%2F0%2AX6xYSvraGXzsMHks.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cdk-patterns/serverless/blob/main/the-simple-webservice/README.md" rel="noopener noreferrer"&gt;A simple web service using API Gateway, Lambda and DynamoDB&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%2Fmiro.medium.com%2Fmax%2F1400%2F0%2A9wZIsqrZCtcBsR_-.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%2Fmiro.medium.com%2Fmax%2F1400%2F0%2A9wZIsqrZCtcBsR_-.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cdk-patterns/serverless/tree/main/s3-react-website" rel="noopener noreferrer"&gt;A Static React/Angular Single Page Application Website&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  When to use
&lt;/h4&gt;

&lt;p&gt;If you are able to find a level 3 construct which matches your desired architecture - you should use this level 3 construct. However, if one does not exist, you should evaluate whether your specific pattern is likely to be reused - either within your code or within your organisation. If the answer is yes, then you should be writing your own level 3 construct from the available level 1 and 2 constructs. If you are working on any enterprise or large scale project, you will find the majority of your time spent with the CDK is spent creating level 3 constructs.  &lt;/p&gt;

&lt;h4&gt;
  
  
  Bonus: Tips for Building L3 Custom Constructs
&lt;/h4&gt;

&lt;p&gt;While a construct should be opinionated and achieve one goal very well, it is important to add properties to allow configuration of the underlying resources and expose the resources created. For example, if you could not configure a lambda function's resources, it would prevent anyone who needs a larger function from using your construct.&lt;/p&gt;

&lt;p&gt;But do not expose excessive amounts of the underlying infrastructure to the user to nullify any abstractions you provided. Recall how the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html#logretention" rel="noopener noreferrer"&gt;Lambda Function construct&lt;/a&gt; automatically uploads the source code to an S3 bucket and then uses this in the lambda function. Providing the object name or path of the uploaded source code as a configurable option would be irrelevant to this construct and unnecessarily leak this abstraction to the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stacks
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What are they
&lt;/h4&gt;

&lt;p&gt;Stacks correspond exactly to one single CloudFormation stack and the corresponding assets. The defining feature of a stack is that it is a unit of deployment - you can deploy stacks independently of each other, but all of the resources within a stack will be deployed together.&lt;/p&gt;

&lt;h4&gt;
  
  
  When to use
&lt;/h4&gt;

&lt;p&gt;You should use stacks to combine resources which should be deployed together. A common split would be having frontend and backend resources in a separate stack. Additionally, having frequently changes and infrequently changed resources in separate stacks. For example, you may not want your infrequently changing networking and database layers in the same stack as your frequently changing compute and content layers.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Apps
&lt;/h3&gt;

&lt;p&gt;Apps are the root object and contain stacks. The key feature here is that stacks housed within the same app can share resources with each other. For example, a backend stack can share resources with the front end stack. Multiple apps can exist in a single project folder, however:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CDK can only deploy or synthesise one stack at a time, whereas it can deploy multiple stacks.&lt;/li&gt;
&lt;li&gt;Two apps can not be defined in the same file - they must live in separate files&lt;/li&gt;
&lt;li&gt;Two apps can not reference each other's resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  When to use
&lt;/h4&gt;

&lt;p&gt;This is always the root object in a CDK app and should contain all the stacks required for an application. Whether or not to house a stack in a separate app comes down to whether it needs to share resources with any other stacks. Examples of this could be load testing stacks and infrastructure testing stacks - these would most likely not need to have access to resources within the front end and backend stacks so could sit in a different app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Me
&lt;/h2&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/faizanrazainterweave/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>awscdk</category>
      <category>aws</category>
    </item>
    <item>
      <title>How to host a static website on AWS with S3, CloudFront, Route53 and Terraform</title>
      <dc:creator>Faizan Raza</dc:creator>
      <pubDate>Mon, 16 May 2022 08:03:30 +0000</pubDate>
      <link>https://dev.to/faizanraza_interweave/how-to-host-a-static-website-on-aws-with-s3-cloudfront-route53-and-terraform-2lph</link>
      <guid>https://dev.to/faizanraza_interweave/how-to-host-a-static-website-on-aws-with-s3-cloudfront-route53-and-terraform-2lph</guid>
      <description>&lt;h1&gt;
  
  
  How to host a static website on AWS with S3, CloudFront, Route53 and Terraform
&lt;/h1&gt;

&lt;p&gt;Hosting a website on AWS using Cloudfront, S3 and Route53 is a popular solution for Amazon web hosting. It typically costs $3 a month outside of the generous free tier and $0.50 within the free tier. Terraform can automate the deployment of this finicky architecture making it much easier to deploy, manage and automatically upload website content. We will use an open-source Terraform module to deploy a secure, scalable and performant website on AWS based on Route 53, CloudFront and S3.&lt;/p&gt;

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

&lt;p&gt;The vast majority of Terraform tutorials I see covering similar topics all focus on recreating an identical architecture from stack utilising resources which is simply a waste of time and effort. Existing tutorials and modules generally follow the architecture shown below which is based on the AWS tutorials &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/HostingWebsiteOnS3Setup.html" rel="noopener noreferrer"&gt;Configuring a static website on Amazon S3&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/website-hosting-cloudfront-walkthrough.html" rel="noopener noreferrer"&gt;Speeding up your website with Amazon CloudFront&lt;/a&gt;. Since they are based on tutorials, the following flaws remain:&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%2Finterweave.cloud%2Fwp-content%2Fuploads%2F2022%2F05%2FArchitectures.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%2Finterweave.cloud%2Fwp-content%2Fuploads%2F2022%2F05%2FArchitectures.png" alt="The architecture that most tutorials and guides follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The architecture that most tutorials and guides follow&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two buckets are created when only one is really required.&lt;/li&gt;
&lt;li&gt;The root bucket is publicly exposed. Public S3 buckets have been at the centre of several high profile data leaks. Due to this, it is increasingly becoming corporate policy to completely disallow public S3 buckets.&lt;/li&gt;
&lt;li&gt;The users still have the ability to access the bucket objects directly from the s3 website endpoint. This circumvents the CloudFront distribution and can nullify CloudFront features like geographic restrictions.&lt;/li&gt;
&lt;li&gt;A manual activity remains to upload the website content onto the S3 bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Finterweave.cloud%2Fwp-content%2Fuploads%2F2022%2F05%2FArchitectures-Proposed-Architecture.drawio.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%2Finterweave.cloud%2Fwp-content%2Fuploads%2F2022%2F05%2FArchitectures-Proposed-Architecture.drawio.png" alt="The improved architecture of what will be deployed in this guide"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The improved architecture of what will be deployed in this guide&lt;/p&gt;

&lt;p&gt;My module "&lt;a href="https://registry.terraform.io/modules/InterweaveCloud/s3-cloudfront-static-website/aws/latest" rel="noopener noreferrer"&gt;s3-cloudfront-static-website&lt;/a&gt;" uses the architecture shown above which represents a much simpler, secure and straightforward architecture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This only uses one bucket for both the root and &lt;a href="http://www" rel="noopener noreferrer"&gt;www&lt;/a&gt;. domains.&lt;/li&gt;
&lt;li&gt;The bucket is private and CloudFront accesses the website content utilising an Origin Access Identity with IAM permissions to the S3 Bucket.&lt;/li&gt;
&lt;li&gt;Users can only access the website through CloudFront.&lt;/li&gt;
&lt;li&gt;Website content can be automatically uploaded during Terraform deployment (uses AWS CLI).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Domain name&lt;/li&gt;
&lt;li&gt;Route 53 Hosted Zone which is the DNS Provider for the domain&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; locally installed with a configured named profile&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.hashicorp.com/tutorials/terraform/install-cli" rel="noopener noreferrer"&gt;Terraform CLI locally installed&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you require assistance with the pre-requisites review the following subsections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain Name
&lt;/h3&gt;

&lt;p&gt;To use a domain name, you must purchase the domain from a registrar like &lt;a href="https://domains.google/" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, &lt;a href="http://Go%20Daddy" rel="noopener noreferrer"&gt;Go Daddy&lt;/a&gt;, &lt;a href="http://www.123-reg.co.uk" rel="noopener noreferrer"&gt;123-Reg&lt;/a&gt;, &lt;a href="http://www.names.co.uk" rel="noopener noreferrer"&gt;names.co.uk&lt;/a&gt; or &lt;a href="https://us-east-1.console.aws.amazon.com/route53/home#DomainRegistration:" rel="noopener noreferrer"&gt;AWS Route 53&lt;/a&gt;. Using AWS Route 53 is easiest since AWS will automatically create a hosted zone for the domain, assign four name servers to the hosted zone, and then update the domain registration to use those name servers. However, it is normally the most expensive option for a domain. I personally browse for the best price for my desired main domain and find names.co.uk is a great site for free domains for testing or development. Beware of the higher prices after the first year. I also turn off auto-renewal - especially for free domains.&lt;/p&gt;

&lt;h3&gt;
  
  
  Route 53 Hosted Zone as DNS Service for the Domain
&lt;/h3&gt;

&lt;p&gt;If you bought your domain through AWS Route 53, you can skip this section. If you bought the domain from another website, you need to create a route 53 hosted zone and make &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/migrate-dns-domain-inactive.html" rel="noopener noreferrer"&gt;Route 53 the DNS service for your domain&lt;/a&gt;. Put simply, the &lt;a href="https://aws.amazon.com/route53/what-is-dns/" rel="noopener noreferrer"&gt;Domain Name Service (DNS)&lt;/a&gt; is the phonebook of the internet, when a user inputs your domain name, DNS will return the correct IP address to access your site. &lt;a href="https://kinsta.com/knowledgebase/what-is-a-nameserver/" rel="noopener noreferrer"&gt;Name servers&lt;/a&gt; are underlying servers which contain and return the information of which IP addresses correspond to which services on your domain. To make Route 53 the hosted zone for your domain, you must change the name servers from your registrar's to your Route 53 hosted zone's.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to create a Route 53 Hosted Zone
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Sign in to the AWS Management Console and open the &lt;a href="https://console.aws.amazon.com/route53/" rel="noopener noreferrer"&gt;Route 53 console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you're new to Route 53, choose &lt;strong&gt;Get started&lt;/strong&gt; under &lt;strong&gt;DNS management&lt;/strong&gt;.
If you're already using Route 53, choose &lt;strong&gt;Hosted zones&lt;/strong&gt; in the navigation pane.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Create hosted zone&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Create Hosted Zone&lt;/strong&gt; pane, enter the name of the domain that you want to route traffic to. You can also optionally enter a comment. The domain name for this website is interweave.cloud - you do not need to include '&lt;a href="http://www." rel="noopener noreferrer"&gt;www.&lt;/a&gt;' or 'HTTPS://'.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Type&lt;/strong&gt;, accept the default value of &lt;strong&gt;Public Hosted Zone&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  Update the name servers for the domain
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;Open the Route 53 console at &lt;a href="https://console.aws.amazon.com/route53/" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/route53/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;In the navigation pane, choose &lt;strong&gt;Hosted zones&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;On the &lt;strong&gt;Hosted zones&lt;/strong&gt; page, choose the radio button (not the name) for the hosted zone, then choose &lt;strong&gt;View details&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;On the details page for the hosted zone, choose &lt;strong&gt;Hosted zone details&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Make note of the four servers listed for &lt;strong&gt;Name servers&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use the method provided by the registrar for the domain to change the name servers for the domain to use the four Route 53 name servers you got from step 5. &lt;em&gt;NOTE: it can take up to 3 days for the change to become effective.&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  AWS CLI
&lt;/h3&gt;

&lt;p&gt;To upload your files to the s3 bucket automatically, the AWS CLI tool is used. Therefore, it is required for the &lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; to be installed on your local machine - instructions can be found &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You also need to configure a local named profile using the &lt;code&gt;aws configure --profile NAME&lt;/code&gt; command. Using named profiles is generally better than static credentials within Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform Code
&lt;/h2&gt;

&lt;p&gt;The terraform module is available on the &lt;a href="https://registry.terraform.io/modules/InterweaveCloud/s3-cloudfront-static-website/aws/latest" rel="noopener noreferrer"&gt;Terraform Registry&lt;/a&gt; with the source code available on &lt;a href="https://github.com/InterweaveCloud/terraform-aws-s3-cloudfront-static-website" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; with &lt;a href="https://github.com/InterweaveCloud/terraform-aws-s3-cloudfront-static-website/tree/main/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; shown. We would like to deploy a static website with the files automatically synced and a custom domain name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Providers.tf
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"4.10.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Default provider for resource creation&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Provider required in useast1 for CloudFront, SSL Certificate&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"useast1"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In providers.tf you will have two providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default provider for the region where your main infrastructure will be deployed.&lt;/li&gt;
&lt;li&gt;'useast1' provider for CloudFront and the SSL certificates. These sit in the us-east-1 region because they are global services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  main.tf
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"s3-cloudfront-static-website&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;_example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"InterweaveCloud/s3-cloudfront-static-website/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;

  &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_uid&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DevOpsNavy"&lt;/span&gt; 
  &lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"XXX"&lt;/span&gt; 
  &lt;span class="nx"&gt;hosted&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_zone&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"XXX"&lt;/span&gt; 
  &lt;span class="nx"&gt;profile&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"XXX"&lt;/span&gt; 

  &lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_directories&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;\[{&lt;/span&gt;
    &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_source&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_directory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./website&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;_content"&lt;/span&gt;
    &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_target&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;_directory&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="p"&gt;}\]&lt;/span&gt; 

  &lt;span class="nx"&gt;providers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useast1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useast1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Variable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;resource_uid&lt;/td&gt;
&lt;td&gt;UID which will be prepended to resources created by this module&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domain_name&lt;/td&gt;
&lt;td&gt;The domain name for the website.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hosted_zone_id&lt;/td&gt;
&lt;td&gt;The Hosted Zone ID. This is automatically generated and can be referenced by zone records.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;profile&lt;/td&gt;
&lt;td&gt;Credentials profile to use for AWS s3 sync command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sync_directories&lt;/td&gt;
&lt;td&gt;Directories to sync with S3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Variables within sample code&lt;/p&gt;

&lt;p&gt;Following the normal terraform init, terraform plan and terraform apply workflow will deploy the infrastructure and your site will be up and running! If you update your website content, you can rerun Terraform apply and a change will be detected and the content re-synced.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Next?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Automated deployment through GitHub Actions
&lt;/h3&gt;

&lt;p&gt;We are currently developing an associated GitHubActions pipeline which will automate the Terraform deployment and the build of the source code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and Budgeting Alarms
&lt;/h3&gt;

&lt;p&gt;We are looking into creating a monitoring dashboard and budgeting alarms.&lt;/p&gt;

&lt;h3&gt;
  
  
  General quality of life updates
&lt;/h3&gt;

&lt;p&gt;We are making general QoL updates such as making the domain name optional initially in case this is not readily available at the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Me
&lt;/h2&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/faizanrazainterweave/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>websitehosting</category>
      <category>s3staticwebsite</category>
    </item>
    <item>
      <title>Terraform - Understanding the Value - Declarative Language (TERF)</title>
      <dc:creator>Faizan Raza</dc:creator>
      <pubDate>Sun, 07 Nov 2021 18:49:46 +0000</pubDate>
      <link>https://dev.to/faizanraza_interweave/terraform-understanding-the-value-declarative-language-terf-5m8</link>
      <guid>https://dev.to/faizanraza_interweave/terraform-understanding-the-value-declarative-language-terf-5m8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I was asked at work recently by someone completely new to DevOps and coding in general - "Why would you use Terraform? It is so much easier to do things in the portal."&lt;/p&gt;

&lt;p&gt;Today, we will be exploring my personal favourite feature of Terraform - the fact it is declarative. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yes - I have ranked my favourite and most hated features of Terraform :).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This will be very beginner orientated. This is also a laying down some of the foundational concepts for the upcoming TERF series (Terraform Enterprise Ready Framework).&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it actually mean to be declarative?
&lt;/h2&gt;

&lt;p&gt;Terraform state on their website:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Terraform language is declarative, describing an intended goal, rather than the steps to reach that goal.&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A common explanation on google searches:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A language which describes what needs to be done, instead of how to do it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are like me - when you first started using Terraform and following tutorials you probably thought, "I am telling Terraform to create these resources for me, but I am also telling it how to join these resources together, so, what exactly is declarative about this?"&lt;/p&gt;

&lt;p&gt;The way I understand it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Terraform allows you to create an image of exactly what you want your infrastructure to look like and then Terraform manages the underlying logic to transform the infrastructure into your desired state.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If no infrastructure currently exists, Terraform will create the new infrastructure in the correct sequence of steps to create what you coded. &lt;/p&gt;

&lt;p&gt;If infrastructure currently exists, but does not match your coded image, then Terraform will orchestrate the sequence of events required to change the infrastructure to match what you coded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's learn through an example - an AWS virtual machine
&lt;/h2&gt;

&lt;p&gt;We will be using the example provided under "Network and Credit Example" at the following &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance" 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%2Fzx9vprc9vgt0uiofzt3g.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%2Fzx9vprc9vgt0uiofzt3g.png" alt="Sample Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will be creating:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A VPC with CIDR address 192.168.0.0/24.&lt;/li&gt;
&lt;li&gt;A subnet with CIDR address 192.168.0.0/24.&lt;/li&gt;
&lt;li&gt;A network interface card associated with the above subnet.&lt;/li&gt;
&lt;li&gt;An EC2 instance with the above network interface card attached. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creation through the portal
&lt;/h2&gt;

&lt;p&gt;Creation through the portal requires the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the VPC through the VPC  section of AWS.&lt;/li&gt;
&lt;li&gt;Create  subnet under that VPC in the VPC section of AWS.&lt;/li&gt;
&lt;li&gt;Create a network interface in the subnet in the EC2 section of AWS.&lt;/li&gt;
&lt;li&gt;Create an EC2 instance attaching the above network interface and selecting above subnet and VPCs during the configuration process. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: The above call be done from the EC2 launch wizard where you can select "Create VPC" and "Create Subnet" under stage "3. Configure Instance".&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's say we want to change the VPC and subnet CIDR subnets to 192.168.1.0/24 - AWS documentation states: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;It's not possible to modify the IP address range of an existing virtual private cloud (VPC) or subnet. You must delete the VPC or subnet, and then create a new VPC or subnet with your preferred CIDR block.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you tried to go make this change within the portal  you see this:&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%2Frvdlelwolosyp98lz3op.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%2Frvdlelwolosyp98lz3op.png" alt="Error message when deleting VPC"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that you have to do the following in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Delete the EC2 instance&lt;/li&gt;
&lt;li&gt;Delete the network interface&lt;/li&gt;
&lt;li&gt;Delete the subnet&lt;/li&gt;
&lt;li&gt;Delete the virtual machine&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You would then have to recreate all the resources all over again. Now, just imagine this change with multiple EC2 instances and other virtual infrastructure created within that VPC. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creation through Terraform
&lt;/h2&gt;

&lt;p&gt;The sample Terraform could be used for this slightly more semantic code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_vpc" "DemoVPC" {
  cidr_block = "192.168.0.0/24"

  tags = {
    Name = "DemoVPC"
  }
}

resource "aws_subnet" "DemoSubnet" {
  vpc_id            = aws_vpc.DemoVPC.id
  cidr_block        = "192.168.0.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name = "DemoSubnet"
  }
}

resource "aws_network_interface" "DemoNIC" {
  subnet_id   = aws_subnet.DemoSubnet.id

  tags = {
    Name = "DemoNIC"
  }
}

resource "aws_instance" "DemoVM" {
  ami           = "ami-005e54dee72cc1d00" # us-west-2
  instance_type = "t2.micro"

  network_interface {
    network_interface_id = aws_network_interface.DemoNIC.id
    device_index         = 0
  }

  credit_specification {
    cpu_credits = "unlimited"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform would create the following plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # aws_instance.DemoVM will be created
  # aws_network_interface.DemoNIC will be created
  # aws_subnet.DemoSubnet will be created
  # aws_vpc.DemoVPC will be created
Plan: 4 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform would then create these resources&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws_vpc.DemoVPC: Creation complete
aws_subnet.DemoSubnet: Creation complete
aws_network_interface.DemoNIC: Creation complete
aws_instance.DemoVM: Creation complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how Terraform created the infrastructure in the correct order? Well it is not too evident here but it will become more evident in the following example.&lt;/p&gt;

&lt;p&gt;To make change the VPC and Subnet CIDR we would change the code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_vpc" "DemoVPC" {
  cidr_block = "192.168.1.0/24"

  tags = {
    Name = "DemoVPC"
  }
}

resource "aws_subnet" "DemoSubnet" {
  vpc_id            = aws_vpc.DemoVPC.id
  cidr_block        = "192.168.1.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name = "DemoSubnet"
  }
}

resource "aws_network_interface" "DemoNIC" {
  subnet_id   = aws_subnet.DemoSubnet.id

  tags = {
    Name = "DemoNIC"
  }
}

resource "aws_instance" "DemoVM" {
  ami           = "ami-005e54dee72cc1d00" # us-west-2
  instance_type = "t2.micro"

  network_interface {
    network_interface_id = aws_network_interface.DemoNIC.id
    device_index         = 0
  }

  credit_specification {
    cpu_credits = "unlimited"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform would make the following plan:&lt;/p&gt;

&lt;p&gt;Terraform has picked up that to make this change, all the resources must be deleted and recreated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Terraform will perform the following actions:

  # aws_instance.DemoVM must be replaced
  # aws_network_interface.DemoNIC must be replaced
  # aws_subnet.DemoSubnet must be replaced
  # aws_vpc.DemoVPC must be replaced

Plan: 4 to add, 0 to change, 4 to destroy.

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

&lt;/div&gt;



&lt;p&gt;However Terraform also knows the sequence in which these events needs to occur. For example it can not delete the VPC until everything else is deleted. You can see this in the simplified output from the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws_instance.DemoVM: Destruction complete 
aws_network_interface.DemoNIC: Destruction complete
aws_subnet.DemoSubnet: Destruction complete
aws_vpc.DemoVPC: Destruction complete
aws_vpc.DemoVPC: Creation complete
aws_subnet.DemoSubnet: Creation complete
aws_network_interface.DemoNIC: Creation complete
aws_instance.DemoVM: Creation complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, it is evident that Terraform orchestrated the destruction and creation of resources into the correct order. &lt;/p&gt;

&lt;p&gt;We did not have to tell Terraform to destroy the old infrastructure, to recreate new infrastructure or explicitly state the order of events. This is the declarative nature of Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding Remarks
&lt;/h2&gt;

&lt;p&gt;In complex and layered deployments - this declarative nature is a game changer and lets you focus on delivering business value and functionality. Terraform abstracts away underlying logic and sequencing of events. &lt;/p&gt;

&lt;p&gt;I work on a Terraform code base over 2000 lines long and managing over 250 resources in AWS. Whenever I have made changes to a resource which are sequenced in the beginning or middle of the deployment, Terraform has been comfortably able to orchestrate the change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support Me
&lt;/h3&gt;

&lt;p&gt;Follow me here on Dev.to&lt;br&gt;
Follow me on &lt;a href="https://www.linkedin.com/in/faizan-raza-997808206" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;br&gt;
Comment below feedback to help me improve my series!&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>beginners</category>
      <category>infrastructureascode</category>
    </item>
  </channel>
</rss>
