<?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: Joseph</title>
    <description>The latest articles on Forem by Joseph (@josephbulger).</description>
    <link>https://forem.com/josephbulger</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%2F1366330%2Faf0c62cd-2e15-465a-856a-238c5f6a0bf8.png</url>
      <title>Forem: Joseph</title>
      <link>https://forem.com/josephbulger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/josephbulger"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Wed, 26 Feb 2025 13:22:19 +0000</pubDate>
      <link>https://forem.com/josephbulger/-5346</link>
      <guid>https://forem.com/josephbulger/-5346</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/imaginex" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F9634%2F035402bf-0d6a-4de6-84e6-8ef75703c8f9.png" alt="ImagineX" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2897279%2F55c753f5-92a0-4477-bbe6-e9ceee7fe966.jpg" alt="" width="800" height="825"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/imaginex/agentic-ai-assistants-3g31" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Agentic AI Assistants&lt;/h2&gt;
      &lt;h3&gt;Pablo Orozco for ImagineX ・ Feb 25&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#crewai&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#agents&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>ai</category>
      <category>crewai</category>
      <category>automation</category>
      <category>agents</category>
    </item>
    <item>
      <title>Intro to Cloud: An API Gateway</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Mon, 02 Dec 2024 14:38:16 +0000</pubDate>
      <link>https://forem.com/imaginex/intro-to-cloud-an-api-gateway-a5f</link>
      <guid>https://forem.com/imaginex/intro-to-cloud-an-api-gateway-a5f</guid>
      <description>&lt;p&gt;I tried to think of the best way to describe building an app from scratch in the cloud, but there are so many different ways you can easily get lost in all the options. So I decided I would start by showcasing what is arguably the easiest app you can make from a computing perspective. It offers the least amount of operational overhead, but at the same time, it's the most expensive option &lt;em&gt;on paper&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS API Gateway
&lt;/h1&gt;

&lt;p&gt;When I think of simple I think API. That might just be because I'm so used to them at this point, but there are also some direct comparisons to other compute solutions which we'll get into later in other posts as we get into more complex cloud compute ideas. Right now, though, we're going to use AWS API Gateway to deploy the first version of the API we're going to make for the rest of this series. This is a great choice if you want to spend as little time maintaining your gateway as possible. There are no load balancers to deal with. You can optionally put an edge cache (CloudFront) in front of it easily, and it connects to other serverless technology in AWS seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the gateway
&lt;/h2&gt;

&lt;p&gt;For the example I'm building, I'm going to actually use a terraform module that already has all the pieces we need. The first thing we need to do is add the module to our project by editing the &lt;code&gt;cdktf.json&lt;/code&gt; file to include the module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terraformModules&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apigateway&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terraform-aws-modules/apigateway-v2/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~&amp;gt; 5.2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later on, we're going to add another module but for now, that's all we need to build the gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the gateway
&lt;/h2&gt;

&lt;p&gt;I'm going to make a &lt;code&gt;serverless&lt;/code&gt; folder and put an &lt;code&gt;api.ts&lt;/code&gt; to hold all the work we'll need to get the app up and working. Inside our class, we have to import the module we made and then create the gateway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Apigateway&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../.gen/modules/apigateway&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Apigateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-gw`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-gw`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;protocolType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTTP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;corsConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;allowHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-amz-date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-amz-security-token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-amz-user-agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;allowMethods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;allowOrigins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;createDomainName&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="na"&gt;createDomainRecords&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="na"&gt;createCertificate&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="na"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hostedZoneName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostedZone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ANY /&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambdaArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload_format_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;timeout_milliseconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;$default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambdaArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload_format_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;timeout_milliseconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&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;In my case, I already had a hosted zone set up in my AWS account for a subdomain &lt;code&gt;aws.josephbulger.com&lt;/code&gt;. I decided to go ahead and add this gateway to that so the hosted zone in this example is &lt;code&gt;aws.josephbulger.com&lt;/code&gt; and the API's domain is &lt;a href="//apigw.examples.how2cloud.aws.josephbulger.com"&gt;apigw.examples.how2cloud.aws.josephbulger.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You don't have to set this up with a domain. If you don't AWS will generate a random URL to host your gateway instead, and you can use that for making your API calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  CORS
&lt;/h3&gt;

&lt;p&gt;I have the API's cors configuration set up to allow traffic from anywhere because, with this kind of setup, I'm assuming the front end that would potentially use this is not hosted by the same app. This is a common approach I use for my systems. I don't like to have the API backend be served by the same system that serves the front end. I like to keep them separate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Routes
&lt;/h3&gt;

&lt;p&gt;Finally, you need to define the routes that the gateway will serve. In my case, I'm going to send all my traffic to a single lambda, which we'll talk about in a second. For now, we just need to know to send all the traffic over to the lambda, and let it figure out what to do with it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stages
&lt;/h4&gt;

&lt;p&gt;Additionally, we have to define the default stage. Stages in API Gateway are kind of a weird concept. It's beyond the scope of what I want to cover in this post, but for this purpose let's just say you have to define a default one, so the gateway knows which stage gets the traffic. In our case, we're just going to set it up the same way we set up the route we made.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lambda
&lt;/h1&gt;

&lt;p&gt;The lambda is the part that makes this computing solution simple and easy. It does this because it's truly serverless. We don't have to worry about running a server, neither as a virtual machine an EC2 instance somewhere, nor as compute sitting on top of an orchestration plan like ECS or k8s. We just create the app, and give it to the lambda. The largest amount of operational overhead with the lambda boils down to dealing with how much CPU and memory to allocate to it, and these days you only specify the memory because the CPU is adjusted based on how much memory you choose. The price we pay for this ease of use is, well, the actual sticker price. All things considered, you can expect to pay anywhere from double to up to 6 times the price of other compute solutions in AWS for the equivalent &lt;em&gt;compute time&lt;/em&gt;, but that's on paper. In reality, there are some very compelling reasons to use Lambda, including financially.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up your Lambda
&lt;/h2&gt;

&lt;p&gt;So to get using the lambda we first need to add the module into cdktf the same way we did the gateway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terraformModules&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lambda&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terraform-aws-modules/lambda/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~&amp;gt; 7.14.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defining the Lambda
&lt;/h2&gt;

&lt;p&gt;Now we can add the lambda to our API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-function`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-function`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;createPackage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;packageType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;architectures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x86_64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imageUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ecrUri&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&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="na"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="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;lambdaArn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambdaFunctionArnOutput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We used the &lt;code&gt;lambdaArn&lt;/code&gt; earlier where we defined the gateway. This is actually where it came from, as an output from the lambda module. Now the last thing we need to do to connect the two is set up the lambda's triggers. The trick to them is that the lambda needs to know about the gateway, but we have to define the lambda first. What we do to get around this is just define the triggers &lt;strong&gt;after&lt;/strong&gt; we make both the lambda and the gateway and then add the triggers back into the lambda.&lt;/p&gt;

&lt;p&gt;It looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;apiExecArn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiExecutionArnOutput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;triggers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;APIGatewayAny&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apigateway&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source_arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiExecArn&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="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowedTriggers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;triggers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the lambda is connected to the gateway we need to create the app that the lambda is going to run.&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker
&lt;/h1&gt;

&lt;p&gt;I created a small server in &lt;code&gt;examples&lt;/code&gt; that creates a simple go web server built using docker. What's nice about Go is that you can actually deploy it from a scratch image so the overall size of the image is really small (~8 MB). Perfect for running on a lambda.&lt;/p&gt;

