<?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: Zhen Kai</title>
    <description>The latest articles on Forem by Zhen Kai (@zhenkai).</description>
    <link>https://forem.com/zhenkai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F498319%2Fb4ab6f18-ed4d-44c3-a029-611dba3d0222.jpeg</url>
      <title>Forem: Zhen Kai</title>
      <link>https://forem.com/zhenkai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/zhenkai"/>
    <language>en</language>
    <item>
      <title>New AWS Management Console Home is a Step in the Right Direction</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Tue, 01 Feb 2022 02:41:59 +0000</pubDate>
      <link>https://forem.com/aws-builders/new-aws-management-console-home-is-a-step-in-the-right-direction-5alm</link>
      <guid>https://forem.com/aws-builders/new-aws-management-console-home-is-a-step-in-the-right-direction-5alm</guid>
      <description>&lt;p&gt;On 12th January 2022, AWS announced a &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/01/console-home-aws-management-console/" rel="noopener noreferrer"&gt;new AWS Management Console home page&lt;/a&gt;. I think it's a step in the right direction. Here's why. &lt;/p&gt;

&lt;h2&gt;
  
  
  Old console home page
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbbwjg8g3kw7wstgqb6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbbwjg8g3kw7wstgqb6z.png" alt="Old console home page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Lack of Customization
&lt;/h4&gt;

&lt;p&gt;In the old console home page, you had a lack of customization. You cannot move, resize, remove any elements on the console page. The only thing you could customize is a bookmark of your favourite AWS services. &lt;/p&gt;

&lt;h4&gt;
  
  
  No Consolidated Dashboard
&lt;/h4&gt;

&lt;p&gt;Although there are dashboard features found in a couple of AWS services, there isn't a customizable multi-service dashboard. For example, in the EC2 and RDS console page, you have the EC2 and RDS dashboard respectively.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bloat (for experienced users)
&lt;/h4&gt;

&lt;p&gt;The old console home page contains a lot of content meant for new users. For experienced users, majority of these content are bloat, diluting the overall console home experience. An example of bloat:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzs9lyggejotklml2xmb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzs9lyggejotklml2xmb.png" alt="aws console mobile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other bloat content includes boxes for "Getting Started" and "Explore AWS", which is hardly ever used for an experienced user. This issue can be solved if customization allowed these bloat content to be removed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's on the new Console Home?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p3tamv3wk0b8n0rsusb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p3tamv3wk0b8n0rsusb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At launch, users can utilize 8 widgets. I will list all of them down below, along with a simple description, my personal recommendation and a rating as an experienced user.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Welcome to AWS&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Includes links to learn fundamentals of AWS, training, certification and AWS news. Widget only available in 1 size.&lt;/li&gt;
&lt;li&gt;Basically useless for experienced users. Will benefit new users accessing the AWS console for the first time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2/10&lt;/strong&gt;. Remove it!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Recently visited&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;List of AWS services you've recently visited. Widget available in Regular or Extended views. Has pagination in the regular view.&lt;/li&gt;
&lt;li&gt;A frequently used part of the old console home page. Hard to find faults with this. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8/10&lt;/strong&gt;. Pretty useful.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;AWS Health&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Contains events that might affect your AWS infrastructure and account. Widget only available in 1 size.&lt;/li&gt;
&lt;li&gt;Not really useful for solo developers or beginners. Very useful for enterprise customers with huge infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5/10&lt;/strong&gt;. Must have for enterprise users, mostly useless for everyone else.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Cost and usage&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Visualizes your AWS costs and usage. Contains information on your current, previous and projected month’s costs for the Regular view. Includes a cost breakdown for a few of your top spending AWS services in Extended views.&lt;/li&gt;
&lt;li&gt;Extremely useful for all types of users. New users can spot unterminated cloud resources, enterprise users can get a quick sensing of cloud spending trend. Recommend to set this at Extended View.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10/10&lt;/strong&gt;! The runaway winner of this new console home page change.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Build a solution&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Workflows and wizards that introduce you to AWS services. Widget available in Regular or Extended views.&lt;/li&gt;
&lt;li&gt;Mostly unused and a big part of the previous bloat problem. Usually not the first place experienced users look at when thinking of solving a problem. Might benefit new users more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3/10&lt;/strong&gt;. Probably should remove.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Trusted Advisor&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Mainly for accounts with AWS Business Support or AWS Enterprise Support. Can see an overview of automated checks from Trusted Advisor. Widget available in Regular or Extended views.&lt;/li&gt;
&lt;li&gt;Practically useless for accounts on Basic (free) and Developer support plan. Benefits enterprise customers with sizeable cloud infrastructure spending.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5/10&lt;/strong&gt;. Must have for enterprise users, mostly useless for everyone else. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Explore AWS&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Contains AWS training and certification links. Widget only available in 1 size.&lt;/li&gt;
&lt;li&gt;Useless for experienced and enterprise users. Hard to see beginners use this as well. Seems to overlap with Welcome to AWS widget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1/10&lt;/strong&gt;. Remove it immediately!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Favorites&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Shows the AWS services that users bookmarked. Widget available in Regular or Extended views.&lt;/li&gt;
&lt;li&gt;Pretty useful all around, more so for enterprise and experienced users. New users will probably utilize the Recently visited widget more often.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;7/10&lt;/strong&gt;. Less often used than Recently visited, but still a mainstay for most users.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I want to see in the future?
&lt;/h2&gt;

&lt;p&gt;The more widgets there are, the more customizations there will be. The new console page does have a nifty little link for users to submit widget suggestions (see screenshot below). I would also be interested to have more options to resize the widgets. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmcppl8orh0fcw8e5caa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmcppl8orh0fcw8e5caa.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope that individual service dashboards, for example, the EC2 dashboard, can be made into a widget. It could also be extra helpful for Ops folks if Cloudwatch dashboards can be exported as widgets on the home page.&lt;/p&gt;

&lt;p&gt;Lastly, I want the ability to share home page configurations across AWS Organizations or different accounts. I feel like that would be helpful for users managing more than 10 AWS accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Historically, AWS console home page have been lagging behind Azure and Google Cloud. This change is a step in the right direction and definitely has an immediate positive impact for enterprise and experienced users. So, enable the new console home page experience right now!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4c2qb75z3avdiikz8gsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4c2qb75z3avdiikz8gsz.png" alt="switch to new console home page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know what you think. Don't forget to follow me on &lt;a href="https://www.linkedin.com/in/zhen-kai/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AWS VPC IPAM Tutorial - The Better Terraform Way (7 Steps Faster)</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Mon, 10 Jan 2022 14:06:01 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-vpc-ipam-tutorial-the-better-terraform-way-7-steps-faster-4d6b</link>
      <guid>https://forem.com/aws-builders/aws-vpc-ipam-tutorial-the-better-terraform-way-7-steps-faster-4d6b</guid>
      <description>&lt;p&gt;In my previous post covering the basics of &lt;a href="https://dev.to/aws-builders/aws-vpc-ipam-basics-and-why-you-need-to-be-careful-5abn"&gt;AWS VPC IPAM&lt;/a&gt;, I promised some Terraform code samples. &lt;/p&gt;