&lt;p&gt;The docker file looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; *.go ./&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETARCH=amd64&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GOARCH=$TARGETARCH&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETOS=linux&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GOOS=$TARGETOS&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-o&lt;/span&gt; /api

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /api /api&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/api"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll be building it using &lt;code&gt;docker buildx bake&lt;/code&gt; so we need to set up a bake file too:&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;group&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;targets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"api"&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;"IMAGE_URI"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"api"&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;"IMAGE_TAG"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="s2"&gt;"api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;dockerfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Dockerfile"&lt;/span&gt;
 &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;
 &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"${IMAGE_URI}:${IMAGE_TAG}"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="nx"&gt;platforms&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"linux/amd64"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;TARGETARCH&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"amd64"&lt;/span&gt;
 &lt;span class="nx"&gt;TARGETOS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"linux"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="nx"&gt;attest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
 &lt;span class="nx"&gt;provenance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provenance
&lt;/h2&gt;

&lt;p&gt;One issue with deploying to lambdas, at least for now, is that they don't support multi-architecture docker images. So when we deploy our image to ECR we have to disable provenance so docker knows to only build the image in the format we specify with multi-mode turned off.&lt;/p&gt;

&lt;p&gt;That's why in our GitHub action we turn provenance off when we do the docker build and push:&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="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/bake-action@v5&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;workdir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;examples/api&lt;/span&gt;
    &lt;span class="na"&gt;provenance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;IMAGE_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt;ECR_URL&lt;/span&gt;&lt;span class="pi"&gt;]]&lt;/span&gt;&lt;span class="s"&gt;/api&lt;/span&gt;
    &lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.ref_name }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  That's It
&lt;/h1&gt;

&lt;p&gt;Now you have a fully functioning API. You can see the one I've deployed at &lt;a href="https://apigw.examples.how2cloud.aws.josephbulger.com/" rel="noopener noreferrer"&gt;https://apigw.examples.how2cloud.aws.josephbulger.com/&lt;/a&gt;. There's also a route you can play around with the changes in the response based on what you give it: &lt;a href="https://apigw.examples.how2cloud.aws.josephbulger.com/some/kind/of/trick" rel="noopener noreferrer"&gt;https://apigw.examples.how2cloud.aws.josephbulger.com/some/kind/of/trick&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;If you want to follow along with the code that I used to build this, or any other part of the "from scratch" series, check out my &lt;a href="https://github.com/josephbulger/how-to-cloud" rel="noopener noreferrer"&gt;how to cloud repo&lt;/a&gt; on github.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>aws</category>
      <category>devops</category>
      <category>serverless</category>
    </item>
    <item>
      <title>How to Cloud: Fencing</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Mon, 01 Jul 2024 13:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-cloud-fencing-3a2h</link>
      <guid>https://forem.com/aws-builders/how-to-cloud-fencing-3a2h</guid>
      <description>&lt;p&gt;Before you can dive into deploying your application you need to create a safe environment for your application to be deployed in. This starts with understanding some basics around making good security decisions, so this next topic is about how to make a good “fence”.&lt;/p&gt;

&lt;h1&gt;
  
  
  First Things First
&lt;/h1&gt;

&lt;p&gt;Ok, in all fairness, if you are just getting started on your cloud journey, you probably only need one vpc and one account. &lt;a href="https://www.josephbulger.com/blog/simplicity"&gt;Keep it simple&lt;/a&gt;. When you get to the point where you are going to be responsible for multiple products, then you need to potentially consider different options. Also, the “you” here is not just yourself as a leader of your team, it’s the overall landscape of your organization or company. Is your company planning on migrating multiple platforms or systems to the cloud? Then you need to read on. If they are a start up that’s literally just starting out on a single product idea, you can probably read this later, but it’s still useful to know. Either way, that’s why we need to talk about VPCs.&lt;/p&gt;

&lt;h1&gt;
  
  
  VPCs
&lt;/h1&gt;

&lt;p&gt;In the cloud world, VPCs act as a fence. They handle a number of key technical issues related to security and network pathing that you need to be able to make quick decisions on. I’m not going to do a deep dive on all the ins and outs of VPCs, but you need to learn how they work so that you can easily choose which way to go in your organization, and with the app you are building. I plan to cover the kinds of decisions that you could make, and why you’d go one way or the other. This decision is kind of in between a hard the right call situation vs preferences. I say that because depending on decisions that your company or organization has made that is outside of your control, you will want to align your decision to the things that were already chosen for you. With that being said, since VPCs act as a fence in a lot of ways, let’s talk about the different ways that you can build that fence in your account.&lt;/p&gt;

&lt;h2&gt;
  
  
  One VPC per Account
&lt;/h2&gt;

&lt;p&gt;This strategy will make sense for a variety of reasons. You might be leading a single team working on a single product but you own an entire account. What’s the point in having more than one VPC when you are actually only building one thing? There really isn’t a good reason to have more than one. Things get trickier when you are responsible for more than one product or platform. I say it that way because in order to build the right fence you have to start thinking about what you build as something consumed by a customer, or client. The second that your answer to that question becomes, “well I have more than one”, then I argue you have another decision to make.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Accounts or Multiple VPCs
&lt;/h2&gt;

&lt;p&gt;The bottom line is that every product or platform that you own should have a fence around it. The thing is, however, that while VPCs can and do act as a good fence in a lot of situations, there are some drawbacks to having multiple products and/or platforms in the same account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Noisy Neighbors
&lt;/h3&gt;

&lt;p&gt;A really good fence isolates you from noisy neighbors. In most cases, VPCs do a pretty good job of this, especially from a networking perspective. They prevent unwanted access through the routing tables you will make coupled with the subnets that are associated to them. That’s all great. What’s not so great is what happens when one system is hogging all the allocated resources you have on your account. Oh yeah, you heard me correctly. Your cloud account has limits. AWS calls them “quotas” now, actually. You can look up the specifics of what your account has and get a report on what they currently are, too. The point, though, is that when your account has reached it’s quota on something you will be denied from allocating more of that thing for your entire account, everywhere. For example, if you have two products on your account, and one of them auto scales during a spike and uses up your quota on auto scaling groups, and then your other product sees a spike as well, guess what, that second product will bottleneck and start crashing. Nothing you can do with your VPC will prevent that from happening. If this is a significant concern for your team, and you operate in an environment, organization, or company with a lot of products and/or platforms in this manner, you will probably be better off strategically operating in a multiple account scenario.&lt;/p&gt;

&lt;p&gt;Having said all that, there are some good reasons to own a single account and maintain good boundaries by leveraging multiple VPCs. If your company only sells one product, that’s a good sign you might be better off with one account, even if you have to deploy multiple systems. If you are in small company, odds are it’s better to have one account. What this means is you have to be aware of those service limitations, and how to deal with them correctly. You need to set up monitors to alert you when you are getting close. We have monitors set up to alert us when we get to 80% of our quota on anything. Once we get the alert we send a support ticket to our cloud vendor to increase the quotas. If you have a mature process for dealing with those situations you can deal with noisy neighbors pretty easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  VPC for each product
&lt;/h2&gt;

&lt;p&gt;If you do operate in a single account with multiple products, then you should deploy each product in their own VPC. This prevents systems from accidentally colliding with one another and becoming noisy neighbors, or teams creating security issues by not collaborating together properly. Each VPC has their own setup of networking and traffic rules, which can be managed by the team that owns the product that lives in it. Having multiple products inside one VPC probably also means you have multiple teams operating in that VPC, and that means they might step on each other’s toes.&lt;/p&gt;

&lt;h2&gt;
  
  
  VPC for each environment
&lt;/h2&gt;

&lt;p&gt;You should also split up your environments into different VPCs. You do have multiple environments, right? We’re not going to talk through that right now, that could be it’s own blog post. For now, I’m assuming you at least have a non prod and a prod environment, which means you need to have at least one VPC for prod and another for non prod. One common pattern we’ve used it actually making an account for production and another account for all non production systems, so we’re guaranteed that no non production system could ever take down a production system for any reason. That actually segues nicely into the next thing we need to talk about.&lt;/p&gt;

&lt;h1&gt;
  
  
  Accounts
&lt;/h1&gt;