&lt;p&gt;My Terraform code sample will be based on the &lt;a href="https://docs.aws.amazon.com/vpc/latest/ipam/tutorials-create-vpc-ipam.html"&gt;AWS VPC IPAM tutorial from the official documentation&lt;/a&gt;. As you can already see from the title, the "Terraform way" is 7 steps shorter than the official tutorial and undoubtedly better.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Better Terraform Way
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1: Clone Github repository
&lt;/h4&gt;

&lt;p&gt;From your terminal, run &lt;code&gt;git clone https://github.com/sgLancelot/aws-vpc-ipam-terraform-tutorial.git&lt;/code&gt;. Change your current directory to the cloned code with &lt;code&gt;cd aws-vpc-ipam-terraform-tutorial&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Terraform Apply
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Note: This step assumes you already have your AWS credentials set up and Terraform installed. As of this point of writing, you will require Terraform version 1.1.2 or higher.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;From your terminal, run &lt;code&gt;terraform apply&lt;/code&gt;. Review the Terraform planned changes before you type &lt;code&gt;yes&lt;/code&gt;. If you are using the default values, you should expect a plan consisting of &lt;code&gt;9 to add, 0 to change, 0 to destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After applying the changes, head over to your AWS console to view what you've created. &lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Terraform Destroy
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Note: This step may take up to 25 minutes to complete. From testing, this seems to be caused by the pool CIDR assignment requiring some time to detect that the test VPC is deleted before allowing you to unassign the CIDR.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;From your terminal, run &lt;code&gt;terraform destroy&lt;/code&gt;. Review the Terraform planned changes before you type &lt;code&gt;yes&lt;/code&gt;. If you are using the default values, you should expect a plan consisting of &lt;code&gt;0 to add, 0 to change, 9 to destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That concludes the AWS VPC IPAM tutorial, the better Terraform way. Next, we will walkthrough the code to give you an understanding of what you just created and destroyed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Code Walkthrough
&lt;/h2&gt;

&lt;p&gt;In this section, I'll walkthrough my code and describe my thought processes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Variables
&lt;/h4&gt;

&lt;p&gt;In the &lt;code&gt;variables.tf&lt;/code&gt; file, you can see where the variables are defined. The default values follows the tutorial, but I've put them as variables to give you &lt;a href="https://www.terraform.io/language/values/variables#variable-definitions-tfvars-files"&gt;the option to define them yourselves in your &lt;code&gt;.tfvars&lt;/code&gt; file, if you choose to do so&lt;/a&gt;. Otherwise, the default values will work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;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;"The AWS Region that the resources will be created in. Will also be included as part of the IPAM operating region"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ipam_operating_regions"&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;list&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Additional AWS VPC IPAM operating regions. You can only create VPCs from a pool whose locale matches this variable. Duplicate values will be removed."&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;"us-west-2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"top_level_pool_cidr"&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;"The top level IPAM pool CIDR. Currently only supports a single CIDR."&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/8"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variables are pretty self-explanatory from the description I've added. &lt;/p&gt;

&lt;p&gt;From here on, all the code can be found in &lt;code&gt;main.tf&lt;/code&gt;.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_service_linked_role"&lt;/span&gt; &lt;span class="s2"&gt;"ipam"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;aws_service_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ipam.amazonaws.com"&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;"Service Linked Role for AWS VPC IP Address Manager"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've chosen to follow the tutorial without using AWS Organizations, and hence, the service-linked IAM role needs to be created for VPC IPAM to automatically discover resources to monitor. There is nothing fancy here.&lt;/p&gt;

&lt;h4&gt;
  
  
  IPAM and it's operating regions
&lt;/h4&gt;

&lt;p&gt;The IPAM construct requires you to define its operating region. One of the operating region must include the AWS provider block region, in this case, it's defined as &lt;code&gt;var.region&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&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;deduplicated_region_list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toset&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ipam_operating_regions&lt;/span&gt;&lt;span class="err"&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 &lt;code&gt;deduplicated_region_list&lt;/code&gt; local variable ensures that the list of regions that you pass into IPAM does not have any duplication, which might cause an error when creating the IPAM. To learn more about the toset function in the &lt;a href="https://www.terraform.io/language/functions/toset"&gt;Terraform documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc_ipam"&lt;/span&gt; &lt;span class="s2"&gt;"tutorial"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-ipam"&lt;/span&gt;
  &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"operating_regions"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deduplicated_region_list&lt;/span&gt;
    &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;region_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;operating_regions&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;aws_iam_service_linked_role&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ipam&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;Here, &lt;code&gt;local.deduplicated_region_list&lt;/code&gt; is passed into the operating systems configuration block as a &lt;a href="https://www.terraform.io/language/expressions/dynamic-blocks"&gt;dynamic block&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another interesting point is the &lt;a href="https://www.terraform.io/language/meta-arguments/depends_on"&gt;depends_on meta-argument&lt;/a&gt; to create a dependency between the service-linked role and IPAM. &lt;a href="https://docs.aws.amazon.com/vpc/latest/ipam/iam-ipam-slr.html"&gt;This allows the IPAM to be deleted before the service-link role is deleted&lt;/a&gt;. From testing, letting Terraform perform this deletion without &lt;code&gt;depends_on&lt;/code&gt; actually causes an error as it deletes the service-linked role and IPAM in parallel.&lt;/p&gt;

&lt;h4&gt;
  
  
  Top-level Pool and CIDR assignment
&lt;/h4&gt;

&lt;p&gt;Along with the creation of the IPAM, a default private and public scope is created as well and can be reference via the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam#attributes-reference"&gt;IPAM Terraform resource's attributes&lt;/a&gt;. To understand more about what a scope is, do check out &lt;a href="https://dev.to/aws-builders/aws-vpc-ipam-basics-and-why-you-need-to-be-careful-5abn"&gt;my previous post&lt;/a&gt; covering the basics of AWS VPC IPAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc_ipam_pool"&lt;/span&gt; &lt;span class="s2"&gt;"top_level"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"top-level-pool"&lt;/span&gt;
  &lt;span class="nx"&gt;address_family&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ipv4"&lt;/span&gt;
  &lt;span class="nx"&gt;ipam_scope_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc_ipam&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tutorial&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_default_scope_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# provision CIDR to the top-level pool&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc_ipam_pool_cidr"&lt;/span&gt; &lt;span class="s2"&gt;"top_level"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ipam_pool_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc_ipam_pool&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top_level&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top_level_pool_cidr&lt;/span&gt; &lt;span class="c1"&gt;# "10.0.0.0/8" if following the tutorial&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The top-level pool will be created in the IPAM's private scope. The &lt;code&gt;var.top_level_pool_cidr&lt;/code&gt; variable follows the tutorial with &lt;code&gt;10.0.0.0/8&lt;/code&gt;. You may set a different CIDR as long as it as a netmask of above&lt;code&gt;/16&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sub-level Pool and CIDR assignment
&lt;/h4&gt;