&lt;p&gt;I guess logically it would make sense to start off talking about accounts and then get into VPCs, but I think this actually makes more sense when you are trying to think about the decisions you need to be making. It becomes a lot more clear why you need multiple accounts or not when you understand the reason why you needed the VPCs to begin with, and how their “fencing” works. The example I gave before is a good one when you want to isolate production from anything going wrong, but you still need to consider the reasons why you might want to have a single account solution vs a multi account solution. Let’s get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single Account Model
&lt;/h2&gt;

&lt;p&gt;I know I just got finished talking about how important it is to isolate production from non production systems (and it is), but there are some situations where it makes sense to go single account that are worth discussing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Innovating
&lt;/h1&gt;

&lt;p&gt;If you are innovating and at the very beginning stages of whatever idea you are cooking up, there’s no reason to have multiple accounts, because you have no production ready product anyway. Don’t spend the extra money on a prod account just to prove that you can do it when you are in research mode.&lt;/p&gt;

&lt;p&gt;That doesn’t mean you are going to be able to “flip a switch” and go from proof of concept straight to prod and making money. That’s not what I’m saying. What I’m saying is, don’t worry about tomorrow’s problems today when you have no good reason to plan that out yet, worry about that tomorrow when you have a better idea of what you are going to try to do.&lt;/p&gt;

&lt;h1&gt;
  
  
  Mature DevOps Culture
&lt;/h1&gt;

&lt;p&gt;This is going to seem like complete ends of a spectrum, mostly because it is, but another reason why it’s feasible to only have one account is because your overall DevOps culture is extremely mature. Everyone knows how to execute well, and it’s boring. You’ll know your teams are heading down the right road towards maturity because the same teams that probably can handle operating a single account will be the same ones that beg you to isolate production. It’s a good litmus test. They understand the risks it poses having everything in one account. The decision you will have to make in this situation is largely a financial one at this point. Does it save you a significant amount of cost to merge your accounts into one? Odds are it won’t, because of the way you are billed for the services you use in the cloud. Either way, once you do a cost analysis then you’ll know which way you should go.&lt;/p&gt;

&lt;h1&gt;
  
  
  Multiple Accounts Model
&lt;/h1&gt;

&lt;p&gt;So if your company didn’t fall into of those the situations above, then you need to go multi account. Deciding to go multi account is one thing, but knowing how to split them up is another. Let’s talk about considerations you need to make.&lt;/p&gt;

&lt;h1&gt;
  
  
  Separate Environments
&lt;/h1&gt;

&lt;p&gt;We’ve talked at length about the production, non production split, so that should be a given at this point, but let’s also talk about some other varieties here. It’s very common to do a prod/non-prod mix, but something else to consider is how well established are your non production systems in your company? Meaning, do all of your dev systems talk to each other? All of your integration systems work well together,? Are all of your test environments playing nicely together? If you have strong synergy in your company across environments, then it makes sense to actually separate them out by accounts too. Why? because it makes establishing communications among them a lot easier. It makes the permissions model simpler, the networking easier, etc. You don’t have to worry about a dev system from one team accidentally reaching out to integration or testing and blowing something up.&lt;/p&gt;

&lt;h1&gt;
  
  
  Multi Tenant Platforms
&lt;/h1&gt;

&lt;p&gt;When your company is large enough you are bound to have teams creating systems that are leveraged across multiple products, which invariably means they will be servicing multiple teams. If you happen to engineer as system in that manner, congratulations you’ve just become multi tenant. In this situation multi tenant platforms should have their own accounts. It creates the proper level of isolation from their “customers” so that they can properly operate in a SaaS engagement model. This is particularly important when the technology office is large enough to have multiple organizations inside it. Speaking of…&lt;/p&gt;

&lt;h1&gt;
  
  
  Multiple Organizations
&lt;/h1&gt;

&lt;p&gt;If your technology office is large enough, you’ll have multiple organizations inside it. To keep things clean, it makes a lot of sense to have each organization own and operate their own accounts. There are exceptions to this (remember what I said about a mature devops culture), but in reality the larger the company is the more sense it makes to break out accounts to match the organizational structure of the technology office.&lt;/p&gt;

&lt;h1&gt;
  
  
  Allocation
&lt;/h1&gt;

&lt;p&gt;As with all things cloud, once you’ve decided which way you are going to go, you need to &lt;a href="https://www.josephbulger.com/blog/how-to-cloud-iac"&gt;script it&lt;/a&gt;. So let’s talk about that.&lt;/p&gt;

&lt;p&gt;In the example I’m doing, I’m going to build out the repository in a multi account, single vpc per account model. I’m choosing this because I wanted to do something that is slightly more difficult than a “startup situation” but not so complicated that you might get lost in the weeds.&lt;/p&gt;

&lt;p&gt;So what does this look like? Inside our iac folder, I’ve created an account folder just for account allocation, so we’ll be making everything for the VPC in there. In the future when we need to make more than one account we can refactor this folder to accomodate. Later the systems we made will be allocated in totally different folders so we’ll have a clear separation from what we needed on the account and what we needed for each system we’re building.&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;class&lt;/span&gt; &lt;span class="nc"&gt;MyStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TerraformStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roleId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;devOps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AwsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;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;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htc-vpc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htc-vpc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cidr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.0.0/16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;azs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;privateSubnets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.1.0/24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.2.0/24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.3.0/24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;publicSubnets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.101.0/24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.102.0/24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.103.0/24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;enableNatGateway&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;ecr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EcrRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htc-ecr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htc-ecr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;imageTagMutability&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MUTABLE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;how-to-cloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synth&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 left out a number of other things I’ve added to the repo for brevity. None of the other code in the account is related to allocating the VPC so it’s not relevant to this article, but if you want you can always &lt;a href="https://github.com/josephbulger/how-to-cloud"&gt;head over there and dive in&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Cloud: IaC</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Mon, 24 Jun 2024 13:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-cloud-iac-3ifl</link>
      <guid>https://forem.com/aws-builders/how-to-cloud-iac-3ifl</guid>
      <description>&lt;p&gt;In my last post I intentionally jumped the gun a little bit when it came to deploying containers into your cloud account. It should be your first priority to get your team to learn containerization, but deploying to the cloud requires allocating container registry infrastructure. That brings us to our next topic which is Infrastructure as Code, and it’s a very close second in priority.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t do it manually
&lt;/h2&gt;

&lt;p&gt;Do not, under any circumstances, try to allocate infrastructure in your account manually through the console. I have tried to think of any situation where it makes sense to just use the console, but there really just isn’t one unless you are truly just toying around. Any product you intend to build and make money on, you can’t afford the windfall of problems you will encounter if you go down that path. Allow me to highlight a couple of these just to give you an idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your team will forget what they were doing
&lt;/h3&gt;

&lt;p&gt;It may seem like it’s faster to just jump into the console and allocate something, especially like a container registry, really quickly and just get it over with. After all, it’s so easy. 6 months later, your team has forgotten how many things they allocated manually, and they don’t remember where to find them, and all of the sudden you are going to have to build it all over again because it’s actually lost in your account and there’s too many other things in your account to find the one thing you need to find. Having your infrastructure written in code completely solves this problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fear of making changes
&lt;/h3&gt;

&lt;p&gt;Even if your team can find the infrastructure they made 6 months ago, they will be terrified to change it, because they won’t be able to easily tell what depends on it, and how it depends on it. Their solution will just be to make another one while keeping the original around for legacy reasons, and your cloud bill cost will suffer the consequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed of Delivery
&lt;/h3&gt;

&lt;p&gt;The reality is that, if you are using the console, you think you are going faster, but you are actually going at a minimum 4 times slower than the way I do it. I know this from experience. I’ve watched other teams that did it manually because they were convinced they could get it done quicker. They took a month and had nothing to show for it because everything kept breaking, and my team came in and got their entire account working from scratch in 1 week using scripts. Allocating an entire compute cluster stack for my teams today takes less than an hour, from start to finish. I’ve never seen a team allocate a web service on any compute cluster cloud product in the console in that time frame. You think you’re going faster in the console because of an illusion. That illusion is the self gratifying feedback loop that the console gives you from seeing a little bit of progress every 10 minutes or so. It is a complete falsehood.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowing what’s necessary and what’s flavor
&lt;/h2&gt;