&lt;p&gt;For this part, I further improved on the tutorial of simply creating just 1 regional sub-level pool. Using the &lt;a href="https://www.terraform.io/language/meta-arguments/for_each"&gt;for_each meta-argument&lt;/a&gt;, the resource taps on &lt;code&gt;local.deduplicated_region_list&lt;/code&gt; local variable again to create multiple regional pools according to the region list you've set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc_ipam_pool"&lt;/span&gt; &lt;span class="s2"&gt;"regional"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deduplicated_region_list&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;"${each.key}-pool"&lt;/span&gt;
  &lt;span class="nx"&gt;address_family&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ipv4"&lt;/span&gt;
  &lt;span class="nx"&gt;ipam_scope_id&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc_ipam&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tutorial&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_default_scope_id&lt;/span&gt;
  &lt;span class="nx"&gt;locale&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;
  &lt;span class="nx"&gt;source_ipam_pool_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc_ipam_pool&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top_level&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc_ipam_pool_cidr"&lt;/span&gt; &lt;span class="s2"&gt;"regional"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&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;index&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deduplicated_region_list&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="nx"&gt;ipam_pool_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc_ipam_pool&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;regional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top_level_pool_cidr&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the CIDR assignment for these regional pools, I had to convert the toset-transformed &lt;code&gt;local.deduplicated_region_list&lt;/code&gt; to a list again. The purpose was to allow the &lt;code&gt;for&lt;/code&gt; expression to properly retrieve the index of each region.&lt;/p&gt;

&lt;p&gt;The code snippet below should help you visualize how the &lt;code&gt;for&lt;/code&gt; expression looks like for the default values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;{&lt;/span&gt; 
  &lt;span class="nv"&gt;us-east-1 = 0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;us-west-2 = 1&lt;/span&gt;
&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, &lt;code&gt;each.key&lt;/code&gt; would iterate through the key (the regions) and &lt;code&gt;each.value&lt;/code&gt; would be the corresponding index in the list.&lt;/p&gt;

&lt;p&gt;The reason why we needed the index value is to generate the sub-level pool CIDR dynamically from the top-level pool CIDR &lt;code&gt;var.top_level_pool_cidr&lt;/code&gt; using the &lt;a href="https://www.terraform.io/language/functions/cidrsubnet"&gt;&lt;code&gt;cidrsubnet&lt;/code&gt; Terraform function&lt;/a&gt;. In short, the &lt;code&gt;cidrsubnet&lt;/code&gt; function slices up a CIDR according to the values you pass into it.&lt;/p&gt;

&lt;h4&gt;
  
  
  VPC to test
&lt;/h4&gt;

&lt;p&gt;Finally, we've reached the end of the code sample. We will create a test VPC in the AWS provider region defined in &lt;code&gt;var.region&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the tutorial, they've manually assigned a direct CIDR block to this VPC, which seems ludicrous because it doesn't showcase the strength of an IPAM-managed VPC.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"tutorial"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ipv4_ipam_pool_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc_ipam_pool&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;regional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;ipv4_netmask_length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; 
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;aws_vpc_ipam_pool_cidr&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;regional&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my Terraform code, we've chosen to test the new attributes added in AWS provider version &lt;code&gt;3.68.0&lt;/code&gt;, which for as long as I can remember, removes the &lt;code&gt;cidr_block&lt;/code&gt; attribute requirement of being mandatory; &lt;code&gt;cidr_block&lt;/code&gt; is now optional if you have the &lt;code&gt;ipv4_ipam_pool_id&lt;/code&gt; and &lt;code&gt;ipv4_netmask_length&lt;/code&gt; attributes. For my example, I've requested a CIDR of /24 netmask length from the regional sub-level pool.&lt;/p&gt;

&lt;p&gt;It should achieve the same results as the tutorial as long as you are using the default values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;I had a blast writing this short Terraform code sample for AWS VPC IPAM. If there is anything you feel I could've done better, please reach out to me.&lt;/p&gt;

&lt;p&gt;Hope you have learnt something!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AWS VPC IPAM Basics and Why You Need to be Careful</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Mon, 03 Jan 2022 12:40:23 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-vpc-ipam-basics-and-why-you-need-to-be-careful-5abn</link>
      <guid>https://forem.com/aws-builders/aws-vpc-ipam-basics-and-why-you-need-to-be-careful-5abn</guid>
      <description>&lt;p&gt;During &lt;a href="https://reinvent.awsevents.com/" rel="noopener noreferrer"&gt;AWS re:Invent 2021&lt;/a&gt;, David Brown, Amazon EC2 VP, &lt;a href="https://www.youtube.com/watch?v=ii5XWpcYYnI&amp;amp;t=2974s" rel="noopener noreferrer"&gt;announced Amazon VPC IP Address Manager (IPAM)&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why VPC IPAM?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/vpc/latest/peering/invalid-peering-configurations.html" rel="noopener noreferrer"&gt;VPCs with overlapping CIDRs cannot be peered&lt;/a&gt;, and is usually resolved only by destroying and recreating the VPC with the correct CIDR. If there are many existing workloads in the VPC, this process will incur painful migration effort and risks to live production systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IP address allocation to Amazon VPCs is something that organizations want to get correct at the beginning and never have to think about in the future&lt;/strong&gt;. Amazon VPC IP Address Manager (VPC IPAM) helps by handling the IP address allocation, ensuring an organization will never issue conflicting CIDRs to its VPCs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ewjysz80kqqx8hnvwru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ewjysz80kqqx8hnvwru.png" alt="vpc ipam logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VPC IPAM can also save organizations from future embarrassments when new AWS resources are blocked from scaling due to a lack of free IP addresses. VPC IPAM has the ability to monitor IP address utilization, generating alerts to administrators when certain thresholds are crossed.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS VPC IPAM basics
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gndtiw4zsv9d7nqstxe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gndtiw4zsv9d7nqstxe.png" alt="aws vpc ipam overview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  [1] Service-Linked Role and IPAM Administration
&lt;/h4&gt;

&lt;p&gt;The service-linked role &lt;code&gt;AWSServiceRoleForIPAM&lt;/code&gt; is required for IPAM to discover, monitor and manage IPAM resources (resources are covered at the end of this list).&lt;/p&gt;

&lt;p&gt;For IPAM administration, it slightly differs depending on situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using AWS Organizations (&lt;a href="https://docs.aws.amazon.com/vpc/latest/ipam/enable-integ-ipam.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;)

&lt;ul&gt;
&lt;li&gt;Delegate an Organizations member account to be the IPAM account. &lt;/li&gt;
&lt;li&gt;The IPAM account will then be responsible for making IPAM-level configurations.&lt;/li&gt;
&lt;li&gt;The IPAM service-linked role will be automatically created in each member account in your organization.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;For single AWS account (no AWS Organizations), the IPAM service-linked role needs to be created manually.&lt;/p&gt;

&lt;h4&gt;
  
  
  [2] IPAM
&lt;/h4&gt;

&lt;p&gt;Before everything else, you'd need to create an IPAM first (&lt;a href="https://docs.aws.amazon.com/vpc/latest/ipam/create-ipam.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;). At this point of time, you have to decide IPAM's operating regions. This ringfences IPAM to the regions where you would have resources for IPAM to manage. This can be modified later on.&lt;/p&gt;

&lt;h4&gt;
  
  
  [3] Scope (Private &amp;amp; Public)
&lt;/h4&gt;

&lt;p&gt;After creating an IPAM, 2 scopes are automatically created, 1 public and 1 private scope. To explain what scopes are, I've compiled some findings from the &lt;a href="https://docs.aws.amazon.com/vpc/latest/ipam/how-it-works-ipam.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, re:Invent and my own testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can only create private scopes.&lt;/li&gt;
&lt;li&gt;Public scopes seem to be for your BYOIP (&lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/introducing-bring-your-own-ip-byoip-for-amazon-vpc/" rel="noopener noreferrer"&gt;bring your own IP&lt;/a&gt;) resources. These IP addresses don't belong to AWS and are not managed by IPAM.&lt;/li&gt;
&lt;li&gt;All IPAM resources (non-BYOIP) will be in the automatically-created private scope by default. You can shift them to a different scope subsequently.&lt;/li&gt;
&lt;li&gt;Each private scope contains it's own set of managed IP CIDRs, IPAM Pools and resources. Inheriting overlapping AWS resources with overlapping IP CIDRS is a consideration for having additional private scopes. &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  [4] Pool (Top-Level)
&lt;/h4&gt;

&lt;p&gt;From the &lt;a href="https://docs.aws.amazon.com/vpc/latest/ipam/planning-ipam.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, a pool is a collection of contiguous IP address ranges (or CIDRs). When creating a top-level pool, it should contain all the specific CIDRs you'd like to allocate to resources within a scope. &lt;/p&gt;

&lt;p&gt;AWS doesn't let you define regions here. So the CIDRs here can be consumed in any region.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: For each top-level pool, you can set allocation rules to enforce IPAM resource tagging and network mask allocation.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  [5] Pool (Sub-Level)
&lt;/h4&gt;

&lt;p&gt;A sub-level pool can be restricted to a specific region and is allocated a CIDR from a higher level pool. A recommended pool structure from AWS looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskfnhya0felxa7cx90ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskfnhya0felxa7cx90ui.png" alt="ipam pool structure visualization"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can have many levels of pools. Design the pool structure according to your needs. Sub-level pools do not require specific CIDRs to be defined; you could define a network mask and let IPAM handle the CIDR allocation automatically.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: For each sub-level pool, you can set allocation rules to enforce IPAM resource tagging, network mask allocation and region.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  [6] Pool sharing and IP allocation
&lt;/h4&gt;

&lt;p&gt;If you are using AWS Organizations, IPAM pools can be shared to member accounts via AWS Resource Access Manager (RAM). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5t545ycpt2j589or7ps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5t545ycpt2j589or7ps.png" alt="sample cidr allocation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once shared, IPAM resources being created can utilize the pool to receive allocation of CIDRs from the pool. Additionally, a user from the member account can also be allocated an IP address through an API call to the shared pool.&lt;/p&gt;

&lt;h4&gt;
  
  
  [7] IPAM managed and monitored Resources
&lt;/h4&gt;

&lt;p&gt;From the &lt;a href="https://docs.aws.amazon.com/vpc/latest/ipam/monitor-cidr-compliance-ipam.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In IPAM, a resource is an AWS service entity that is assigned an IP address or CIDR block. IPAM manages some resources, but only monitors other resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A managed resource has CIDR allocated from an IPAM pool, and is typically a VPC. A monitored resource extends beyond that, covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPCs&lt;/li&gt;
&lt;li&gt;Subnets&lt;/li&gt;
&lt;li&gt;Elastic IPs&lt;/li&gt;
&lt;li&gt;Subnet reserves&lt;/li&gt;
&lt;li&gt;Public IPv4 pools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: The number of "active" IP addresses determines IPAM's costs. See the next section for more information on this.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That wraps up the Amazon VPC IP Address Manager basics and its various components.&lt;/p&gt;

&lt;h2&gt;
  
  
  You should be careful of...
&lt;/h2&gt;

&lt;p&gt;In this section, I'll share some of my findings based on my experience testing the service out without AWS Organizations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is possible to create your IPAM, scopes, pools and allocate CIDRs to VPCs before creating your service-linked role. You wouldn't be able to monitor what you have allocated.&lt;/li&gt;
&lt;li&gt;Once a pool locale/region is configured, it cannot be changed. The only way is to recreate it with the correct locale/region.&lt;/li&gt;
&lt;li&gt;You cannot delete a higher level pool without deleting it's sub-levels first.&lt;/li&gt;
&lt;li&gt;You must deprovision all CIDRs in a pool before you can delete your pool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Costs
&lt;/h3&gt;

&lt;p&gt;From the &lt;a href="https://aws.amazon.com/vpc/pricing/" rel="noopener noreferrer"&gt;VPC pricing page&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You pay an hourly rate for each active IP address that you manage using IP Address Manager (IPAM). An active IP address is defined as an IP address assigned to a resource such as an EC2 instance or an Elastic Network Interface (ENI).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;It is important to note that not only IPAM-managed resources with active IP addresses are billed, IPAM-monitored resources, with IP addresses not allocated by IPAM are billed as well&lt;/strong&gt;. In Singapore (ap-southeast-1), each active IP address costs $0.00027 USD per hour.&lt;/p&gt;

&lt;p&gt;This is a problem for users who run a self-managed Kubernetes cluster in AWS or use Amazon Elastic Kubernetes Service (EKS) with self-managed nodes. These Kubernetes nodes have many IP addresses per Elastic Network Interface (ENI) and with each IP address being classified as an "active IP address" to IPAM, it's easy to see how your bill could skyrocket.&lt;/p&gt;

&lt;p&gt;Fortunately, I received a great tip to mitigate this from the AWS Community Builders group. By using &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/07/amazon-virtual-private-cloud-vpc-customers-can-assign-ip-prefixes-ec2-instances/" rel="noopener noreferrer"&gt;prefix delegation&lt;/a&gt;, IPAM counts the whole delegation (16 IP addresses) as a single IP address. You can find additional considerations and limitations for using prefixes in the &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-prefix-eni.html#prefix-limit" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Alternatively, you can consider using a separate CIDR as an overlay and potentially reserving those CIDRs manually on VPC IPAM. Since these CIDRs are not assigned on an ENI, it does not count as an active IP address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts &amp;amp; Whether You Should use VPC IPAM
&lt;/h2&gt;

&lt;p&gt;Amazon VPC IPAM is a compelling and useful service for a moderately-sized AWS Organizations greenfield or brownfield set up. If you are running a Kubernetes cluster with self-managed nodes, VPC IPAM's costs could be exponentially higher. Consider some of the above suggestions to reduce costs before using the service.&lt;/p&gt;

&lt;p&gt;Stay tuned for a future blogpost for some sample Terraform code to get you started on VPC IPAM.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>vpc</category>
      <category>ipam</category>
    </item>
    <item>
      <title>Simple Serverless Telegram Bot with AWS Lambda</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Mon, 04 Oct 2021 13:29:27 +0000</pubDate>
      <link>https://forem.com/aws-builders/simple-serverless-telegram-bot-with-aws-lambda-4p7c</link>
      <guid>https://forem.com/aws-builders/simple-serverless-telegram-bot-with-aws-lambda-4p7c</guid>
      <description>&lt;p&gt;Previously, I created a &lt;a href="https://blog.zhenkai.xyz/serverless-sms-reminder-part-1/" rel="noopener noreferrer"&gt;simple reminder application&lt;/a&gt; with AWS Lambda and Simple Notification Service (SNS) SMS messages. With so many instant messenger applications these days, it is about time I move away from using SMS messages, specifically to Telegram.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;There are no changes to the previous requirements. To recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To send a text message to people once a month&lt;/li&gt;
&lt;li&gt;Cheap&lt;/li&gt;
&lt;li&gt;Serverless on AWS&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lv2w7yh9gzkpe68yl6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lv2w7yh9gzkpe68yl6n.png" alt="Serverless Telegram Bot"&gt;&lt;/a&gt;&lt;br&gt;
Similar to the previous application, I will still be using CloudWatch Events to trigger a Lambda function in a monthly interval to send the reminder message.&lt;/p&gt;

&lt;p&gt;Previously, I had to manually subscribe mobile numbers to the SNS topic. The improvement this time for the Telegram bot comes from user self-service on-boarding to subscribe to the reminder notification, which is actually just a simple &lt;code&gt;/start&lt;/code&gt; to the bot.&lt;/p&gt;
&lt;h2&gt;
  
  
  How the Bot works
&lt;/h2&gt;

&lt;p&gt;Every day, a Lambda function (named Register) will be triggered to call the Telegram Bot API to retrieve any outstanding &lt;code&gt;/start&lt;/code&gt; initiation. Upon detecting an outstanding message, the chat ID and username will be extracted and appended to the DynamoDB table.&lt;/p&gt;

&lt;p&gt;Every month, a Lambda function (named sendMessage) will be triggered to scan the above-mentioned DynamoDB table, and recursively send out a custom Telegram message via the bot.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Configure the Bot
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Create a Telegram Bot with Botfather
&lt;/h3&gt;

&lt;p&gt;Following the &lt;a href="https://core.telegram.org/bots" rel="noopener noreferrer"&gt;Telegram bot guide&lt;/a&gt;, you start by looking for &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt; in Telegram and following the instructions to create a new bot. Give your bot a meaningful name, description and profile picture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4aayk9j9fm0vzebm1gx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4aayk9j9fm0vzebm1gx.jpeg" alt="BotFather"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of the token that Botfather assigns to you; it will be required for your Lambda functions to call your bot API.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Create a DynamoDB Table
&lt;/h3&gt;

&lt;p&gt;Telegram Chat IDs will be unique for every user that initiated a chat with my Telegram Bot. This made chat IDs a great candidate as the primary partition key. I set the primary partition key as chat_id (Number).&lt;/p&gt;

&lt;p&gt;I also configured on-demand Read/Write Capacity modes, because I knew my DynamoDB Table is not going to be accessed frequently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cz7ohrichvixj5tlxee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cz7ohrichvixj5tlxee.png" alt="DynamoDB Table for Telegram Bot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember to take note of the DynamoDB Table name, you will need it for the next step.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Create Lambda functions (register and sendMessage)
&lt;/h3&gt;

&lt;p&gt;Go to the Lambda console page and create 2 Lambda functions, 1 for the registration workflow and 1 for sending messages. The codes for the Lambda functions are as follows (I used the Node.js runtime):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DynamoDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DocumentClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ap-southeast-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;telegram&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;telegram-bot-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDB_TABLE_NAME&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;telegram&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TG_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&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="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;telegram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetUpdateMessageProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMessageProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API is started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="na"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Adding a new item...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;docClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error cant read.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="na"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Registration to Mr Gentle Reminderer is unsuccessful. Please let Zhen Kai know of the error. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="s2"&gt;`&lt;/span&gt;
                &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;succeed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="na"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Registration to Mr Gentle Reminderer is successful! The gentle reminder will be sent on the first day of every month. Thanks!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lambda function for sending messages&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DynamoDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DocumentClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ap-southeast-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;telegram&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;telegram-bot-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DDB_TABLE_NAME&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;telegram&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TG_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&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="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ProjectionExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chat_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;docClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unable to scan the table. Error JSON:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scan succeeded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chatIdObj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chat ID is:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chatIdObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="na"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chatIdObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gentle reminder to transfer the monthly subscription fee to the subscriber. Thanks!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;A few things to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I used the &lt;a href="https://www.npmjs.com/package/telegram-bot-api" rel="noopener noreferrer"&gt;Telegram Bot API NPM package&lt;/a&gt; for calling the Bot APIs.&lt;/li&gt;
&lt;li&gt;I zipped the npm package together with my Lambda code before I uploaded it.&lt;/li&gt;
&lt;li&gt;I used the DDB_TABLE_NAME environment variable for my Lambda function to know which table to call. Assign your DynamoDB Table to this variable.&lt;/li&gt;
&lt;li&gt;I used the TG_API_KEY environment variable for my Lambda function to know which Bot API to call. Assign your Bot token to this variable.&lt;/li&gt;
&lt;li&gt;Remember to give your Lambda functions the right IAM permissions to call DynamoDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Configure CloudWatch Events
&lt;/h3&gt;

&lt;p&gt;At the CloudWatch console page, under Events, create a new Event with the cron expression you want to trigger your respective Lambda functions.&lt;/p&gt;

&lt;p&gt;I wanted to consolidate registration once a day and send messages once a month, so I used the following cron expressions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily register: cron(0 10 * * ? *)&lt;/li&gt;
&lt;li&gt;Monthly sendMessage: cron(0 11 1 * ? *)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Head over to the Lambda console page and choose “+ Add trigger”, and choose EventBridge (CloudWatch Events) and select the CloudWatch events you have created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4fov3zjqhlig6v9czoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4fov3zjqhlig6v9czoi.png" alt="Lambda function"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;There are still a lot of room for improvement for this simple Telegram bot I hacked together over the weekend. A quicker feedback during bot registration using webhooks is a future improvement I would want to make.&lt;/p&gt;

&lt;p&gt;I hope you had fun with this little project. Reach out to me if you have any feedback.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>How I passed Certified Kubernetes Administrator (CKA) Exam (quick tips)</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Mon, 04 Oct 2021 13:05:43 +0000</pubDate>
      <link>https://forem.com/zhenkai/how-i-passed-certified-kubernetes-administrator-cka-exam-with-tips-3g4</link>
      <guid>https://forem.com/zhenkai/how-i-passed-certified-kubernetes-administrator-cka-exam-with-tips-3g4</guid>
      <description>&lt;p&gt;Exam Score: 93. Passing score: 66.&lt;/p&gt;

&lt;h2&gt;
  
  
  4-6 weeks before Exam
&lt;/h2&gt;

&lt;p&gt;Since CKA is an "open book" exam, you should be preparing with the mindset of knowing where to find stuff in the Kubernetes documentation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mumshad Mannambeth CKA course
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;If you are stuck while doing the practice questions after each chapter, dive into the official Kubernetes documentation to find your answer.&lt;/li&gt;
&lt;li&gt;Don't rely on the course content to find your answers. Use it to understand concepts.&lt;/li&gt;
&lt;li&gt;Begin &lt;strong&gt;bookmarking documentation&lt;/strong&gt; pages you find helpful.&lt;/li&gt;
&lt;li&gt;Run through the jsonpath practices.&lt;/li&gt;
&lt;li&gt;After finishing the Killer.sh simulators, come back this course to run through as many practice questions as you can.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Killer.sh CKA simulator
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Comes with 2 free tries after you've registered for the CKA exam&lt;/li&gt;
&lt;li&gt;Only use official Kubernetes documentation to find your answers&lt;/li&gt;
&lt;li&gt;Take the opportunity to &lt;strong&gt;bookmark documentation&lt;/strong&gt; pages you find helpful&lt;/li&gt;
&lt;li&gt;Do not panic if you find yourself struggling, I did too. The simulator is very tough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practice&lt;/strong&gt; setting up your Vimrc configuration, Kubectl autocomplete and alias.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Administrative stuff
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Register for the CKA exam&lt;/li&gt;
&lt;li&gt;Purchase a magnifying glass if you don't have one, in case you have issues with your webcam not being able to focus on your identification proof clearly. I faced this issue personally.&lt;/li&gt;
&lt;li&gt;Ensure you have read through the &lt;a href="https://docs.linuxfoundation.org/tc-docs/certification/lf-candidate-handbook"&gt;Candidate Handbook&lt;/a&gt; and &lt;a href="https://docs.linuxfoundation.org/tc-docs/certification/tips-cka-and-ckad"&gt;Important Instructions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Buy a decent webcam if you do not have one.&lt;/li&gt;
&lt;li&gt;Get a second monitor (or a monitor extension for your laptop). This is extremely helpful and reduces time required to alternate between windows.&lt;/li&gt;
&lt;li&gt;Run the compatibility check tool and make sure everything is good.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Less than 1 Day Before Exam
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Clear out your desk, including speakers. You don't need speakers for the exam.&lt;/li&gt;
&lt;li&gt;Get a clear cup for water&lt;/li&gt;
&lt;li&gt;Run the compatibility check tool again and make sure everything is good.&lt;/li&gt;
&lt;li&gt;Go through the entire Kubernetes Cheat Sheet and get familiar with every line in it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  During the Exam
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always execute the switch context command displayed at every question before attempting the question, even if you think you are already on the correct context.&lt;/li&gt;
&lt;li&gt;Do not get stuck on a question. Mark/Flag it and move on to the next.&lt;/li&gt;
&lt;li&gt;Open a second browser window with a single tab on your second monitor, with all the bookmarks you need on the bookmark bar. Remember to let the proctor know that you have a second monitor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bonus: My List of Bookmarks
&lt;/h2&gt;

&lt;p&gt;These bookmarks have also been named according to what you see in the list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/kubectl/cheatsheet/"&gt;Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands"&gt;Kubectl Cmds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/debug-application-cluster/debug-service/"&gt;Debug Svcs (nslookup,exec,iptables,run)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/"&gt;Debug DNS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/certificates/"&gt;Certs openssl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolume"&gt;PV hostPath&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity"&gt;Affinity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/"&gt;Kubeadm upgrade&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/setup-tools/kubeadm/"&gt;Kubeadm cmds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/"&gt;Kubelet cmds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#snapshot-using-etcdctl-options"&gt;etcd backup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/"&gt;Taints &amp;amp; Tolerations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/extend-kubernetes/configure-multiple-schedulers/"&gt;Mutli-scheduler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/"&gt;Svc Acc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/"&gt;ConfigMap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/"&gt;CSR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/security-context/"&gt;Security Contexts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/"&gt;Liveness, Readiness Probes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  After the Exam
&lt;/h2&gt;

&lt;p&gt;Results usually come within 24 hours. For me, it was published almost exactly 24 hours later. &lt;/p&gt;

&lt;p&gt;Good luck and all the best!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>Default Tags for Terraform AWS Provider is finally here</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Sun, 16 May 2021 10:47:01 +0000</pubDate>
      <link>https://forem.com/aws-builders/default-tags-for-terraform-aws-provider-is-finally-here-1864</link>
      <guid>https://forem.com/aws-builders/default-tags-for-terraform-aws-provider-is-finally-here-1864</guid>
      <description>&lt;p&gt;Default tags for Terraform AWS provider had been in the works since I started using Terraform a couple of years ago. Now that it is finally here, does it live up to its expectation?&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Default Tags?
&lt;/h1&gt;

&lt;p&gt;Default Tags allows its user to tag all AWS resources that support tags with the exception of the &lt;code&gt;aws_autoscaling_group&lt;/code&gt; resource. Default Tags are defined in the AWS provider configuration, and AWS resources configured with that provider will inherit &lt;code&gt;default_tags&lt;/code&gt;. A sample usage could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-1"&lt;/span&gt;
  &lt;span class="nx"&gt;default_tags&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="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
      &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example project"&lt;/span&gt;
      &lt;span class="nx"&gt;Terraform&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about it &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging#propagating-tags-to-all-resources" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://www.hashicorp.com/blog/default-tags-in-the-terraform-aws-provider" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Default Tags in different scenarios
&lt;/h1&gt;

&lt;p&gt;From the &lt;a href="https://www.hashicorp.com/blog/default-tags-in-the-terraform-aws-provider" rel="noopener noreferrer"&gt;official blog post&lt;/a&gt;, it states that individual resource tags will take precedence over &lt;code&gt;default_tags&lt;/code&gt;. Let’s see it in action with a few scenarios and observe its &lt;code&gt;terraform plan&lt;/code&gt; output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Only resource tags, no &lt;code&gt;default_tags&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"tagging_test_vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&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="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tagging-test"&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;"dev"&lt;/span&gt;
    &lt;span class="nx"&gt;Terraform&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;terraform plan&lt;/code&gt; output:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx3v939miwv71uoq0nsx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx3v939miwv71uoq0nsx.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we can see the existence of a &lt;code&gt;tags_all&lt;/code&gt; attribute that has the same value as the original &lt;code&gt;tags&lt;/code&gt; attribute. That being said, I’ve noticed this &lt;code&gt;tags_all&lt;/code&gt; attribute for some time now in the newer versions of AWS provider. Let’s move on to the next scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Only &lt;code&gt;default_tags&lt;/code&gt;, no resource tags
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-1"&lt;/span&gt;
  &lt;span class="nx"&gt;default_tags&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="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"stg"&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;"tagging-test-3"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"tagging_test_vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.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;code&gt;terraform plan&lt;/code&gt; output:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzuyj6jfo9rym6zf2sj6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzuyj6jfo9rym6zf2sj6.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without resource tags, the &lt;code&gt;tags&lt;/code&gt; attribute disappears, leaving only the &lt;code&gt;tags_all&lt;/code&gt; attribute with the value inherited from &lt;code&gt;default_tags&lt;/code&gt; from the AWS provider configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Conflicting tags
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-1"&lt;/span&gt;
  &lt;span class="nx"&gt;default_tags&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="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"stg"&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;"tagging-test-3"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"tagging_test_vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&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="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tagging-test"&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;"dev"&lt;/span&gt;
    &lt;span class="nx"&gt;Terraform&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;terraform plan&lt;/code&gt; output:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flitqt2pgtxrppaynwg9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flitqt2pgtxrppaynwg9t.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to what the official blog post mentioned, we can observe that resource tags take precedence over default tags when there is a conflict of tag name/key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 4: Non-conflicting tags
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-1"&lt;/span&gt;
  &lt;span class="nx"&gt;default_tags&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="nx"&gt;environment&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"stg"&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;"tagging-test-3"&lt;/span&gt;
      &lt;span class="nx"&gt;provisionedby&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"tagging_test_vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&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="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tagging-test"&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;"dev"&lt;/span&gt;
    &lt;span class="nx"&gt;Terraform&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;terraform plan&lt;/code&gt; truncated output:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbyfpov53amwqekotrsxz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbyfpov53amwqekotrsxz.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since tag names/keys are case-sensitive, all the default_tag tags in this example will be effected for &lt;code&gt;aws_vpc&lt;/code&gt;. The &lt;code&gt;tags_all&lt;/code&gt; attribute shows a combined list from default and resource tags, while the &lt;code&gt;tags&lt;/code&gt; attribute only shows the resource tags.&lt;/p&gt;

&lt;h1&gt;
  
  
  How this could affect your Terraform State design
&lt;/h1&gt;

&lt;p&gt;In a medium-to-large-sized project, Terraform state separation is one of the early design considerations a team needs to settle on. If you don’t know what state is, check out &lt;a href="https://www.terraform.io/docs/language/state/purpose.html" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt; first.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foamsv0ng4w653cm51nzy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foamsv0ng4w653cm51nzy.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

From &lt;a href="https://learn.hashicorp.com/tutorials/terraform/state-import?in=terraform/state" rel="noopener noreferrer"&gt;HashiCorp&lt;/a&gt;




&lt;p&gt;Each Terraform state culminates in a state file, and each state file has its own Terraform and provider configurations. Since default tags are a configured on the AWS provider level, being able to enforce different default tags in different states can make any mandatory organizational tagging requirements a breeze.&lt;/p&gt;

&lt;p&gt;For example, if your organization has different tag values for each application, it might make sense to separate all applications into it’s own state and enforce their unique application tags at the provider level.&lt;/p&gt;

&lt;p&gt;Terraform state design is a topic I’ve always seen a lot of discussion about. I will write about it in a future post.&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing Thoughts
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbljr6gdt523q27n660l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbljr6gdt523q27n660l.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s good to know that Terraform and the AWS Provider is still well supported and constantly improved on. All in all, Terraform as an IaC tool is reaching a level of maturity that I’ve been looking forward to. With the current Terraform 0.15 touted as the “beginning of the pre-release period leading up to Terraform 1.0”, I think Terraform users can expect a lot of exciting and useful releases in the coming weeks and months.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>The Best Git branching strategy for Terraform is no branching</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Sat, 08 May 2021 03:07:01 +0000</pubDate>
      <link>https://forem.com/zhenkai/the-best-git-branching-strategy-for-terraform-is-no-branching-4knk</link>
      <guid>https://forem.com/zhenkai/the-best-git-branching-strategy-for-terraform-is-no-branching-4knk</guid>
      <description>&lt;p&gt;&lt;em&gt;disclaimer: Opinions are my own and not the views of my employer&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We all know that code should be checked into version control. Terraform code is no different.&lt;/p&gt;

&lt;p&gt;There are so many Git branching strategies available out in the wild, such as the popular &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/" rel="noopener noreferrer"&gt;Git flow&lt;/a&gt; and &lt;a href="https://guides.github.com/introduction/flow/" rel="noopener noreferrer"&gt;Github flow&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrnxv4345jdobuenxxkz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrnxv4345jdobuenxxkz.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

Git Flow from &lt;a href="https://nvie.com/posts/a-successful-git-branching-model" rel="noopener noreferrer"&gt;nvie.com&lt;/a&gt;



&lt;h1&gt;
  
  
  What is Terraform State?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Terraform requires some sort of database to map Terraform config to the real world. When you have a resource resource “aws_instance” “foo” in your configuration, Terraform uses this map to know that instance i-abcd1234 is represented by that resource.&lt;/p&gt;

Purpose of State from &lt;a href="https://www.terraform.io/docs/language/state/purpose.html" rel="noopener noreferrer"&gt;Terraform Documentation&lt;/a&gt;



&lt;/blockquote&gt;

&lt;p&gt;When collaborating on Terraform code, HashiCorp recommends using &lt;a href="https://www.terraform.io/docs/language/state/remote.html" rel="noopener noreferrer"&gt;Remote State&lt;/a&gt; to store State files remotely, outside of Version Control (Git). All team members need to have access to this remote state storage if they want to test or apply their code contributions.&lt;/p&gt;
&lt;h1&gt;
  
  
  Feature Branch Problem
&lt;/h1&gt;

&lt;p&gt;When feature branches are worked on for a short period of time before merging into the main branch, there are usually no huge integration issues.&lt;/p&gt;

&lt;p&gt;However, in the age of Continuous Integration/Continuous Delivery (CI/CD), multiple new features could be worked on in parallel and delivered whenever they are ready. This creates a problem for Terraform and Terraform State management.&lt;/p&gt;

&lt;p&gt;Imagine this situation: Alice and Bob are collaborating on a Terraform code repository with the same development remote backend using Amazon S3. Alice has a new requirement to introduce load balancing capabilities, so she checks out a new feature branch to work on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# existing resources
resource "aws_instance" "app_server" {
  ami = "ami-0518bb0e75d3619ca"
  ....
}

# new load balancer resource by Alice
resource "aws_lb" "alice_test_lb" {
  subnet_mapping {
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typically with feature branches, Alice would apply her feature branch changes and provision the load balancer. Terraform state in the remote backend would be updated with Alice’s load balancer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxdcry7tzhjr87txcnmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxdcry7tzhjr87txcnmp.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
At the same time, Bob also checks out another new feature branch to modify the existing EC2 instance to test a new AMI as part of immutable infrastructure operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# existing resources
resource "aws_instance" "app_server" {
  ami = "ami-0fab0953c3bb514a9" # new AMI
  ....
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Bob is ready to test and apply his change, Terraform assesses that Alice’s load balancer had been removed in Bob’s code, generating a planned action to destroy Alice’s load balancer and recreate the existing EC2 instance with the new AMI ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9nea42axeolxdl9ip93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9nea42axeolxdl9ip93.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
This is highly undesirable and an absolute nightmare for integration testing and Terraform plan/apply.&lt;/p&gt;
&lt;h1&gt;
  
  
  1 State File, 1 Branch
&lt;/h1&gt;

&lt;p&gt;Live Infra Terraform code should represent the live infrastructure provisioned and Terraform state files record the details of those live infrastructure. Every single Terraform state file should have exactly 1 source of truth and should only reference to exactly 1 Git branch.&lt;/p&gt;
&lt;h1&gt;
  
  
  Avoiding Integration Hell
&lt;/h1&gt;

&lt;p&gt;I first got to realize the problems of feature branches when stumbling across &lt;a href="https://www.nomachetejuggling.com/2017/04/09/a-different-branching-strategy/" rel="noopener noreferrer"&gt;Rod Hilton’s blog post&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Two engineers are happily working away making commit after commit to their own respective feature branches, but neither of their branches are seeing the other’s code. Even if they’re regularly pulling off mainline, they’re still only seeing the commits that make it into the main branch, not each others. Developer A merges their code into mainline, then Developer B pulls and merges theirs, but now they have to deal with tons of merge conflicts. Developer B might not be in the best position to understand and resolve those conflicts if they don’t fully understand what Developer A is doing, and depending on how long these branches have been alive, they might have tons of code to resolve.&lt;/p&gt;

&lt;a href="https://www.nomachetejuggling.com/2017/04/09/a-different-branching-strategy/" rel="noopener noreferrer"&gt;A Branching Strategy Simpler than GitFlow: Three-Flow&lt;/a&gt; by Rod Hilton



&lt;/blockquote&gt;

&lt;p&gt;Feature branches provides little value when it comes to integration testing and even more so when Terraform doesn’t work when there are multiple branches to modifying a single state file.&lt;/p&gt;
&lt;h1&gt;
  
  
  Commit Directly to Development Branch
&lt;/h1&gt;

&lt;p&gt;The simplest way to prevent Terraform code from being Terraform applied on feature branches is to do away with feature branches completely.&lt;/p&gt;

&lt;p&gt;In the age of Continuous Integration, pushing and pulling changes to and from the main development branch frequently avoids the inevitable integration hell of merging multiple weeks-old feature branches.&lt;/p&gt;

&lt;p&gt;Instead of feature branches, make use of feature toggles to “hide” your features in plain sight until they are ready for testing. Terraform has good support of feature toggle implementation through the usage of &lt;a href="https://www.terraform.io/docs/language/meta-arguments/count.html" rel="noopener noreferrer"&gt;count&lt;/a&gt;, &lt;a href="https://www.terraform.io/docs/language/meta-arguments/for_each.html" rel="noopener noreferrer"&gt;for_each&lt;/a&gt; and &lt;a href="https://www.terraform.io/docs/language/expressions/conditionals.html" rel="noopener noreferrer"&gt;conditional expressions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using the previous example, Alice and Bob could’ve pushed their code directly into the development branch with feature toggles and it would like something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# feature toggles
locals {
  lb_feature  = false # change to true when ready
  ami_feature = false # change to true when ready
}

# existing resources
resource "aws_instance" "app_server" {
  ami = local.ami_feature ? "ami-0fab0953c3bb514a9" : "ami-0518bb0e75d3619ca"
  ....
}

# new load balancer resource by Alice
resource "aws_lb" "alice_test_lb" {
  count = local.lb_feature ? 1 : 0
  subnet_mapping {
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some good examples of Git branching strategies that do away with feature branches are the &lt;a href="https://www.nomachetejuggling.com/2017/04/09/a-different-branching-strategy/" rel="noopener noreferrer"&gt;Three-Flow model&lt;/a&gt;, &lt;a href="https://barro.github.io/2016/02/a-succesful-git-branching-model-considered-harmful/" rel="noopener noreferrer"&gt;Cactus model&lt;/a&gt; and &lt;a href="https://trunkbaseddevelopment.com/" rel="noopener noreferrer"&gt;Trunk based development&lt;/a&gt;. I highly recommend to consider these Git branching strategies for your next Terraform project collaboration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhs2ow703tlucgd80cc22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhs2ow703tlucgd80cc22.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;a href="https://www.nomachetejuggling.com/2017/04/09/a-different-branching-strategy/" rel="noopener noreferrer"&gt;A Branching Strategy Simpler than GitFlow: Three-Flow&lt;/a&gt; by Rod Hilton




&lt;h1&gt;
  
  
  Some Caveats
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Merge Directly into Dev Branch only
&lt;/h3&gt;

&lt;p&gt;Merging directly from local into the main branch should only be for the development environment. Production and staging environments should have a different merge and release processes like some of the Git branching examples I’ve shared in the previous section. I am a strong advocate of having 1 branch per environment and I’ll share more on this in a future blog post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Review Processes
&lt;/h3&gt;

&lt;p&gt;Merging feature code directly into the development/main branch meant that the typical code review processes with pull requests wouldn’t be possible. If code reviews are a necessity, use short-lived feature branches solely for the purpose merging code. You can consider shifting your team’s code review processes to before releasing to your staging environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terraform v0.12
&lt;/h3&gt;

&lt;p&gt;Usage of count and for_each for feature toggles presents some challenges if your team is still on an older Terraform version. Modules do not have native count and for_each support in v0.12 and should be something to keep in mind. You would have to bake the feature toggle into the module itself by passing in true or false values to decide whether to create the resources within the modules. It’s not ideal, but still doable.&lt;/p&gt;

&lt;p&gt;Native Module count and for_each is supported in Terraform v0.13 and higher.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collaborators must be Trusted
&lt;/h3&gt;

&lt;p&gt;To commit directly into the main branch, all collaborators must be on the same team and trusted. This Git branching strategy is not suitable for public repositories.&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing Thoughts
&lt;/h1&gt;

&lt;p&gt;I’d like to end this lengthy blog post with this quote from Rod Hilton.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A developer’s primary form of communication with other developers is source code&lt;/strong&gt;. It doesn’t matter how often you have stand-up meetings, when it comes to the central method of communication, &lt;strong&gt;long-running branches represent dead silence&lt;/strong&gt;.&lt;/p&gt;

&lt;a href="https://www.nomachetejuggling.com/2017/04/09/a-different-branching-strategy/" rel="noopener noreferrer"&gt;A Branching Strategy Simpler than GitFlow: Three-Flow&lt;/a&gt; by Rod Hilton



&lt;/blockquote&gt;

&lt;p&gt;Thank you for taking the time to read this. Reach out to me if you would like to discuss further.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Passionate about Cloud and Terraform</title>
      <dc:creator>Zhen Kai</dc:creator>
      <pubDate>Sun, 25 Oct 2020 02:51:55 +0000</pubDate>
      <link>https://forem.com/zhenkai/hello-world-4d06</link>
      <guid>https://forem.com/zhenkai/hello-world-4d06</guid>
      <description>&lt;p&gt;Test post&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