&lt;p&gt;Before I get into the next steps for IaC, I want to take a second and point something out that’s really important for decision making as a leader. You need to be able to tell the difference between what is the right call vs what’s just your own preference or the team’s preference. What I said earlier about manual vs scripting? That’s the right call. You want to make sure that’s something you handle at your level as a leader. Which tech stack you choose to do the scripting? That’s preference. You want to help facilitate your team into making the best decision here, but I like to leave this area up to my teams, collectively. Empower them to do this for themselves and work together on this decision, not in silos for those of you that manage multiple teams. There are, of course, pros and cons to what you choose, and some situations will call for one solution more than another, but you can be successful using a lot of different tech stacks.&lt;/p&gt;

&lt;p&gt;In the realm of IaC you have a few options. I’ll cover the main points how I see them today and explain why I choose what I choose, but that doesn’t mean it’s going to be the right choice for your team. This is the preference part. You need to choose what’s right for your situation, and you need to get good at making that choice quickly.&lt;/p&gt;

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

&lt;p&gt;The OG for AWS IaC scripting. It was the best and most comprehensive solution for allocating AWS infrastructure for a long time. It isn’t hosted inside AWS, however, because it’s just a cli you run yourself, but it can be extremely versatile. It used to be that AWS would roll out commands to the cli before any other IaC solution so you had to leverage it for the newly released tech on your account.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS CloudFormation
&lt;/h3&gt;

&lt;p&gt;The first true IaC language developed by AWS. It’s declarative, runs inside your AWS account which is great for tracking and maintainability. CF was always lagging with features in the past because the cli would get them first, but AWS has done some work to try to close that gap so it may not be that much of an issue anymore. Being declarative has it’s drawbacks, though.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure ARM
&lt;/h3&gt;

&lt;p&gt;For those of you that use Azure, ARM is the equivalent tech for that cloud vendor.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS CDK
&lt;/h3&gt;

&lt;p&gt;Which is why AWS made a new language for IaC called AWS CDK. This is the next evolution in AWS’s offering for IaC. It is programmatic, getting away from being declarative, which allows for much more flexibility in defining your infrastructure. It is, however, still vendor locked into only leveraging AWS cloud technology. If you are 100% using only AWS Cloud offerings, then any of AWS’s options should be fine, including CDK, but before you make that choice let’s talk about what I mean by 100%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terraform
&lt;/h3&gt;

&lt;p&gt;Terraform offers two varieties of IaC, one declarative and one programmatic, but the main distinction that Terraform has over the AWS offerings above is that it’s built to be multi-cloud, multi-provider. Let’s talk about what multi-provider actually means.&lt;/p&gt;

&lt;p&gt;Most people getting into the cloud and learning about IaC will latch onto the idea of scripting out whatever they are building in their cloud account of choice pretty quickly. It’s a pretty natural process. What will happen to a lot of teams, however, is that you will come to realize that you actually purchase or own licenses for all kinds of other services you need to build the product your team is focused on. Things like…. maybe you use fastly as a cdn provider, MongoDB Atlas for a database solution, or datadog as your observability platform. Now, you may be asking yourself, why do any of those things matter? Well, with terraform, all of those products are connected in the terraform ecosystem as providers. So you can script one IaC solution together that ties all of those things, including your cloud account infrastructure, all in one place. So when I was talking earlier about 100%, I was talking about everything your team uses.&lt;/p&gt;

&lt;p&gt;Point of note, I didn’t really bring up GCP anywhere in this discussion, but that’s because they leverage APIs for a lot of their allocation, and those APIs feed into terraform providers so if you are in GCP land you are probably looking to adopt terraform. In a lot of ways, the same may be true for folks looking to adopt Azure, even though they have their own templating stack. Terraform is a choice my teams tend to make these days because of all the flexibility I mentioned before. It tends to be the selection of choice, although I have managed teams in the past that used AWS CloudFormation extremely successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting back
&lt;/h2&gt;

&lt;p&gt;Ok so getting back to the example we started, let’s now talk about how you actually create that infrastructure you need for you container registry. My teams use Terraform CDK so my examples will be with that, but our approach translates well to whatever choice you make. And again, I’m not going to get into the details of how to do all things terraform, rather I want to point out some key decisions that you want to make when you start building out your IaC.&lt;/p&gt;

&lt;h3&gt;
  
  
  Organizing your stack
&lt;/h3&gt;

&lt;p&gt;When we start this build out you are going to want to start organizing where you put your scripts, and from my experience you want to immediately split up infrastructure you make for your account, and what you make for the product you are deploying, and their subsequent environments. Things you need to do in your account would be things like allocating your VPC, networking, domain management, stuff that you don’t normally handle on a product by product basis. For this example I’m going to actually put my ECR allocation at the account level because I’m going into this with the mindset that the overall stack here is going to be rather small, so allocating an ECR for each product is probably overkill. Think the difference between a start-up vs an enterprise corporate team. This stack is more of a start-up scene right now, whereas in a more enterprise environment you might allocate an ECR for each product, or maybe each team.&lt;/p&gt;

&lt;p&gt;This is what allocating the ECR looks like right now in my repo, it’s nothing fancy:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TerraformStack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TerraformOutput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cdktf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AwsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cdktf/provider-aws/lib/provider&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EcrRepository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cdktf/provider-aws/lib/ecr-repository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TerraformStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AwsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;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;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EcrRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;how-to-cloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;how-to-cloud-ecr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TerraformOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;testing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;how-to-cloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is actually way less important here than the folder structure I’ve started. We started with setting up docker and we have that over in a docker folder, and now that we’re allocating infrastructure we put that over in an iac folder, with an account folder inside that just for the account stuff. Later we’ll make more scripts in product folders that are separate from the account, so building out new infrastructure will be really easy.&lt;/p&gt;

&lt;p&gt;But that’s for a later post.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>beginners</category>
      <category>terraform</category>
    </item>
    <item>
      <title>From Scratch: OIDC Providers</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Mon, 20 May 2024 12:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/from-scratch-oidc-providers-252d</link>
      <guid>https://forem.com/aws-builders/from-scratch-oidc-providers-252d</guid>
      <description>&lt;p&gt;It's time to divert our attention to what will soon become our infrastructure deployment process. This will feel a lot like application deployments, but there will be some stark differences we'll be going over. Our applications will incorporate infrastructure in the future, but not everything we do with our IaC will be related to an application.&lt;/p&gt;

&lt;h1&gt;
  
  
  Github Actions
&lt;/h1&gt;

&lt;p&gt;I'll be deploying my infrastructure through Github Actions because it's where I keep my repositories anyway, and keeping the deployment scripts there is really convenient and makes tracking things really easy too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Github and AWS Dance
&lt;/h2&gt;

&lt;p&gt;If we're going to be deploying infra from within github actions, it stands to reason we'll have to give our workflows permission to deploy infrastructure in our AWS account. That's where OIDC comes in. AWS allows you to build a trust relationship with an OpenID connected provider (in our case github), and then define a permissions policy to a role in your account. Once the role is defined, you then have complete control over what permissions you let that role have within your AWS account.&lt;/p&gt;

&lt;p&gt;Github has some &lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services"&gt;documentation&lt;/a&gt; on how the whole process works.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating the Provider
&lt;/h1&gt;

&lt;p&gt;We'll start off by essentially following &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html#idp_oidc_Create_GitHub"&gt;AWS guidance on connecting with Github&lt;/a&gt;. In order to do that, we're going to need to grab a certificate from github to put into our provider definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificate
&lt;/h2&gt;

&lt;p&gt;Grab the certificate from github by using&lt;/p&gt;

&lt;p&gt;&lt;code&gt;openssl s_client -servername token.actions.githubusercontent.com -showcerts -connect token.actions.githubusercontent.com:443&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The cert you want is the last one you see in the terminal. You want to create a file named &lt;code&gt;certificate.crt&lt;/code&gt; and put all the cert information from the terminal which includes the &lt;code&gt;-----BEGIN CERTIFICATE-----&lt;/code&gt; and &lt;code&gt;-----END CERTIFICATE-----&lt;/code&gt; parts too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thumbprint
&lt;/h2&gt;

&lt;p&gt;Now with the certificate, we'll need to run through a process to &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html"&gt;grab the thumbprint&lt;/a&gt;. Open up a terminal in whatever path you made put the cert, and run the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;openssl x509 -in certificate.crt -fingerprint -sha1 -noout&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The output should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SHA1 Fingerprint=99:0F:41:93:97:2F:2B:EC:F1:2D:DE:DA:52:37:F9:C9:52:F2:0D:9E&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You only care about the alphanumerics in the fingerprint itself, so remove the colons and you're left with something like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;990F4193972F2BECF12DDEDA5237F9C952F20D9E&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Depending on where your provider is, you're fingerprint will be different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider Definition
&lt;/h2&gt;

&lt;p&gt;Now we're finally ready to script the provider itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oidcProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IamOpenidConnectProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-provider`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://token.actions.githubusercontent.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;clientIdList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sts.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;thumbprintList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1b511abead59c6ce207077c0bf0e0043b1382612&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the thumbprint list you'll put the fingerprint you got from earlier. You need to make sure your thumbprint is in all lowercase when you do this, because if you don't then terraform will always detect drift from the AWS provider that is provisioned since AWS will tell it it's in all lowercase.&lt;/p&gt;

&lt;h1&gt;
  
  
  Trust Relationship
&lt;/h1&gt;

&lt;p&gt;After we've defined the provider we have to describe the trust relationship between it and our AWS Account. This basicaly boils down to defining a policy that allows role assumption with a web identity.&lt;/p&gt;

&lt;p&gt;The policy looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assumePolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataAwsIamPolicyDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-assume-policy`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sts:AssumeRoleWithWebIdentity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Federated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;identifiers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;oidcProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;StringEquals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;token.actions.githubusercontent.com:aud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sts.amazonaws.com&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;StringLike&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;token.actions.githubusercontent.com:sub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;githubOrg&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="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;The provider ARN is the provider we made earlier, and the value in &lt;code&gt;token.actions.githubusercontent.com:sub&lt;/code&gt; is your github org you want to allow to provision in your AWS account. You can limit this in various ways, but in my case I just want to give the whole org the same set of permissions. Alternatively you can limit this down to specific repos, or github envs, etc etc.&lt;/p&gt;

&lt;h1&gt;
  
  
  Permissions
&lt;/h1&gt;

&lt;p&gt;Now that we have the provider scripted and the trust relationship defined we can turn our attention to what permissions we want to give the role. To keep things simple, I'm going to give the deployment process &lt;code&gt;PowerUserAccess&lt;/code&gt;. This is a managed policy from Amazon, and I have no security concerns around the permissions it allows. If you work in a space where the security concerns need to be more limiting, this is where you could make a more restrictive policy yourself, which is also fine.&lt;/p&gt;

&lt;p&gt;For this situation, I just need to grab the power user policy like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;powerUserPolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataAwsIamPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-trust-policy`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PowerUserAccess&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Role
&lt;/h1&gt;

&lt;p&gt;None of this matters if we don't create a role that is going to use all this stuff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IamRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-trust-role`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;namePrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-trust-role`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;assumeRolePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;assumePolicy&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assume role policy here is the trust relationship we defined. Now we need to give the role power user permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IamRolePolicyAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-rpa`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;policyArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;powerUserPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Try is Out
&lt;/h1&gt;

&lt;p&gt;After we've &lt;code&gt;cdktf deploy '*'&lt;/code&gt; the stack we should try this out and see if it's working. I made a really simple workflow that just configures the aws cli and then prints out all the roles in the account to make sure it has the permissions we gave it earlier and everything is wired up properly.&lt;/p&gt;

&lt;p&gt;The workflow file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Infrastructure&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Git clone the repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;configure aws credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;aws&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;iam&lt;/span&gt;&lt;span class="pi"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;therolearn-i-just-made&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;role-session-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;githubsession&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Proof of Life&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;aws iam list-roles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that it's just a matter of cleaning things up a little bit and push main up to github, which triggers the workflow to run, and I'm able to see the github action produce a list of the roles in my account.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next Steps
&lt;/h1&gt;

&lt;p&gt;We're now ready to start letting Github Actions handle our infrastructure deployments. That's going to involve changing a few things we already did and then making some more workflows in our repo, but that's for next time.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>From Scratch: Permissions</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Wed, 15 May 2024 23:55:12 +0000</pubDate>
      <link>https://forem.com/aws-builders/from-scratch-permissions-4c94</link>
      <guid>https://forem.com/aws-builders/from-scratch-permissions-4c94</guid>
      <description>&lt;p&gt;Once we have our groups and users set up from our external identity provider we can move on to defining what permissions they should have. This is mostly straightforward, but we'll have to take a few detours along the way because of how groups work with the Google Workspace IDP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Permission Sets
&lt;/h2&gt;

&lt;p&gt;The first thing we have to do is script our permission sets. We will use these to connect IAM policies to our IDP groups. This will all be done in CDKTF.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SsoadminPermissionSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-admin`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-admin`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arnId&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;There's not a lot to this. The ARN we are using here for the &lt;code&gt;instanceArn&lt;/code&gt; actually comes from the same &lt;code&gt;DataAwsSsoadminInstances&lt;/code&gt; we used to get the &lt;code&gt;storeId&lt;/code&gt; &lt;a href="https://www.josephbulger.com/blog/from-scratch-iac"&gt;before&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policies
&lt;/h2&gt;

&lt;p&gt;Now that we have our admin permission set, we have to actually give it an admin policy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SsoadminManagedPolicyAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-admin-policy`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arnId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;managedPolicyArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:iam::aws:policy/AdministratorAccess&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;permissionSetArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The policy here is just the AWS managed policy that comes with every account. We're ready to deploy at this point, so we need to go back into our AWS console, and reactivate the root user's access key. Remember, the last time we used it we deactivated it as soon as we were done, and we're going to do the same thing here.&lt;/p&gt;

&lt;p&gt;With the access key active again, we can go ahead and &lt;code&gt;cdktf deploy&lt;/code&gt; our stack, and we should see two more resources get created. Once it's done we go back into the console and turn off the access keys again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Group Assignment
&lt;/h2&gt;

&lt;p&gt;With these new permissions we should be all set now to assign our groups those permissions and be on our way, but this is where we hit our first snag. We need to go into the identity center console as root and assign the group to our AWS account along with the admin permission set we just made, which we can do, but there's a problem. Because of how we had to make the groups in identity center, we aren't able to assign users to the group in the console. It has to be scripted. The root user shouldn't do this, though, because managing permissions at this level would mean people would have to be logging into root all the time, which is a big problem. Instead, what I'm going to do instead is assign myself to the AWS account. My user was brought over automatically through the identity provider, and I can set all that up in the console easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Switching Users
&lt;/h3&gt;

&lt;p&gt;With that set up, I now have my user assigned as an admin in the AWS account. Now it's time to configure AWS CLI &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/sso-configure-profile-token.html"&gt;with my sso&lt;/a&gt;. With that done, we're going to make a new stack that uses the admin profile to allocate infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Account Assignment
&lt;/h3&gt;

&lt;p&gt;Now we're ready to assign the group to the AWS account, and add any users we want along the way. Since we'll be doing this as an admin user, we'll need to make a new CDKTF stack, and deploy it using the admin profile after logging in using &lt;code&gt;aws sso login&lt;/code&gt;. In my case, I made an &lt;code&gt;admin&lt;/code&gt; profile and configured the aws cli with it.&lt;/p&gt;

&lt;p&gt;The first thing we'll add to the new stack is going to be the account assignment that adds the group to the AWS account with admin permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SsoadminAccountAssignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-ga-admin`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;principalType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GROUP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;targetType&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_ACCOUNT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;targetId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;permissionSetArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identityCenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;principalId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identityCenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupId&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;At this point the group is set up and ready to use. We can now go ahead and add some users to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;juser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataAwsIdentitystoreUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-iu-joseph`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;identityStoreId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;alternateIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;uniqueAttribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;attributePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UserName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attributeValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;joseph@awsdemo.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IdentitystoreGroupMembership&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-gm-joseph`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;identityStoreId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identityCenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;memberId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;juser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&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;Remember the users are automatically syncronized from our external identity provider, so we won't be making a user. Instead, we just need to grab the one we already have. In my case I found my own account by username. There are other options too, but this one is pretty straightforward. From this point it's just a matter of creating a membership for the user to belong to the group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate Stacks
&lt;/h2&gt;

&lt;p&gt;We started off by building some resources using root and then made a separate stack for things an admin can then do. However, the interesting thing about this approach is that once you have an admin that can log into the system, they also have enough permissions to do everything in the first stack we made. We used the root user to get us just far enough in our IaC build out that our admin users could take over. From this point on, we won't be needing the root user anymore, so we can go ahead and delete the access key it had.&lt;/p&gt;

&lt;p&gt;As always, if you are interested in how everything ties together, you can take a look at the &lt;a href="https://github.com/josephbulger/how-to-cloud"&gt;how to cloud&lt;/a&gt; repository.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>beginners</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>From Scratch: IaC</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Fri, 10 May 2024 01:33:23 +0000</pubDate>
      <link>https://forem.com/aws-builders/from-scratch-iac-2f2m</link>
      <guid>https://forem.com/aws-builders/from-scratch-iac-2f2m</guid>
      <description>&lt;p&gt;It occurred to me recently that it might not be obvious when someone should start using IaC. This can be daunting because there are things you can't do, and some things you shouldn't do with IaC. There are also situations where you could use it, but it doesn't lead to any benefit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sign up for AWS Cloud Account
&lt;/h2&gt;

&lt;p&gt;There will some some things you have to do that are probably common sense you can't leverage IaC for. &lt;a href="https://aws.amazon.com/free"&gt;Signing up for your AWS cloud account&lt;/a&gt; is one of them. In my case I'm completely starting from scratch, so let's go through everythign involved in that. During this series, when we actually use IaC, I will be very explicit about it, so you'll know the difference between something I had to do by hand / manually vs something I scripted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Root Access
&lt;/h2&gt;

&lt;p&gt;So you've signed up for your account, and you have your root account. For the majority of the work you'll be doing, you will &lt;strong&gt;not&lt;/strong&gt; be using your root account. It's dangerous to rely too heavily on your root account, but there are a couple things you have to use it for, especially at the beginning.&lt;/p&gt;

&lt;h3&gt;
  
  
  MFA your Root Account
&lt;/h3&gt;

&lt;p&gt;I've had the privelege of working with some really talented security engineers around cloud, like &lt;a href="https://www.chrisfarris.com/about/"&gt;Chris Farris&lt;/a&gt; for example. When it comes to cloud governance you really can't do wrong by just &lt;a href="https://www.primeharbor.com/blog/multicloud/"&gt;doing what they recommend&lt;/a&gt;. So it should come to no surprise the next few steps I'll be doing are going to essentially be doing that.&lt;/p&gt;

&lt;p&gt;Your Root Account will be the only account that can be logged in outside your identity provider, and the account that can do the most damage if compromised, so it's critical you keep it as secure as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup External Identity Provider
&lt;/h2&gt;

&lt;p&gt;You &lt;strong&gt;really&lt;/strong&gt; need to connect your organization with an identity provider. Before you can do this you have to &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/get-set-up-for-idc.html"&gt;enable Identity Center&lt;/a&gt;. While you go through this process AWS is going to ask you to enable Organizations as well, which you'll want to do. In my case I'm going to be doing this with google workspaces, so I just followed &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/gs-gwp.html"&gt;this documentation&lt;/a&gt; and got up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customize the access portal url
&lt;/h3&gt;

&lt;p&gt;This isn't necessary but it's a nice to have. Once you have your identity provider active, the portal to sign in can be a little obscure, but you can &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/howtochangeURL.html"&gt;change it&lt;/a&gt; to use a subdomain of your liking, assuming it's available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Groups
&lt;/h2&gt;

&lt;p&gt;When working with Google Workspaces, one of the limitations with the IDP integration is that workspace groups don't automatically come over like users do. To make matters even more complicated, you can't add groups using the AWS console, you have to add them through the CLI or the API. That being said, it's worth going through the trouble to make some groups anyway, because they will be useful later when we want to start giving permissions to our teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter IaC
&lt;/h3&gt;

&lt;p&gt;Now, most of the time folks will just make the groups using the CLI and call it a day, but I think this is actually a good starting point to creating your IaC footprint. This does come with some &lt;em&gt;caveats&lt;/em&gt; though, because this isn't going to follow the normal flow of scripting we will get into later after we're done using the root user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scripting against the Root Account
&lt;/h3&gt;

&lt;p&gt;We are going to be creating identity groups using terraform, but in order to do that we need some access keys, and they have to come from root (because we don't have any other users yet). This is something that is usually recommended &lt;strong&gt;never&lt;/strong&gt; to do, and while I largely do agree with this practice, in this situation I argue it's actually a better practice to do it this way than to do it manually. Why? Scripting something makes it repeatable, which is less prone to error. Additionally, there are some safeguards we'll be putting in place so that the root user doesn't get compromised. I can't tell you how many times I've set something up without IaC, and then a month later I forgot what I did, which caused me or my team a &lt;strong&gt;lot&lt;/strong&gt; of headaches. It also creates a less secure situation because you now have to circle back on what you did and rethink everything, which can introduce errors and misteps.&lt;/p&gt;

&lt;p&gt;So we're going to make some access keys, but we're going to immediately &lt;strong&gt;deactivate&lt;/strong&gt; them. Why? Because we haven't written our scripts yet, and we're not going to leave anything to chance when it comes to the root account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;I'm going to be building a solution around this using &lt;a href="https://developer.hashicorp.com/terraform/cdktf"&gt;CDKTF&lt;/a&gt; but you could solve this with other scripting tools as well.&lt;/p&gt;

&lt;p&gt;To remedy this group situation is actually pretty easy and doesn't require a lot of code. We're going to define a construct to hold onto all our org's groups. We'll deploy this once and shouldn't need to revisit this again, which will be a common pattern for anything we script using the root user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IdentityCenterGroups&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AwsProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;org&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataAwsSsoadminInstances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-stores`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;config&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storeId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identityStoreIds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IdentitystoreGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-admins`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;identityStoreId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;storeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-admins`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provider&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;Here I'm just making an admins group, but you would also add others like &lt;code&gt;developers&lt;/code&gt; or &lt;code&gt;security&lt;/code&gt; based on whatever needs you have. This is important because later we can give these groups specific permissions.&lt;/p&gt;

&lt;p&gt;Finally we need to add this construct to our stack and deploy it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RootStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TerraformStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;providerAsRoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AwsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-provider`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IdentityCenterGroups&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-identity-center`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;providerAsRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;org&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;organization&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RootStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;organization&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we can deploy it, we need to go back into our AWS console and activate the access keys for our root account. Now we can deploy this with &lt;code&gt;cdktfy deploy&lt;/code&gt; and we should immediately see the groups show up in Identity Center. After we're verified the group was created, immediately go back to the access keys for root and deactivate them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;We have our AWS account created and connected to our identity provider of choice. That Identity Provider isn't configured to provide access into our AWS account yet, however, because we haven't configured permissions yet. That's what we'll get into next. It's also worth pointing out that, even though we've been doing everything using the root user up until this point, our goal is to do &lt;strong&gt;as little as possible with this user&lt;/strong&gt;, so once we've finished the bare minimum with root, we'll be switching over to an identity user for everything else. We're just not to that point yet.&lt;/p&gt;

&lt;p&gt;Also one last thing for reference because I get asked this a lot: it took me about 3 hours to do everything in this article &lt;strong&gt;and&lt;/strong&gt; write the blog article. If you are wondering how long it takes to do IaC properly, it really doesn't take that long. If you're curious exactly what I built for this set up, you can check it out in my &lt;a href="https://github.com/josephbulger/how-to-cloud"&gt;how to cloud&lt;/a&gt; repository in the aws section.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Cloud: Virtual Machines</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Wed, 08 May 2024 01:08:27 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-cloud-virtual-machines-4h0</link>
      <guid>https://forem.com/aws-builders/how-to-cloud-virtual-machines-4h0</guid>
      <description>&lt;p&gt;Ok at this point we are now ready to get into deploying your app into some kind of compute solution in your cloud provider. In order to make the best decisions when it comes to choosing said compute solution, we need to start with the foundations of almost all of them: virtual machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cornerstone of Compute
&lt;/h2&gt;

&lt;p&gt;Virtual machines will make up the cornerstone of all compute solutions you will build on top of, whether you have control over them or not. In fact, that’s one of the decision criteria you should consider when you get to choosing one, but that’s for another post. For now, what’s important is understanding the ins and outs of how your cloud provider allocates and operates their VMs, and how those aspects of your cloud provider effect the higher level compute solutions they all offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bare Metal
&lt;/h2&gt;

&lt;p&gt;Virtual Machines (VMs) are probably the closest to the metal kind of solution that you can leverage with a cloud provider. AWS calls this offering AWS EC2, Azure just calls them Azure Virtual Machines, and GCP calls them Compute Engine. They all end up doing the same thing in the end. You request for a certain configuration of machine, and after some time (usually a few minutes), you can have a machine spun up that closely mimics an on prem hypervisor. You can almost, for all intents and purposes, do the same thing to this machine that you would do with one mounted in a data center. That means you have the highest degree of control on this machine that you can possibly have in your cloud provider. You can mount disks to them for storage, you can configure them with GPU, SSD state storage, basically set up any kind of compute or data storage solution you want.&lt;/p&gt;

&lt;p&gt;This also means, however, that you have the least amount of operational efficiency by leveraging a virtual machine. You have to keep the OS up to date from a security perspective. You have to maintain the overall health and state of the machine. You have to connect that virtual machine to all the other offerings of your cloud provider to create an overall solution. There is a lot of extra work you have to put in to make that virtual machine do what you want it to do. The cloud provider is really only giving you one thing you didn’t have before, and that is rapid availability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Model
&lt;/h2&gt;

&lt;p&gt;This is where things might get a little tricky. On paper, VMs look to be the cheapest of all compute solutions. However, if you are using VMs as the only element in your compute solution, you are going to have significant labor costs. In fact, if you don’t add other aspects to your compute solution, like serverless functions, or some type of orchestration plane, you will end up operating the most expensive solution you could have built once you consider the total all in cost. VMs are still crucial to understand, however, because they feed into all the other compute solutions you can use. This will ultimately boil down to trades off between labor costs or operational efficiency, and operational flexibility or control. We can get into those specifics more in other posts when we talk about the specific solutions that are offered and how they effect those trade offs.&lt;/p&gt;

&lt;p&gt;To keep things simple for now, let’s just do a basic run down of the cost structure for VMs by themselves. It will serve as a fraction of the overall cost to build your solution, and in a lot of ways it makes up the foundation of your cost model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;h4&gt;
  
  
  AWS
&lt;/h4&gt;

&lt;p&gt;Amazon offers a variety of ways to rent their VMs. The most straight forward (and expensive) one is just using their On Demand allocation. Prices vary depending on your configuration of CPU, Memory, GPU support, storage, etc, and I won’t be going into the details of how they effect cost, but it’s important to note that On Demand pricing effectively influences all other pricing.&lt;/p&gt;

&lt;p&gt;There is also Reserved pricing, which allows you to actually reserve a certain level of allocation in 1 or 3 year agreements. That may sound confusing, but it’s important to note in this pricing model that you aren’t renting a specific VM for a specified time. It’s more like you are renting the time for that configuration, and that agreement nets you some cost savings. Having said that, there are some limitations. There are two ways to purchase a Reserved instance, Standard and Convertible. Standard rental allows you to modify certain aspects of the reservation like availability zone, networking type, instance size, but you can’t change other things such as the instance type. You can, however, sell that rental license on the AWS marketplace. Then there is convertible. Convertible agreements allow you to exchange one instance for another. You can do this as many times as you like but the value of the overall exchange has to be equal or greater than the agreement you put in place to begin with. Convertible rentals can not be sold on the AWS marketplace. If you choose either of these styles of pricing, you can get upwards of a 75% reduction in price from AWS for the equivalent machine with on demand pricing.&lt;/p&gt;

&lt;p&gt;Finally we have Spot pricing. Spot pricing doesn’t require a termed agreement like reserved pricing, but it can still afford you similar savings. It achieves this through one major stipulation: AWS may choose at any time to terminate your instance with only a two minute warning. This can sound devastating if your cloud solution relies solely on VMs, but, as we will discuss in later posts, layering an orchestration platform on top of your rented VMs can make this an extremely appealing option. With this type of set up you can achieve savings up to 90% of what you would have spent on an on demand instance.&lt;/p&gt;

&lt;h4&gt;
  
  
  GCP
&lt;/h4&gt;

&lt;p&gt;You’re going to start to see a lot of repetition amongst cloud providers. What GCP offers is extremely similar to AWS, and largely the only differences are in names.&lt;/p&gt;

&lt;p&gt;GCP’s main offering is largely referred as on demand or “pay as you go” pricing. It’s akin to AWS’ on demand pricing, and the structure of offering has a lot of similarities. From there, GCP offers “commitment” pricing that is similar in principle to AWS’ reserved pricing. In addition to this offering, GCP will also give discounts for what they call “sustained use” discounts (or SUDs). SUDs can give you a 30% discount if you haven’t received some other form of discount and your compute has been running for at least 25% of the billable month. GCP also offers Spot or Preemptible VMs, and as you’ve probably guessed they function in a very similar way to what AWS Spot instances offer. With GCP spot instances you can achieve savings upwards of 90%.&lt;/p&gt;

&lt;h4&gt;
  
  
  Azure
&lt;/h4&gt;

&lt;p&gt;We’re going to do the big three and then we’re going to stop. I wanted to get all three laid out so you can see the pattern. This will effect your decision process later when choosing your solutions, and it’s important to understand that all cloud providers should (most do) offer you the same cost savings models.&lt;/p&gt;

&lt;p&gt;Azure’s VMs can be rented under a Pay as you Go model, a Savings Plan, Reserved Instances, and finally Spot models. Pay as you Go is the same thing as On Demand. Reserved is the same as before. Spot as well. Savings Plans are worth pointing out. They allow you to lock in an agreed to hourly price for a variety of services Azure offers for a period ranging from 1 to 3 years. In order to get those reduced prices, however, you have to agree to a certain level of consumption during that time period.&lt;/p&gt;

&lt;h4&gt;
  
  
  Get Containerized
&lt;/h4&gt;

&lt;p&gt;Developing the right compute solution is a series of related decisions. It starts with understanding how to choose the right VM structure. We’ve already talked about &lt;a href="https://dev.to/aws-builders/how-to-cloud-containerization-1k56"&gt;containerization&lt;/a&gt;, so the next thing we’ll go over will be how to tie together containerized applications with the foundation of virtual machines we just went over.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>aws</category>
    </item>
    <item>
      <title>How to Cloud: Containerization</title>
      <dc:creator>Joseph</dc:creator>
      <pubDate>Tue, 19 Mar 2024 18:11:10 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-cloud-containerization-1k56</link>
      <guid>https://forem.com/aws-builders/how-to-cloud-containerization-1k56</guid>
      <description>&lt;p&gt;I have kind of a standard approach on building out a cloud presence that I’ve developed over the last decade or so. I’ve used it extensively with multiple teams to create a highly mature DevOps culture within groups again and again. Today I’m beginning a series that explains some of the basics. Hopefully it can be of some use to those trying to understand good ways to leverage cloud technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Any Cloud Will Work
&lt;/h2&gt;

&lt;p&gt;I’m going to start this series off with examples of how to deploy the concepts I’m talking about in AWS, but the approach I use works with a lot of cloud solutions out there, including Azure and GCP. I will do my best to call out how the approach would work in the Big 3 where it’s appropriate, but the topic here is not about showing you a detailed solution of a tech stack, but rather the strategy behind the build out and how to make the best decisions when it comes to rolling things out. And speaking of decisions, when it comes to adopting a cloud approach, the very first thing your teams need to understand is containerization. The need to learn Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn Docker
&lt;/h2&gt;

&lt;p&gt;Docker is the gateway to understanding what containerization is all about. Not only is it a containerization solution itself, but the concepts your team learns from understanding it will help them navigate the massive forest of different compute options they will have across any cloud vendor. Without starting here you will never be able to see the forest through all the trees being thrown at you. Options range from AWS ECS, to Fargate, or EKS, or Lambdas, and that’s just one cloud vendor, so trust me and start here. I’m not going to get into the specifics of all things Docker, but I do want to highlight some of the learnings I’ve made over the years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Organize your images
&lt;/h3&gt;

&lt;p&gt;When you are just beginning to learn what containers are this won’t make a lot of sense, but after you have your first image working and you actually have a system working inside a container, you should stop and think about organizing your image builds. What this means is not only keeping them logically organized so your team can understand how to use them efficiently, but also layering them together so you can reduce your build times significantly where you have a lot of commonality across systems. Let me show you a simple example.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Starting Point
&lt;/h4&gt;

&lt;p&gt;Once the team knows how to build docker images and can reliably run them, I start introducing a pattern that allows them to create layered images that use each other instead of creating separate duplicate builds that make your CI/CD process take forever to finish. This begins by identifying your base starting point and branching off from there. Let’s say your team needs to build a web app and a background service, but they plan to use nodejs for both. Start by making a docker compose yml that will simply define the common base both of them will use. For example:&lt;/p&gt;

&lt;h5&gt;
  
  
  docker-compose.yml
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;how-to-cloud-base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;how-to-cloud:base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Dockerfile
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The base image right now is mind numbingly simple. The docker compose yml just builds the Dockerfile below and then tags the image with a base tag. The Dockerfile doesn’t really do anything other than grab the alpine distro of node. That’s how it starts. Later your team will realize there are commonalities that both systems share and they can “bubble up” those things into the base image when they realize it. Now that you have the base the team can use it to create the web app and the background service.&lt;/p&gt;

&lt;p&gt;Let’s build out the web version first…&lt;/p&gt;

&lt;h5&gt;
  
  
  docker-compose.web.yml
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;how-to-cloud-web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docker/Dockerfile.web&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;how-to-cloud-web:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Dockerfile.web
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; how-to-cloud:base&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./web /app&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; npm start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now the background service, which for now will look very similar to the web…&lt;/p&gt;

&lt;h6&gt;
  
  
  docker-compose.background.yml
&lt;/h6&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;how-to-cloud-background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docker/Dockerfile.background&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;how-to-cloud-background:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Dockerfile.background
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; how-to-cloud:base&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./background /app&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; npm start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Clarifying Points
&lt;/h4&gt;

&lt;p&gt;One thing that’s worth pointing out here is that the web app and the background service function completely independently. Meaning one system does not in any way depend on the other from a deployment perspective. If they did you there are other ways to deal with that.&lt;/p&gt;

&lt;p&gt;Another call out I want to make is that when I use this organizational method I really like to make an LTS build that has kind of a default set of what my team normally uses in the cases where they just wanna use “the normal” build and not have to have something specialized. It looks something like this.&lt;/p&gt;

&lt;h5&gt;
  
  
  docker-compose.lts.yml
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;how-to-cloud-lts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docker/Dockerfile.lts&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;how-to-cloud:lts&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Dockerfile.lts
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; how-to-cloud:base&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./lts /app&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; npm start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;So with all this set up we can talk about how this all ties together in a CI/CD pipeline and why you can get some pretty significant performance gains for your build when you follow this approach. The process is separated into three main stages: build, deploy, clean. It looks something like this.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# build image&lt;/span&gt;
docker compose build
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.web.yml build
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.background.yml build
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.lts.yml build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing here is that you build the base docker image first, and then build the images that depend on that base image. You can get much more elaborate with the layering of the images if you want to, but the main point here is that the more commonalities that you bring up into higher layers the faster you’ll make your build, and the easier it will be to maintain your systems across all your teams because they’ll be sharing and collaborating together instead of duplicating the same concepts in silos.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deploy
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# login to ECR&lt;/span&gt;
&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws &lt;span class="nt"&gt;--version&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[/.]'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$version&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;login&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecr get-login &lt;span class="nt"&gt;--no-include-email&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;aws ecr get-login-password | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="nv"&gt;$ecr&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# push image to ECR repo&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.lts.yml push
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.web.yml push
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.background.yml push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the images are built you can deploy them to your container registry. In AWS it looks basically like this. The AWS CLI has two versions, and both of them are pretty widely used from what I’ve seen, so I still have a check for which version the team has installed so it uses the right command, but if you know your team is going to only use version 2, you can clean that up a little bit and only use the second command to log into the ECR.&lt;/p&gt;

&lt;p&gt;Aside from that once you have logged into your ECR it’s a simple matter of pushing all the images up. You don’t need to publish the base image because you should be building that every time your CI/CD pipeline runs, unless you have a really good reason to need some kind of available redundancy for it. From my experience that is usually over kill.&lt;/p&gt;

&lt;p&gt;Also, in the example above we’re deploying to AWS ECR, but every cloud vendor has their own container registry. The point here is how you design the process, not the specifics of how you get the image into the registry. The process works for most if not all cloud vendors.&lt;/p&gt;

&lt;h4&gt;
  
  
  Clean
&lt;/h4&gt;

&lt;p&gt;At the end of your build process you might need to do some cleaning up, so we have a third stage to handle that, but a lot of the time it’s empty because the CI/CD process is ephemeral anyway. However, if you are using external dependencies during your build and you need to clean them up after the fact, then you can do that here, or if your CI/CD process is not ephemeral then you’ll probably want to do some clean up before you call the build done.&lt;/p&gt;

&lt;p&gt;In this set up our clean up process is empty because there’s nothing really to clean, but I’ve used this in the past under certain situations. For example, we’ve built github self hosted runner farms that required us to pre-emptively download and extract binaries because the docker build was actually failing to sign certificates, so the host had binaries we needed to clean up after we were finished with the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the Code
&lt;/h2&gt;

&lt;p&gt;I went ahead and made a repository to showcase all the lessons I talked about here, as well as any future topics surrounding How to Cloud. If you want to see the full set up I described here head over to &lt;a href="https://github.com/josephbulger/how-to-cloud"&gt;https://github.com/josephbulger/how-to-cloud&lt;/a&gt; &lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>docker</category>
      <category>containers</category>
    </item>
  </channel>
</rss>
