<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: IaSQL</title>
    <description>The latest articles on DEV Community by IaSQL (@iasql).</description>
    <link>https://dev.to/iasql</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F6611%2F6a5ff3ff-595f-4c11-8c2f-15dc382e43ff.png</url>
      <title>DEV Community: IaSQL</title>
      <link>https://dev.to/iasql</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iasql"/>
    <language>en</language>
    <item>
      <title>Why SQL is right for Infrastructure Management</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Thu, 06 Apr 2023 21:15:23 +0000</pubDate>
      <link>https://dev.to/iasql/why-sql-is-right-for-infrastructure-management-3gge</link>
      <guid>https://dev.to/iasql/why-sql-is-right-for-infrastructure-management-3gge</guid>
      <description>&lt;p&gt;Infrastructure is the heart of your company. Without it, nothing can actually be done and there'd be no reason for customers to pay you. For software companies that infrastructure is the software that its engineers directly write, and usually the cloud infrastructure and services it runs on top of and integrates with.&lt;/p&gt;

&lt;p&gt;In this post, we will geek out on various software abstractions and data structures, tools used by professionals of various sorts, and dig into the pros and cons of these with respect to cloud infrastructure management in particular. We'll see that while SQL has its own warts, it is the "least worst" of all of the options out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Following these instructions is imperative
&lt;/h2&gt;

&lt;p&gt;The simplest way to define how to set something up is to write up a list of instructions to follow, in order, to build whatever infrastructure you're dealing with, whether its the instructions on how to build a restaurant or an AWS Fargate cluster. This list of steps to process (with a LISt Processor, or &lt;a href="https://en.wikipedia.org/wiki/Lisp_%28programming_language%29" rel="noopener noreferrer"&gt;LISP&lt;/a&gt;, if you will 😉), which can include instructions to repeat or skip over steps based on conditions you have run into, is called &lt;a href="https://en.wikipedia.org/wiki/Imperative_programming" rel="noopener noreferrer"&gt;imperative programming&lt;/a&gt; in the software world.&lt;/p&gt;

&lt;h3&gt;
  
  
  Imperative Restaurant Instructions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fyau2bf2nfsdlioiy24rm.png" class="article-body-image-wrapper"&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%2Farticles%2Fyau2bf2nfsdlioiy24rm.png" alt="Man in hard-hat builds your restaurant" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Design restaurant.&lt;/li&gt;
&lt;li&gt;Flatten the ground.&lt;/li&gt;
&lt;li&gt;Prop up pieces of wood next to each other.&lt;/li&gt;
&lt;li&gt;Bang nails into wood.&lt;/li&gt;
&lt;li&gt;Is restaurant finished? No, go to 3.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For your cloud infrastructure, this is similar to using the &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/" rel="noopener noreferrer"&gt;AWS SDK&lt;/a&gt; and directly calling the various methods, checking their results and eventually arriving at the desired infrastructure. It is &lt;em&gt;also&lt;/em&gt; like following a step-by-step guide clicking through the various AWS console UI elements to set up your infrastructure, like in &lt;a href="https://towardsdatascience.com/deploy-your-python-app-with-aws-fargate-tutorial-7a48535da586" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;. The latter may not be automated, to you, but to your company executives it is as they don't care how the infrastructure building was accomplished as long as it was done quickly, cheaply, and reliably, and you know they always want all three. 😉&lt;/p&gt;

&lt;p&gt;Imperative infrastructure management works &lt;em&gt;well&lt;/em&gt; for initial setup of new infrastructure. There's nothing there to interfere with it, so you can just write the "happy path" and get it working. And if that fails, you can just tear it all down and start all over again if that's cheap, which it is for cloud infrastructure (though not for building a restaurant), so the &lt;a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity" rel="noopener noreferrer"&gt;cyclomatic complexity&lt;/a&gt; of the code you write is low and everyone can follow along with it. For example, one can use the &lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;&lt;code&gt;aws&lt;/code&gt; cli application&lt;/a&gt; inside of a &lt;a href="https://www.gnu.org/software/bash/" rel="noopener noreferrer"&gt;&lt;code&gt;bash&lt;/code&gt; script&lt;/a&gt; to create a new &lt;a href="https://en.wikipedia.org/wiki/Ssh-keygen" rel="noopener noreferrer"&gt;ssh keypair&lt;/a&gt; in AWS, a &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/security-groups.html" rel="noopener noreferrer"&gt;security group&lt;/a&gt; enabling ssh access, a new EC2 instance associated with both, and then access that instance (to demonstrate that it works).&lt;br&gt;
&lt;/p&gt;

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

aws ec2 create-key-pair &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-name&lt;/span&gt; login &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-type&lt;/span&gt; ed25519 | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="nt"&gt;-r&lt;/span&gt; .KeyMaterial &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; login.pem
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 login.pem
&lt;span class="nv"&gt;SG_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 create-security-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; login-sg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Login security group"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-id&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-vpcs &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 | &lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Vpcs[] | select(.IsDefault = true) | .VpcId'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="nt"&gt;-r&lt;/span&gt; .GroupId&lt;span class="si"&gt;)&lt;/span&gt;
aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; login-sg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ip-permissions&lt;/span&gt; &lt;span class="s1"&gt;'IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=0.0.0.0/0}]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nv"&gt;INSTANCE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 run-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-id&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-subnets &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 | &lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .Subnets[0].SubnetId&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-type&lt;/span&gt; t3.small &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image-id&lt;/span&gt; resolve:ssm:/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--security-group-ids&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SG_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-name&lt;/span&gt; login &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--associate-public-ip-address&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag-specifications&lt;/span&gt; &lt;span class="s1"&gt;'ResourceType=instance,Tags=[{Key=name,Value=login-inst}]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="nt"&gt;-r&lt;/span&gt; .Instances[0].InstanceId&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;EC2 instance &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INSTANCE_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; starting 
aws ec2 &lt;span class="nb"&gt;wait &lt;/span&gt;instance-status-ok &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-ids&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INSTANCE_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;PUBLIC_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tag:name,Values&lt;span class="o"&gt;=[&lt;/span&gt;login-inst] &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;instance-state-name,Values&lt;span class="o"&gt;=[&lt;/span&gt;running] | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="nt"&gt;-r&lt;/span&gt; .Reservations[0].Instances[0].PublicIpAddress&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;Server accessible at &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PUBLIC_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; login.pem &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"StrictHostKeyChecking no"&lt;/span&gt; ubuntu@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PUBLIC_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind that this is a &lt;em&gt;simple&lt;/em&gt; script that does not handle failure of any of the commands called, creating one EC2 instance with two supporting elements (a security group to allow access to the SSH port and an SSH keypair to log into the instance). If you intend to actually use it multiple times, several of the arguments currently hardwired should be turned into shell arguments themselves, and all of the error paths should be tackled.&lt;/p&gt;

&lt;p&gt;However, the vast majority of the time you are not creating clones of the same infrastructure over and over again, instead you need to make changes to your existing infrastructure, and that original code is more than likely useless to you. To get from the &lt;em&gt;current&lt;/em&gt; state of your infrastructure to your &lt;em&gt;desired&lt;/em&gt; state, you need to call different APIs than you did before, and its much riskier to make a mistake because this infrastructure is already in use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declare your intentions at once
&lt;/h2&gt;

&lt;p&gt;When the desired state is relatively simple to define and the mechanism to reach that state is not that important, writing up a declaration of what is needed and letting something/someone else deal with it is the most logical abstraction. This would be like drafting up the architectural draft for your new restaurant and paying a contracting company to actually build it, or &lt;a href="https://en.wikipedia.org/wiki/Declarative_programming#Domain-specific_languages" rel="noopener noreferrer"&gt;writing HTML and letting a web browser render it&lt;/a&gt;, or writing a &lt;a href="https://github.com/hashicorp/hcl" rel="noopener noreferrer"&gt;Terraform HCL&lt;/a&gt; file and letting the Terraform CLI tool &lt;code&gt;apply&lt;/code&gt; it. This is called &lt;a href="https://en.wikipedia.org/wiki/Declarative_programming" rel="noopener noreferrer"&gt;declarative programming&lt;/a&gt; in the software world, and has many advantages (and a few disadvantages!) for cloud infrastructure management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declarative Restaurant Instructions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fryegl9t2j9h2m7xtvq1x.png" class="article-body-image-wrapper"&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%2Farticles%2Fryegl9t2j9h2m7xtvq1x.png" alt="King in robes on throne" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By decree of the king, a restaurant shall be built!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In declarative programming you have some initial state and in comparatively dense and high-level declarative code define the desired state. In many use-cases (like web browsers and sometimes for restaurant-building contractors) the initial state is "blank" and the engine that transforms the declarative code into imperative operations can often be a relatively straightforward &lt;a href="https://en.wikipedia.org/wiki/Interpreter_%28computing%29" rel="noopener noreferrer"&gt;interpreter&lt;/a&gt; walking the &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" rel="noopener noreferrer"&gt;AST&lt;/a&gt; of the declarative code.&lt;/p&gt;

&lt;p&gt;Infrastructure management &lt;em&gt;generally&lt;/em&gt; is not that straightforward. Changes to infrastructure require in-place mutations of the current state or a full &lt;a href="https://en.wikipedia.org/wiki/Blue-green_deployment" rel="noopener noreferrer"&gt;blue/green deployment&lt;/a&gt; of the entirety of your production infrastructure is very expensive, requiring all resource costs to be doubled, and deployments to require some amount of downtime for users to swap over and state to be resynchronized. Particularly for databases this approach is essentially impossible as there is too much state to swap, which is what makes database migrations tricky when dropping data.&lt;/p&gt;

&lt;p&gt;So declarative programming for infrastructure, also known as &lt;a href="https://en.wikipedia.org/wiki/Infrastructure_as_code" rel="noopener noreferrer"&gt;Infrastructure as Code&lt;/a&gt;, must take the existing state and generate the imperative operations to perform based on the differences between the existing state and the declared desired state. This is like a contracting company being given an architecture draft for a new restaurant and being told to remodel an existing commercial space (maybe it was also a restaurant, maybe it was a clothing store, etc) and the contracting company figuring out what needs to be torn down first, what can be re-used, and what needs to be built new.&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"4.60.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"external"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOT&lt;/span&gt;&lt;span class="sh"&gt;
      if [ ! -f ./login.pem ]; then
        # Terraform doesn't support creating the PEM file via AWS. It has to be
        # created locally and then imported into AWS. Inside of a conditional so
        # we don't accidentally re-create the file between 'plan' and 'apply'
        yes '' | ssh-keygen -t ed25519 -m PEM -f login.pem
      fi
      chmod 600 login.pem
      # The output of this script needs to be JSON formatted. This is a bit wonky
      # but building it over time via 'jq' operators is actually harder to follow
      echo {
      echo   '"access_key_id": "'$(aws configure get aws_access_key_id)'",'
      echo   '"secret_access_key": "'$(aws configure get aws_secret_access_key)'",'
      echo   '"public_key": "'$(cat login.pem.pub)'"'
      echo }
&lt;/span&gt;&lt;span class="no"&gt;    EOT
&lt;/span&gt;  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
  &lt;span class="nx"&gt;access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"access_key_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;secret_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"secret_access_key"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_key_pair"&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt;
  &lt;span class="nx"&gt;public_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"public_key"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"login_sg"&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;"login-sg"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Login security group"&lt;/span&gt;
  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"resolve:ssm:/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.small"&lt;/span&gt;
  &lt;span class="nx"&gt;associate_public_ip_address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_key_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key_name&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login_sg&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="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"login-inst"&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;This example still requires the following lines from the imperative example to get the public IP address of the new EC2 instance and log into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PUBLIC_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tag:name,Values&lt;span class="o"&gt;=[&lt;/span&gt;login-inst] &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;instance-state-name,Values&lt;span class="o"&gt;=[&lt;/span&gt;running] | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="nt"&gt;-r&lt;/span&gt; .Reservations[0].Instances[0].PublicIpAddress&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;Server accessible at &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PUBLIC_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; login.pem &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"StrictHostKeyChecking no"&lt;/span&gt; ubuntu@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PUBLIC_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This diffing of original and desired state can be resolved several ways. The contracting company could spend a lot of time inspecting the existing commercial space to find absolutely every piece that can be re-used, removing them and setting them aside with the materials that need to be purchased, then rework the interior walls, and build everything from scratch, or it could decide to tear everything out back into an empty space and rebuild from scratch (like what your web browser does between web pages), or perhaps you need to keep the space mostly usable for customers, minimizing disruption to the current operations while things are changed. This last case is what infrastructure management tools need to tackle your infrastructure with the least amount of downtime or no downtime at all, if you're &lt;em&gt;careful&lt;/em&gt; with it.&lt;/p&gt;

&lt;p&gt;How can you "careful" with declarative programming tools, if you don't have direct control over what they do? Terraform does this with a &lt;a href="https://developer.hashicorp.com/terraform/cli/commands/plan" rel="noopener noreferrer"&gt;plan&lt;/a&gt; command, which performs the diffing of current and desired state and reports back to you the operations it expects to execute to do so. Terraform will tell you when a change it intends to make is going to provision new resources, alter existing resources, drop resources, and &lt;em&gt;replace&lt;/em&gt; resources. That last one is particularly annoying when it shows up because it means the change you want to perform can only be done by dropping it and recreating it with the new configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform plan

data.external.example: Reading...
data.external.example: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  &lt;span class="c"&gt;# aws_instance.login will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + ami                                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"resolve:ssm:/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id"&lt;/span&gt;
      + arn                                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + associate_public_ip_address          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + availability_zone                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + cpu_core_count                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + cpu_threads_per_core                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + disable_api_stop                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + disable_api_termination              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + ebs_optimized                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + get_password_data                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + host_id                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + host_resource_group_arn              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + iam_instance_profile                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + instance_initiated_shutdown_behavior &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + instance_state                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + instance_type                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.small"&lt;/span&gt;
      + ipv6_address_count                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + ipv6_addresses                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + key_name                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt;
      + monitoring                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + outpost_arn                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + password_data                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + placement_group                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + placement_partition_number           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + primary_network_interface_id         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + private_dns                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + private_ip                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + public_dns                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + public_ip                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + secondary_private_ips                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + security_groups                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + source_dest_check                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + subnet_id                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags                                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          + &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"login-inst"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + tags_all                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          + &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"login-inst"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + tenancy                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + user_data                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + user_data_base64                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + user_data_replace_on_change          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + vpc_security_group_ids               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# aws_key_pair.login will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_key_pair"&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + fingerprint     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + key_name        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt;
      + key_name_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + key_pair_id     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + key_type        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + public_key      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF4yiy4lef2WFQ7zdtuOZUHz9onyADTJ16l0OX8a211y damocles@zelbinion"&lt;/span&gt;
      + tags_all        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# aws_security_group.login_sg will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"login_sg"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + description            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Login security group"&lt;/span&gt;
      + egress                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + ingress                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
          + &lt;span class="o"&gt;{&lt;/span&gt;
              + cidr_blocks      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + description      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
              + from_port        &lt;span class="o"&gt;=&lt;/span&gt; 22
              + ipv6_cidr_blocks &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
              + prefix_list_ids  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
              + protocol         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
              + security_groups  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
              + self             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
              + to_port          &lt;span class="o"&gt;=&lt;/span&gt; 22
            &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;]&lt;/span&gt;
      + name                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"login-sg"&lt;/span&gt;
      + name_prefix            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + owner_id               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + revoke_rules_on_delete &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + tags_all               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + vpc_id                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

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

─────────────────────────────────────────────────────────────────────────────

Note: You didn&lt;span class="s1"&gt;'t use the -out option to save this plan, so Terraform can'&lt;/span&gt;t
guarantee to take exactly these actions &lt;span class="k"&gt;if &lt;/span&gt;you run &lt;span class="s2"&gt;"terraform apply"&lt;/span&gt; now.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you see steps where Terraform plans to drop or replace resources, you can decide if that's actually alright, or if it will negatively impact your business during the deployment, and then you can abort and define an intermediate state to first transition to, usually creating a new resource without dropping the old one, then do whatever operation is necessary to move internal state to it, then continue by deleting the old resource. This is like checking up on your contracting company before they start doing their work to make sure they don't do anything strange you don't want, and sometimes needing to handhold them through details of your business to come up with the new plan of action.&lt;/p&gt;

&lt;p&gt;Until cloud resource management is as quick to execute as a webpage render where state changes can be done faster than a human can respond, this sort of hand-holding is necessary for live infrastructure with uptime guarantees. Declarative programming, at least for cloud infrastructure management, is a &lt;a href="https://en.wikipedia.org/wiki/Leaky_abstraction" rel="noopener noreferrer"&gt;leaky abstraction&lt;/a&gt; over the imperative operations that need to be executed.&lt;/p&gt;

&lt;p&gt;The Terraform HCL files don't have the prior state encoded into them, nor does it depend on being stored within a git repository to provide that prior state, so how does Terraform get that initial state to operate on? Terraform maintains an &lt;a href="https://developer.hashicorp.com/terraform/language/state" rel="noopener noreferrer"&gt;internal statefile&lt;/a&gt; to perform the diffing against. It has a mechanism to &lt;a href="https://developer.hashicorp.com/terraform/cli/commands/refresh" rel="noopener noreferrer"&gt;refresh that state&lt;/a&gt;, but being a JSON blob with poor typing, can &lt;a href="https://faun.pub/cleaning-up-a-terraform-state-file-the-right-way-ab509f6e47f3" rel="noopener noreferrer"&gt;become corrupted, requiring manual intervention&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why does Terraform need this JSON-based statefile when it has the much cleaner HCL format that you use as a developer? This is because HCL, as a language that provides affordances to the developer's own intuition on how to structure their representation of their infrastructure, does not have a &lt;a href="https://en.wikipedia.org/wiki/Canonical_form" rel="noopener noreferrer"&gt;canonical form&lt;/a&gt; to make these comparisons with. Terraform must first pre-process the HCL into an internal data structure which it can then generate a &lt;a href="https://en.wikipedia.org/wiki/Diff" rel="noopener noreferrer"&gt;diff&lt;/a&gt; that can then be used to determine the operations to perform. The JSON-based statefile is likely a very close representation of this internal data structure, and manual editing of it is highly discouraged because the details of it likely vary from release to release with an internal migration mechanism performed on upgrade of the Terraform CLI.&lt;/p&gt;

&lt;p&gt;Needing to learn Terraform's HCL language to make infrastructure changes has been cited as a &lt;a href="https://www.pulumi.com/why-pulumi/" rel="noopener noreferrer"&gt;weakness&lt;/a&gt; that can be mitigated by wrapping it in the syntaxes more familiar to the user like &lt;a href="https://developer.hashicorp.com/terraform/cdktf" rel="noopener noreferrer"&gt;Terraform CDK&lt;/a&gt;, &lt;a href="https://www.pulumi.com" rel="noopener noreferrer"&gt;Pulumi&lt;/a&gt;, or &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt;. This &lt;em&gt;is&lt;/em&gt; a weakness, but we believe that the HCL-to-Statefile transformation being &lt;a href="https://en.wikipedia.org/wiki/Surjective_function" rel="noopener noreferrer"&gt;surjective&lt;/a&gt; instead of &lt;a href="https://en.wikipedia.org/wiki/Bijection" rel="noopener noreferrer"&gt;bijective&lt;/a&gt; is actually the bigger issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surjective Function &lt;em&gt;f&lt;/em&gt; Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F6%2F6c%2FSurjection.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F6%2F6c%2FSurjection.svg" alt="Surjective Diagram" width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Surjective_function#/media/File:Surjection.svg" rel="noopener noreferrer"&gt;From Wikipedia:&lt;/a&gt; A function &lt;em&gt;f&lt;/em&gt; is surjective if all of its inputs &lt;em&gt;X&lt;/em&gt; map to all outputs &lt;em&gt;Y&lt;/em&gt;, but overlaps can occur.&lt;/p&gt;

&lt;p&gt;This is like multiple Terraform HCL representations all mapping to the exact same cloud infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Objective is Bi- hmmmm....
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bijective Function &lt;em&gt;f&lt;/em&gt; Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Fa5%2FBijection.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Fa5%2FBijection.svg" alt="Bijective Diagram" width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Bijection#/media/File:Bijection.svg" rel="noopener noreferrer"&gt;From Wikipedia:&lt;/a&gt; A function &lt;em&gt;f&lt;/em&gt; is bijective if all of its inputs &lt;em&gt;X&lt;/em&gt; map to all outputs &lt;em&gt;Y&lt;/em&gt; exactly once.&lt;/p&gt;

&lt;p&gt;A bijective function has a one-to-one mapping of all state in one set with all state in another set. Both representations are equivalent (given the appropriate transform functions), both sets (of possible cloud states) are the same size (conceptually), and for any one particular state in one set there is exactly one state in the other set. Programming Languages don't fit the bill here, as was demonstrated earlier, but what does? What, exactly, defines your cloud resources in the most minimal way?&lt;/p&gt;

&lt;p&gt;The best answer in most cases is &lt;del&gt;&lt;a href="https://en.wikipedia.org/wiki/Cloud" rel="noopener noreferrer"&gt;water vapor&lt;/a&gt;&lt;/del&gt; &lt;a href="https://en.wikipedia.org/wiki/Entity#In_computer_science" rel="noopener noreferrer"&gt;Entities&lt;/a&gt;. Each cloud resource has some unique identifier and a set of properties that configure it. Some of those properties can be relations or dependencies on other cloud resources. The ordering of the entities does not matter, so long as the dependencies are explicitly defined to make determination of the order of operations possible. You may have noticed that this sounds a lot like &lt;a href="https://en.wikipedia.org/wiki/Object_%28computer_science%29" rel="noopener noreferrer"&gt;Objects&lt;/a&gt; in &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming" rel="noopener noreferrer"&gt;Object-Oriented Programming&lt;/a&gt;. It is very much the same, with the only difference being that the unique identifier &lt;em&gt;must&lt;/em&gt; be unique, so if we considered an array of pointers to these objects as the entity IDs, multiple pointers to the same object wouldn't be allowed.&lt;/p&gt;

&lt;p&gt;This means the representation of your cloud resources should be &lt;em&gt;data&lt;/em&gt;, not code. Particularly, it should be &lt;a href="https://en.wikipedia.org/wiki/Relational_model" rel="noopener noreferrer"&gt;relational data&lt;/a&gt; to allow the references/relations with other entities that actually exist within your cloud. Modeling each entity as a wholly-siloed object without those relations explicitly tracked &lt;em&gt;might&lt;/em&gt; work for read-only cases, but puts the burden of properly joining the entities on the user and could lead to unexpected errors during mutation if the disjointed state is not carefully kept in sync. This mutation of that data would be the code, and fits very well into a classic &lt;a href="https://www.redhat.com/en/blog/rest-architecture" rel="noopener noreferrer"&gt;REST architecture&lt;/a&gt; (in the original sense, though it could also work in the &lt;a href="https://en.wikipedia.org/wiki/Overview_of_RESTful_API_Description_Languages#Hypertext-driven_API" rel="noopener noreferrer"&gt;Web-oriented RESTful meaning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since your infrastructure is actually data, just about any data format &lt;em&gt;could&lt;/em&gt; be used to store it, as demonstrated by Terraform's usage of JSON under-the-hood. Some will require more application-level guarantees on top of the data format to achieve all of the necessary components, however. For instance JSON does not have any native way to represent &lt;em&gt;references&lt;/em&gt; to other JSON objects that are not wholly contained within the object in question, so you would need to define a way to represent them and enforce it within your application. &lt;a href="https://yaml.org/spec/1.2.2/#24-tags" rel="noopener noreferrer"&gt;YAML does support them via tags&lt;/a&gt; but being a markup language has multiple equivalent representations and is therefore not suitable for this purpose (without a strict linter to reduce it back down to a canonical form). There are also &lt;a href="https://capnproto.org/language.html#dynamically-typed-fields" rel="noopener noreferrer"&gt;binary formats that can encode references&lt;/a&gt; with &lt;a href="https://www.sqlite.org/cli.html" rel="noopener noreferrer"&gt;SQLite's file format&lt;/a&gt; arguably being the most prolific.&lt;/p&gt;

&lt;p&gt;SQL immediately stands out here because it was designed for making &lt;a href="https://en.wikipedia.org/wiki/Relational_algebra" rel="noopener noreferrer"&gt;relational algebra&lt;/a&gt;, the other side of the &lt;a href="https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model" rel="noopener noreferrer"&gt;Entity-Relationship model&lt;/a&gt;, accessible. There are likely more people who know SQL than any programming language (for IaC) or data format you could choose to represent your cloud infrastructure. Many non-programmers know it, as well, such as data scientists, business analysts, accountants, etc, and there is an entire ecosystem of useful functionality you gain "for free" by using a SQL representation than just about any format out there. Furthermore, a powerful SQL engine like the open source &lt;a href="https://postgresql.org" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; provides tools capable of elevating the infrastructure management &lt;em&gt;experience&lt;/em&gt; beyond what IaC can do today.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Advantages of SQL Databases for Infrastructure Management
&lt;/h3&gt;

&lt;p&gt;First, by defining &lt;a href="https://www.postgresql.org/docs/current/tutorial-fk.html" rel="noopener noreferrer"&gt;foreign key relations&lt;/a&gt; between tables, the database can immediately inform you if you have dependencies that must be created before you can create the resource you desire. It will simply return an error message to you if you have inserted an incomplete record missing any required fields.&lt;/p&gt;

&lt;p&gt;"But," you say, "programming languages with solid type systems, like &lt;a href="https://www.rust-lang.org" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;, can also do that for you." That is true, and this was not an attempt to disparage them, merely to show that you aren't losing anything by using SQL as the representation. There are, however, ways SQL goes above and beyond. By defining &lt;a href="https://www.postgresql.org/docs/current/ddl-constraints.html" rel="noopener noreferrer"&gt;unique, not-null, and check constraints&lt;/a&gt; on top of a &lt;a href="https://www.postgresql.org/docs/current/datatype.html" rel="noopener noreferrer"&gt;rich type system&lt;/a&gt; the database can prevent you from inserting faulty data to a greater degree than even Rust, as the constraints can be against the live state of your infrastructure itself. Instead of code needing to deal with unexpected values further down the line, the faulty data can be prevented from ever being inserted. Rust's compiler making sure that you handle every possible branch is an awesome thing, but SQL has the power to make more of these branches impossible in the first place.&lt;/p&gt;

&lt;p&gt;By being in a database, you can answer questions about your infrastructure via queries, and in the case of security vulnerabilities or expensive misconfigurations you can update that state across the board at the same time. The shared, live nature of the SQL database more closely matches the reality of the cloud infrastructure, while the more malleable nature of the querying in the Structured Query Language lets you cross-check in many dimensions the AWS console does not.&lt;/p&gt;

&lt;p&gt;There's a further advantage by having a bijective relationship with your cloud: you can re-synchronize the database with your cloud at any time, avoiding statefile issues by never getting too out-of-date, and being almost impossible to corrupt. Software bugs are unavoidable, but since populating the database is trivial (compared to the cloud account itself), it could simply be dropped and recreated at any time without an impact on your own usage.&lt;/p&gt;

&lt;p&gt;Most SQL databases have the concept of &lt;a href="https://www.postgresql.org/docs/current/sql-createtrigger.html#SQL-CREATETRIGGER-EXAMPLES" rel="noopener noreferrer"&gt;trigger functions&lt;/a&gt; that execute based on changes to the database itself. With a collection of automatically-created trigger functions, an &lt;a href="https://en.wikipedia.org/wiki/Audit_trail" rel="noopener noreferrer"&gt;audit log&lt;/a&gt; can be maintained, which can keep track of who made what cloud changes and when they did, going a step further than even a git repository of IaC changes, as changes done manually through the AWS console will also show up (though these would not have a database user to tie them to).&lt;/p&gt;

&lt;p&gt;Trigger functions have other uses that don't even have an analogous concept in IaC tooling. For instance, trigger functions can be added to automatically re-insert deleted records of sensitive cloud resources for automated &lt;a href="https://en.wikipedia.org/wiki/Chaos_engineering" rel="noopener noreferrer"&gt;Chaos Engineering&lt;/a&gt; recovery. Either a mutation that was attempted by a developer that shouldn't be allowed (without also going through the much larger change of removing said trigger function first) so the resource is never deleted in the first place, as well as automatic recreation of the desired resource in the cloud if deleted via the AWS console or dropped during an outage.&lt;/p&gt;

&lt;p&gt;SQL Database &lt;a href="https://en.wikipedia.org/wiki/Snapshot_%28computer_storage%29" rel="noopener noreferrer"&gt;Snapshotting&lt;/a&gt; could be used to quickly revert all infrastructure changes for infrastructure without hidden state (eg, the snapshot of your load balancers' prior state would successfully restore the load balancer configuration, but a dropped database restored via a snapshot would not restore the data within, only that you had such a database in the first place and you would still need to manually re-load a backup snapshot of those, as well).&lt;/p&gt;

&lt;p&gt;Database engines also represent a standardized protocol to interface with your data. This standard opens up a world of possibilities that can encompass the imperative and declarative infrastructure management styles and go beyond them. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perform operations on your database via scripts and the &lt;a href="https://www.postgresql.org/docs/current/app-psql.html" rel="noopener noreferrer"&gt;&lt;code&gt;psql&lt;/code&gt; CLI&lt;/a&gt; tool, using whatever mix of declarative and imperative styles you prefer, mutating various tables declaratively and calling &lt;a href="https://dev.to/docs/modules/aws/aws_sdk/?view=code-examples"&gt;&lt;code&gt;AWS SDK functions directly&lt;/code&gt;&lt;/a&gt; where and when you see fit.&lt;/li&gt;
&lt;li&gt;Integrate the database into your application itself with a &lt;a href="https://node-postgres.com/" rel="noopener noreferrer"&gt;postgres client library&lt;/a&gt; allowing your applications to make infrastructure changes (like provisioning sharded resources for a client that wants isolation, or using a more accurate forecasting model to pre-allocate more resources before the storm hits).&lt;/li&gt;
&lt;li&gt;Connect with a &lt;a href="https://www.jetbrains.com/datagrip/features/postgresql/" rel="noopener noreferrer"&gt;SQL IDE&lt;/a&gt; for a variety of reasons, such as outage mitigation or inspection of your infrastructure in a tabular (Excel-like) view.&lt;/li&gt;
&lt;li&gt;Connect the database to a &lt;a href="https://grafana.com/docs/grafana/latest/datasources/postgres/" rel="noopener noreferrer"&gt;graphical dashboard&lt;/a&gt; for live visualization of your infrastructure's state and any other metadata transferred through.&lt;/li&gt;
&lt;li&gt;Connect it to a &lt;a href="https://docs.retool.com/docs/postgresql-integration" rel="noopener noreferrer"&gt;low code tool&lt;/a&gt; for rapid response dashboards for your infrastructure with buttons you can tackle known outage vectors near-instantly, or just to make self-service provisioning of new microservices in the infrastructure team's preferred way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are negatives to representing your infrastructure as SQL, of course, as there is no perfect solution for all situations, but we believe &lt;a href="https://iasql.com" rel="noopener noreferrer"&gt;IaSQL&lt;/a&gt; is a superset versus IaC in all but one category.&lt;/p&gt;

&lt;p&gt;SQL is an old, irregular language to work with, but it is better known than HCL and SQL already has it's own Pulumi/CDK in the form of every ORM with introspection (like &lt;a href="https://www.prisma.io/docs/concepts/components/introspection" rel="noopener noreferrer"&gt;Javascript's Prisma&lt;/a&gt;, &lt;a href="https://docs.djangoproject.com/en/4.1/howto/legacy-databases/" rel="noopener noreferrer"&gt;Python's Django&lt;/a&gt;, &lt;a href="https://github.com/xo/xo" rel="noopener noreferrer"&gt;Go's XO&lt;/a&gt; etc) and QueryBuilder (&lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/" rel="noopener noreferrer"&gt;LINQ&lt;/a&gt;, &lt;a href="https://knexjs.org/" rel="noopener noreferrer"&gt;Knex&lt;/a&gt;, etc) in whatever programming language you prefer. You probably already know it.&lt;/p&gt;

&lt;p&gt;Peer review of changes is &lt;em&gt;different&lt;/em&gt; versus IaC. Instead of writing HCL or other IaC code, having it reviewed, and then &lt;code&gt;apply&lt;/code&gt;ing it, you can enter an &lt;a href="https://dev.to/docs/transaction"&gt;IaSQL transaction&lt;/a&gt;, make the desired changes with Postgres acting as your IDE, automatically blocking many impossible changes via types and relations, and use &lt;a href="https://dev.to/docs/review/"&gt;&lt;code&gt;iasql_create_review&lt;/code&gt;&lt;/a&gt; to create a review artifact for your team &lt;em&gt;after&lt;/em&gt; IaSQL has already vetted it for technical correctness. This vetting makes the result superior to the traditional peer review system in IaC.&lt;/p&gt;

&lt;p&gt;Database schemas can be overwhelming, and a schema accurately representing &lt;em&gt;all&lt;/em&gt; of AWS would be dizzying. HCL and other IaC tools let you elide parts of AWS via a module system, so we have implemented our own &lt;a href="https://dev.to/docs/module"&gt;module system&lt;/a&gt; on top of Postgres inspired equally by programming language module systems and Linux distro package managers. This module system takes the place the usual database &lt;a href="https://en.wikipedia.org/wiki/Schema_migration" rel="noopener noreferrer"&gt;schema migration system&lt;/a&gt; for the management of the tables in the database, which may feel unusual at first, but we believe it is superior (for this use-case).&lt;/p&gt;

&lt;p&gt;Even with IaC tools letting you ignore details you don't specifically need, AWS and other clouds expose many details in multiple layers for you to control when often you just need the same subset of functionality over-and-over, so these IaC tools have higher-level modules for these cookie-cutter use-cases. For IaSQL we &lt;a href="https://dev.to/docs/low-level-vs-high-level"&gt;did the same&lt;/a&gt;, though in a way that coexists with the lower-level modules so you can still query those details later, if you like. &lt;a href="https://dev.to/blog/ecs-simplified"&gt;Here&lt;/a&gt; is an example of a high-level module that greatly simplifies ECS fargate. &lt;/p&gt;

&lt;p&gt;IaSQL is declarative like IaC, so the leaky abstraction problem of declarative programming can still impact things. Like IaC's &lt;code&gt;apply&lt;/code&gt; concept IaSQL has &lt;a href="https://dev.to/docs/transaction"&gt;its own transaction system&lt;/a&gt; (separate from the actual &lt;a href="https://www.postgresql.org/docs/current/tutorial-transactions.html" rel="noopener noreferrer"&gt;database transaction system&lt;/a&gt;) that will let you enqueue changes and preview what actions the engine will take before commiting to it (or rolling it back). Going beyond that and acknowledging that sometimes you need to directly work in the lower abstraction layer, IaSQL exposes optional &lt;a href="https://iasql.com/docs/modules/aws/aws_sdk/" rel="noopener noreferrer"&gt;raw SDK access&lt;/a&gt; within the database to handle those tougher situations, and can then re-synchronize the declarative state afterwards.&lt;/p&gt;

&lt;p&gt;As IaSQL is able to both push changes from the database to the cloud and pull changes from the cloud to the database versus the one-way push from HCL that Terraform provides, you can make changes directly in the AWS console, then inspect the audit log directly or &lt;a href="https://iasql.com/docs/modules/builtin/tables/iasql_functions_rpcs_iasql_get_sql_since.IasqlGetSqlSince/" rel="noopener noreferrer"&gt;call a special formatting function&lt;/a&gt; to see what SQL statements were the equivalent of those console changes. This reduces the learning curve of IaSQL and also lowers the barrier to entry in comparison to IaC. You can continue using other cloud management tooling in conjunction with IaSQL (including to a degree IaC) and IaSQL will show you "it's way" of doing the same when it syncs.&lt;/p&gt;

&lt;p&gt;Finally, IaSQL makes your infrastructure &lt;em&gt;accessible&lt;/em&gt; to a much large portion of your company than IaC tools can. Data Scientists who know SQL could help with the provisioning of their own batch processing code, EngSec can query &lt;em&gt;all&lt;/em&gt; company infrastructure for security vulnerabilities and propose changes to improve things, Business Analysts in Growth teams can query actual traffic information from the live infrastructure (with read-only AWS credentials, of course), Finance auditors can query your infrastructure joined on pricing data and figure out &lt;em&gt;accurate&lt;/em&gt; expense reporting per division, and DevOps can run queries joined on CloudWatch utilization stats to automatically identify overprovisioned services and create recommended auto-load scaling configuration changes. Tons of "not-developer" and "developer-adjacent" roles that can take over a lot of the ancillary burden of maintaining a production system.&lt;/p&gt;

&lt;p&gt;But IaSQL is much newer than the IaC tools. We have coverage at the time of writing for 25 different AWS services, but that is admittedly far from complete. IaC tools also have cloud resources they cannot represent, but it is a much smaller percentage. This disadvantage will diminish over time, but creating a bijective entity representation of every cloud service requires more developer firepower than the approach IaC tools have taken, so we expect to always lag temporally on this front. At least, as long as AWS and friends don't follow a resource-oriented standard like &lt;a href="https://swagger.io/specification/" rel="noopener noreferrer"&gt;Swagger&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloud APIs on Swagger?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fibn1tthjypydhcgs9fps.jpg" class="article-body-image-wrapper"&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%2Farticles%2Fibn1tthjypydhcgs9fps.jpg" alt="swagger-i-would-be-so-happy" width="500" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Me too, Craig, me too...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that said, what does our EC2 instance example look like in IaSQL, uh, SQL?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Assuming that the AWS credentials have already been inserted as done during the setup wizard&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;default_aws_region&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Install the necessary modules&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;iasql_install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_ec2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'aws_ec2_metadata'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ssh_accounts'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Create the key-pair and put it in our ssh_credentials table&lt;/span&gt;
&lt;span class="c1"&gt;-- We will update this later with the public IP address&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;ssh_credentials&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tbd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ubuntu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;privateKey&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;key_pair_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="c1"&gt;-- Start an IaSQL transaction so these changes don't mutate our infrastructure immediately&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;iasql_begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;-- Define the security group&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;security_group&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Login security group'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'login-sg'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- And it's ingress rule&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;security_group_rule&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_egress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_protocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cidr_ipv4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;security_group_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tcp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0.0.0.0/0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;security_group&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;group_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'login-sg'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Define the instance and the key-pair to use&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ami&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_pair_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subnet_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="s1"&gt;'resolve:ssm:/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'t3.small'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{"name":"login"}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;subnet&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;availability_zone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2a'&lt;/span&gt;
  &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Associate the instance with the security group&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;instance_security_groups&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;security_group_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;security_group&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'login-sg'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then &lt;code&gt;SELECT * FROM iasql_preview();&lt;/code&gt; to see what this does, like a &lt;code&gt;terraform plan&lt;/code&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;iasql_preview&lt;/code&gt; output
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;action&lt;/th&gt;
&lt;th&gt;table_name&lt;/th&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;create&lt;/td&gt;
&lt;td&gt;instance&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;create&lt;/td&gt;
&lt;td&gt;security_group&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;create&lt;/td&gt;
&lt;td&gt;security_group_rule&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is a "high level" overview with minimal details. We can get back a listing of all changes, represented in auto-generated SQL with &lt;code&gt;SELECT * FROM iasql_get_sql_for_transaction();&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;security_group&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Login security group'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'login-sg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_regions&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;security_group_rule&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_egress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_protocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cidr_ipv4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;security_group_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tcp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'22'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'22'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0.0.0.0/0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_regions&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;security_group&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;group_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_regions&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;


&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ami&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_pair_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hibernation_enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subnet_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'resolve:ssm:/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'t3.small'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'running'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"login"}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_regions&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;subnet&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;subnet_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'subnet-06140fd708a495450'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_regions&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;


&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;instance_security_groups&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;security_group_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;instance_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_regions&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;security_group&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;group_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_regions&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'us-west-2'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which in this case is very similar to (though a bit more verbose than) our original SQL statements, but if you were going back and forth on what exact changes you're going to make, it can be a good summary.&lt;/p&gt;

&lt;p&gt;If we're good with this, we can &lt;code&gt;SELECT iasql_commit()&lt;/code&gt; to push this to our cloud account and then update our &lt;code&gt;ssh_credentials&lt;/code&gt; record with the public IP address and check the connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;public_ip_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;instance_metadata&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;
  &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'login'&lt;/span&gt;
  &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;ssh_credentials&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;ssh_credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tbd'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;ssh_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'uname -a'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SQL is the least bad option for Infrastructure Management
&lt;/h2&gt;

&lt;p&gt;Infrastructure Management is a complex beast. The cloud APIs are massive with a rich collection of knobs to tweak, and mistakes can be dangerous because the changes often take a long time and recovery even more so. We have learned about the two main types of infrastructure management: imperative and declarative, where ad-hoc usage of the AWS console fits under the imperative umbrella along with scripts calling an SDK, while IaC has until recently been the only way to declaratively manage your infrastructure.&lt;/p&gt;

&lt;p&gt;Both imperative and declarative styles have their downsides. Imperative being more explicit makes it more tedious and more prone to error, and the usual execution frequency of &lt;em&gt;only once&lt;/em&gt; for most production changes makes scripting usually no better than clicking around in the AWS console. Declarative is much less tedious, but because it is a leaky abstraction inspecting its execution plan is critical to make sure it doesn't automatically put you into an outage, and failures during application can still require falling back to imperative mode to get things back into shape from time-to-time.&lt;/p&gt;

&lt;p&gt;Also until recently, all declarative approaches have suffered from only being a one-way transition, from your IaC codebase into the cloud, but no good or easy way to synchronize that code with the current state of the cloud if they drift from each other. Because these declarative programming approaches do not have a singular canonical form, they can't be bijective, making the transform back from the other side impossible to square up.&lt;/p&gt;

&lt;p&gt;Data, on the other hand, is much easier to normalize in such a way, and relational data models, as found in SQL databases, can naturally model the relationships between cloud services, making it possible to produce a bijective representation. IaSQL takes advantage of this to make a declarative model based on tables and foreign keys that can automatically handle changes done outside of this declarative model, either by dropping down into its own imperative mode or by using any other tooling to manage your infrastructure, giving it a flexibility and resiliency that IaC tools don't have.&lt;/p&gt;

&lt;p&gt;On top of this foundation, tooling to replicate or substitute for existing IaC tooling has been added, along with all of the past ~50 years of database tooling and expertise that you can take advantage of, providing a superset of functionality that makes existing use-cases easier and new use-cases possible.&lt;/p&gt;

&lt;p&gt;IaSQL is &lt;a href="https://dev.to/blog/beta"&gt;currently in beta&lt;/a&gt; and can be used locally with &lt;a href="https://hub.docker.com/r/iasql/iasql" rel="noopener noreferrer"&gt;&lt;code&gt;docker&lt;/code&gt;&lt;/a&gt; or via our &lt;a href="https://dev.to/hosted"&gt;SaaS&lt;/a&gt;. We're proud of what we have built, and can't wait to hear from you on feature requests and bug reports.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Securely connect to an Amazon RDS via PrivateLink using SQL</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Wed, 29 Mar 2023 20:55:59 +0000</pubDate>
      <link>https://dev.to/iasql/securely-connect-to-an-amazon-rds-via-privatelink-using-sql-40ck</link>
      <guid>https://dev.to/iasql/securely-connect-to-an-amazon-rds-via-privatelink-using-sql-40ck</guid>
      <description>&lt;p&gt;Do you have some database instances on RDS and wonder what's the most secure way to reach them? In this post, we will walk you through how to securely connect to an AWS RDS instance using PrivateLink and IaSQL. AWS PrivateLink provides private connectivity between virtual private clouds (VPCs), supported AWS services, and your on-premises networks without exposing your traffic to the public internet. IaSQL is an &lt;a href="https://github.com/iasql/iasql" rel="noopener noreferrer"&gt;open-source&lt;/a&gt; software tool that creates a two-way connection between an unmodified PostgreSQL database and an AWS account so you can manage your infrastructure from a database.&lt;/p&gt;

&lt;p&gt;When creating a database, AWS provides some specific information about the hostnames and ports used. You can access those to reach your databases by using specific clients for MySQL, Postgres, etc...:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Flyif41v4pes3b1egsbgo.png" class="article-body-image-wrapper"&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%2Farticles%2Flyif41v4pes3b1egsbgo.png" alt="Image description" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On a public VPC, those endpoints will be public by default. However, consider creating them under a private VPC and accessing them internally, to grant additional security to critical services. AWS PrivateLink offers the possibility to securely access those services without exposing them to the internet, just using Amazon's private network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are my RDS instances properly configured?
&lt;/h3&gt;

&lt;p&gt;Please use this query to check it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt; &lt;span class="c1"&gt;---- Installing the needed modules&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_install&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_rds'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'aws_vpc'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Perform the query for endpoints&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cidr_block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'rds'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;has_endpoint_interface&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;rds&lt;/span&gt;
  &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have you found missing endpoints? No problem, IaSQL can generate missing Endpoint Interfaces for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;-- Inserts the missing endpoints&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;endpoint_interface&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_dns_enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;RDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'rds'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;TRUE&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;rds&lt;/span&gt;
  &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Preview the changes&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;iasql_preview&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;-- Apply the changes&lt;/span&gt;
&lt;span class="c1"&gt;--select * from iasql_commit();&lt;/span&gt;
&lt;span class="c1"&gt;-- Rollback the changes&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;iasql_rollback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this query on an IaSQL database will auto-generate the missing endpoint interfaces and will allow you to preview the changes to be applied on your cloud. Once you are OK with the results, you can uncomment the &lt;code&gt;iasql_commit&lt;/code&gt; query, comment the &lt;code&gt;iasql_rollback&lt;/code&gt; query, and it will create the endpoints for you. If the final results in the cloud are not as expected, you can always roll back your changes by calling the &lt;code&gt;iasql_rollback&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing the result
&lt;/h3&gt;

&lt;p&gt;After running the query, you should have Endpoint Interfaces created for your RDS resources. Those should be on the region and VPC where you had your databases:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F3t739za4kz7vgzk3376q.png" class="article-body-image-wrapper"&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%2Farticles%2F3t739za4kz7vgzk3376q.png" alt="Image description" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To start testing the result, you can start a new EC2 instance on a private VPC in the same region where your RDS and Endpoint interface is configured. You can double-check that there is no internet connectivity. But the RDS endpoint could still be reached, by using the interface that has been created:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Frqhqpzqyueo97g4vm373.png" class="article-body-image-wrapper"&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%2Farticles%2Frqhqpzqyueo97g4vm373.png" alt="Image description" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please note that you could only use those endpoints from the same region. You could reach the services in multiple regions with the use of &lt;a href="https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html" rel="noopener noreferrer"&gt;VPC peering&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>postgres</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Save $ on public S3 buckets using VPC endpoints via SQL</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Thu, 23 Mar 2023 20:19:30 +0000</pubDate>
      <link>https://dev.to/iasql/save-on-public-s3-buckets-using-vpc-endpoints-via-sql-5dko</link>
      <guid>https://dev.to/iasql/save-on-public-s3-buckets-using-vpc-endpoints-via-sql-5dko</guid>
      <description>&lt;p&gt;Are you using S3 buckets as part of your cloud deployments? How are you accessing them?&lt;/p&gt;

&lt;p&gt;When running applications behind VPCs without public access, there may be the need to access S3 buckets from the private subnet over the public internet.&lt;br&gt;
One simple but costly way to do so is to rely on &lt;a href="https://docs.aws.amazon.com/en_en/vpc/latest/userguide/vpc-nat-gateway.html" rel="noopener noreferrer"&gt;NAT gateways&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fp6l13rn5xoev5l0vjin2.png" class="article-body-image-wrapper"&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%2Farticles%2Fp6l13rn5xoev5l0vjin2.png" alt="Image description" width="800" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, creating &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-s3.html" rel="noopener noreferrer"&gt;gateway or interface VPC endpoints&lt;/a&gt; for each region where your buckets are exposed is a more optimal solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F4eo3581aswhxqkae75uf.png" class="article-body-image-wrapper"&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%2Farticles%2F4eo3581aswhxqkae75uf.png" alt="Image description" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the VPC endpoints are enabled you can access your S3 buckets using this endpoint. In this post, we will walk you through how to control your buckets from an internal network with the desired security, and without the extra costs of a NAT gateway using a VPC endpoint and IaSQL. IaSQL is an &lt;a href="https://github.com/iasql/iasql" rel="noopener noreferrer"&gt;open-source&lt;/a&gt; software tool that creates a two-way connection between an unmodified PostgreSQL database and an AWS account so you can manage your infrastructure from a database.&lt;/p&gt;


&lt;h3&gt;
  
  
  Why use VPC Interface Endpoints
&lt;/h3&gt;

&lt;p&gt;AWS VPC Interface Endpoints are a component of AWS's &lt;a href="https://aws.amazon.com/privatelink" rel="noopener noreferrer"&gt;PrivateLink&lt;/a&gt; infrastructure. AWS PrivateLink provides private connectivity between virtual private clouds (VPCs), supported AWS services, and your on-premises networks without exposing your traffic to the public internet.&lt;/p&gt;

&lt;p&gt;Relying on PrivateLink offers a set of advantages in terms of &lt;strong&gt;cost&lt;/strong&gt;, &lt;strong&gt;security&lt;/strong&gt; and &lt;strong&gt;performance&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using NAT gateways, you are paying for the NAT gateway itself, and the bandwidth used to access the internet. A NAT gateway is a generic way for private instances to reach the internet, and can be useful for generic usage, such as pulling dependencies, updating repositories, etc... But when used for reaching AWS resources directly, there are more optimal and cheaper solutions such as VPC endpoints - they will not have generic access to internet but only to specified public AWS endpoints. When using VPC endpoints, you are paying for the bandwidth used to access the public endpoints, but not for the VPC endpoint itself.&lt;/p&gt;

&lt;p&gt;Costs for NAT gateway depend on each region, but it should be over &lt;em&gt;0.045 USD/hour&lt;/em&gt; per GB processed + &lt;em&gt;0.045 USD/hour&lt;/em&gt; for the instance itself. Costs for VPC endpoints are charged at &lt;em&gt;0.01 USD/hour&lt;/em&gt; per GB processed, being cheaper after the first PB.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Hourly pricing&lt;/th&gt;
&lt;th&gt;Per GB pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NAT gateway&lt;/td&gt;
&lt;td&gt;0.045 USD/hour&lt;/td&gt;
&lt;td&gt;0.45 USD/hour per GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VPC endpoint&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;td&gt;0.01 USD/hour per GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Security: no internet traversal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When deploying services in production, the concept of defense-in-depth is essential: have an information assurance strategy that provides multiple, redundant defensive measures in case a security control fails or a vulnerability is exploited.&lt;br&gt;
By using interface endpoints the services will be accessed via Amazon's internal networks. It is an essential security measure, that will prevent attackers from directly reaching the services by keeping them in a private network, reducing the surface area of attack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security: policies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS services can benefit from &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_controlling.html" rel="noopener noreferrer"&gt;IAM policies&lt;/a&gt; for fine-grained access control. When using interface endpoints, those policies can be applied to determine the traffic that can pass through the interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance: latency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because the traffic stays within Amazon’s networks, the physical distance traveled, the number of hops and the risk of traversing congested networks are all significantly lower.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance: bandwidth&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PrivateLink supports a sustained bandwidth of 10 Gbps per availability zone with bursts up to 40 Gbps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance: stability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PrivateLink consistently lowers the number of errors and timeouts on high loads. The distance traveled, the number of hops, and the risks associated with traversing congested networks are reduced when the traffic over Private Link stays within Amazon’s networks.&lt;/p&gt;

&lt;p&gt;In terms of speed, Private Link supports a sustained bandwidth of 10 Gbps per availability zone with bursts up to 40 Gbps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you want to know if you have it properly configured? The following query will check for active S3 VPC interface endpoints:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This query will find your active S3 buckets for all regions, associated with the existing endpoint gateways or interfaces - if they exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt; &lt;span class="c1"&gt;-- Installing the needed modules&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_install&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_s3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'aws_vpc'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Perform the query for endpoints&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cidr_block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_gateway&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'s3'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;endpoint_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;has_endpoint_gateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'s3'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;has_endpoint_interface&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;bucket&lt;/span&gt;
  &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have you found missing endpoints? No problem, IaSQL can generate them for you. You can create two different types of endpoints: gateway and interface. We're going to describe their main features and you can create the desired type in an automated way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interface endpoints
&lt;/h2&gt;

&lt;p&gt;An interface endpoint is an elastic network interface with a private IP address that serves as an entry point for traffic destined for a supported AWS service. As they use ENIs, they can benefit from security groups to control traffic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_controlling.html" rel="noopener noreferrer"&gt;IAM policies&lt;/a&gt; can also be&lt;br&gt;
added to control access to those endpoints.&lt;/p&gt;

&lt;p&gt;They are regional, meaning they can only be accessed by the same region they are created. However, Multi-region is possible by also using VPC peering, so resources in one region can be accessed from others, though this only supports IPv4 TCP traffic.&lt;/p&gt;

&lt;p&gt;An interface endpoint (except S3 interface endpoint) has a corresponding private DNS hostname, that can be used to access the resource.&lt;/p&gt;

&lt;p&gt;Interface endpoints cover a wide range of services such as S3, Lambda, API Gateway, etc... &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html" rel="noopener noreferrer"&gt;Get more detail about interface endpoints&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This query will auto-generate the missing interface endpoints and will preview the changes to be applied on your cloud. Simply uncomment the final statement and run it to make the changes to your cloud account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt; &lt;span class="c1"&gt;-- Inserts the missing endpoints&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;endpoint_interface&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'s3'&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;bucket&lt;/span&gt;
  &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;endpoint_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Preview the changes&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;iasql_preview&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;-- Commit the changes&lt;/span&gt;
&lt;span class="c1"&gt;-- SELECT * FROM iasql_commit();&lt;/span&gt;
&lt;span class="c1"&gt;-- Rollback changes&lt;/span&gt;
&lt;span class="c1"&gt;-- SELECT * FROM iasql_rollback();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Endpoint gateways
&lt;/h2&gt;

&lt;p&gt;An endpoint gateway is a gateway that can be configured as a target for a route in your route table, used to access traffic in DynamoDB or S3.&lt;/p&gt;

&lt;p&gt;Multiple gateway endpoints can be created in a single VPC, and those can be used on different route tables to enforce different access policies from different subnets to the same service.&lt;/p&gt;

&lt;p&gt;Gateway endpoints are supported within the same region only, resources from multiple regions cannot be accessed, even using VPC peering. They also support IPv4 traffic only.&lt;/p&gt;

&lt;p&gt;To use them, DNS resolution must be enabled in the VPC.&lt;/p&gt;

&lt;p&gt;When a route is added, all instances in the subnets associated with the route table will automatically use the endpoint to access the service.&lt;/p&gt;

&lt;p&gt;Gateway endpoints only support S3 and DynamoDB services. &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/gateway-endpoints.html" rel="noopener noreferrer"&gt;Get more detail about gateway endpoints&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This query will auto-generate the missing endpoint gateways and will allow you to preview the changes to be applied on your cloud.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt; &lt;span class="c1"&gt;-- Inserts the missing endpoints&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;endpoint_gateway&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'s3'&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;bucket&lt;/span&gt;
  &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_gateway&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
      &lt;span class="n"&gt;endpoint_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;endpoint_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;-- Preview the changes&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;iasql_preview&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;-- Commit the changes&lt;/span&gt;
&lt;span class="c1"&gt;-- SELECT * FROM iasql_commit();&lt;/span&gt;
&lt;span class="c1"&gt;-- Rollback changes&lt;/span&gt;
&lt;span class="c1"&gt;-- SELECT * FROM iasql_rollback();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the result
&lt;/h2&gt;

&lt;p&gt;Once all the relevant endpoints have been created, you can confirm your access to the gateway endpoints from an internal EC2 instance on the same region as the endpoint you want to test:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fs8ggg3xxrhx6dkbjt4gl.png" class="article-body-image-wrapper"&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%2Farticles%2Fs8ggg3xxrhx6dkbjt4gl.png" alt="Image description" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For interface endpoints, access also can be tested using the named endpoint.&lt;/p&gt;

</description>
      <category>s3</category>
      <category>aws</category>
      <category>sql</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Deploy a Static Website on AWS using SQL</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Mon, 06 Mar 2023 20:37:34 +0000</pubDate>
      <link>https://dev.to/iasql/deploy-a-static-website-on-aws-using-sql-4nj</link>
      <guid>https://dev.to/iasql/deploy-a-static-website-on-aws-using-sql-4nj</guid>
      <description>&lt;p&gt;Did you know you can deploy a static website using a SQL REPL? In this post, we'll show you how to use IaSQL to deploy a static website from your GitHub repository to AWS S3 + Cloudfront services using only SQL queries. IaSQL is an &lt;a href="https://github.com/iasql/iasql" rel="noopener noreferrer"&gt;open-source&lt;/a&gt; software tool that creates a two-way connection between an unmodified PostgreSQL database and an AWS account so you can manage your infrastructure from a database.&lt;/p&gt;

&lt;p&gt;We will create and configure an S3 bucket to serve our static website. To enable support for HTTPS, we'll also add a CloudFront distribution. We will also leverage CodeBuild to automatically build the files for our project and copy them to the S3 bucket created already.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a S3 Bucket
&lt;/h2&gt;

&lt;p&gt;To be able to work with S3, we should first install the corresponding IaSQL module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_install&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_s3'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installing a module gives us the ability to use tables and RPCs provided by it. &lt;code&gt;aws_s3&lt;/code&gt; module gives us the ability to manage an S3 bucket, S3 static website hosting, and other related stuff. So let's create an S3 bucket first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy_document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::&amp;lt;bucket-name&amp;gt;/*"
      ]
    }
  ]
}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'us-east-1'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_commit&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above query will create a new bucket in the &lt;code&gt;us-east-1&lt;/code&gt; region with the defined name &lt;code&gt;&amp;lt;bucket-name&amp;gt;&lt;/code&gt; and the given policy using IaSQL. The &lt;code&gt;iasql_begin&lt;/code&gt; and &lt;code&gt;iasql_commit&lt;/code&gt; functions are RPCs that will start and finish an IaSQL transaction. Learn more about IaSQL transactions in this part of our &lt;a href="https://dev.to/docs/transaction/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have a bucket, we can upload a file to it and see if we're able to view it using our web browser. Let's use IaSQL to upload a file to our newly created bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;s3_upload_object&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'hello.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Hello IaSQL!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'text/plain'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is going to upload a file named &lt;code&gt;hello.txt&lt;/code&gt; in our bucket whose content is &lt;code&gt;Hello IaSQL!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can read the code for &lt;a href="https://github.com/iasql/iasql/blob/c70f068c7520baf00cea9ddd3a76b8c6dbd2b23b/src/modules/aws_s3/rpcs/s3_upload_object.ts#L27-L33" rel="noopener noreferrer"&gt;&lt;code&gt;s3_upload_object&lt;/code&gt; RPC&lt;/a&gt; as well as all other IaSQL modules and RPCs in our GitHub &lt;a href="https://github.com/iasql/iasql/" rel="noopener noreferrer"&gt;repository&lt;/a&gt; to see how they work.&lt;/p&gt;

&lt;p&gt;Let's see if we can access our file using the S3 bucket URL. It should be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;bucket-name&amp;gt;.s3.amazonaws.com/hello.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we're unable to access the file directly because S3 blocks public access by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F6gs1uxohdyj4uoinmfdl.png" class="article-body-image-wrapper"&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%2Farticles%2F6gs1uxohdyj4uoinmfdl.png" alt="Image description" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Make The Bucket Public
&lt;/h2&gt;

&lt;p&gt;We need to enable public access to our bucket files to be able to directly access the files. We can use the &lt;code&gt;public_access_block&lt;/code&gt; table provided by &lt;code&gt;aws_s3&lt;/code&gt; module to allow for public requests to reach our objects.&lt;/p&gt;

&lt;p&gt;If you want to know which resources (via tables) an IaSQL module handles, you can visit our documentation page. It also provides a list and explanation of all the RPCs that are provided by a module. In our case, we can visit &lt;a href="https://iasql.com/docs/reference/sql/#aws_s3" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get a list of tables and RPCs available for &lt;code&gt;aws_s3&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;public_access_block&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_public_acls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ignore_public_acls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_public_policy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;restrict_public_buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="n"&gt;block_public_acls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_public_acls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ignore_public_acls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ignore_public_acls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;block_public_policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_public_policy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;restrict_public_buckets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;restrict_public_buckets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_commit&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 a 1-1 relationship between the bucket and bucket public access block, therefore we're using Postgres &lt;a href="https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT" rel="noopener noreferrer"&gt;&lt;code&gt;ON CONFLICT&lt;/code&gt;&lt;/a&gt; syntax so that when there's a record already, we can replace it without hassle.&lt;/p&gt;

&lt;p&gt;Now we should be able to directly access our file through the web browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;bucket-name&amp;gt;.s3.amazonaws.com/hello.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="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%2Farticles%2Fwbuga1wuf2gwak0ep8b6.png" class="article-body-image-wrapper"&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%2Farticles%2Fwbuga1wuf2gwak0ep8b6.png" alt="Image description" width="340" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use S3 Static Website Hosting
&lt;/h2&gt;

&lt;p&gt;But simply serving the files doesn't mean we can host a static website. We need to enable &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html" rel="noopener noreferrer"&gt;static website hosting&lt;/a&gt; for our bucket to be able to deploy a React codebase. So let's enable it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;bucket_website&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index_document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'index.html'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_commit&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll use this functionality to route all the requests to &lt;code&gt;index.html&lt;/code&gt; file. This way we can deploy a sample React application and serve it through S3. To get the link for our S3 bucket's static website, we can use &lt;code&gt;get_bucket_website_endpoint&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;get_bucket_website_endpoint&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build The Project And Sync To S3
&lt;/h2&gt;

&lt;p&gt;Now that everything is set, we just need to build our React app and deploy it to S3. We have already pushed a sample app to this repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/iasql/sample-react-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But you can use whatever codebase you'd like by changing the URLs so that they point to the Github repository hosting your React app.&lt;/p&gt;

&lt;p&gt;Now it's the time to create a CodeBuild project. CodeBuild is an AWS CI/CD system that is free of cost. The CodeBuild project will do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull the codebase from the GitHub repository&lt;/li&gt;
&lt;li&gt;Build the app&lt;/li&gt;
&lt;li&gt;Copy the resulting files (&lt;code&gt;build/*&lt;/code&gt;) to the S3 bucket&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can do this with the following SQL query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;-- create the needed role for codebuild&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;iam_role&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assume_role_policy_document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attached_policies_arns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'deploy-static-website-role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "codebuild.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ],
    "Version": "2012-10-17"
  }'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s1"&gt;'arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'arn:aws:iam::aws:policy/AmazonS3FullAccess'&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- create the codebuild project&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;codebuild_project&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;build_spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;privileged_mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_role_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'deploy-static-website'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'version: 0.2

phases:
  pre_build:
    commands:
      - git clone https://github.com/iasql/sample-react-app &amp;amp;&amp;amp; cd sample-react-app
  build:
    commands:
      - echo Installing dependencies
      - npm install
      - echo Building the app
      - npm run build
  post_build:
    commands:
      - echo Copying the files to the S3 bucket
      - aws s3 sync build/ s3://&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'NO_SOURCE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'deploy-static-website-role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'us-east-1'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_commit&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above SQL command first creates a role that is needed for CodeBuild to operate. Then it'll create the actual CodeBuild project that clones the repo, builds it and finally syncs the resulting files to our S3 bucket. We need to trigger the CodeBuild project to run and then our files will be uploaded to our bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;start_build&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'deploy-static-website'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'us-east-1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will trigger the CodeBuild project to start. After a while we should be able to see the files appearing in our S3 bucket. We can access our React app using the endpoint for the S3 static website hosting. As we already mentioned, to get the endpoint we can use &lt;code&gt;get_bucket_website_endpoint&lt;/code&gt; helper function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;get_bucket_website_endpoint&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By visiting the link returned by the above function, you can see our sample app has been deployed. The problem is that S3 static website hosting does not support HTTPS, and therefore we need to use a CloudFront distribution in order to have HTTPS connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a CloudFront Distribution
&lt;/h2&gt;

&lt;p&gt;Serving files in a bucket to the public using pure S3 isn't a good idea. In our case because the S3 static website hosting does not provide an HTTPS endpoint, but in most cases because the &lt;a href="https://aws.amazon.com/s3/pricing/" rel="noopener noreferrer"&gt;data transfer rates for S3&lt;/a&gt; aren't cheap and can grow out of control. Also, the speed in which the users can access to the bucket objects will increase if you use a CDN because they'll be delivered to the users from the nearest edge server.&lt;/p&gt;

&lt;p&gt;With the above in mind, let's create a CloudFront distribution for our S3 bucket. But first, we need to install the &lt;code&gt;aws_cloudfront&lt;/code&gt; module to be able to leverage its abilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_install&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_cloudfront'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;distribution&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caller_reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_cache_behavior&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'my-website'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;'[
  {
    "DomainName": "'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt;
          &lt;span class="n"&gt;get_bucket_website_endpoint&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;bucket-name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'",
    "Id": "my-website-origin",
    "CustomOriginConfig": {
      "HTTPPort": 80,
      "HTTPSPort": 443,
      "OriginProtocolPolicy": "http-only"
    }
  }
]'&lt;/span&gt;
    &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{
  "TargetOriginId": "my-website-origin",
  "ViewerProtocolPolicy": "allow-all",
  "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6"
}'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_commit&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can access our website through the CloudFront distribution domain name. To get it, we can simply run the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;domain_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;distribution&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;caller_reference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my-website'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It supports HTTPS, it's fast, it's cheaper than directly serving on S3, and it can be easily connected to a Route53 domain.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>aws</category>
    </item>
    <item>
      <title>Deploy a Node.js app to AWS ECS+ECR+ELB quickly using PostgreSQL</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Thu, 02 Mar 2023 18:39:24 +0000</pubDate>
      <link>https://dev.to/iasql/deploy-an-app-to-aws-using-ecsecrelb-quickly-using-postgresql-42dd</link>
      <guid>https://dev.to/iasql/deploy-an-app-to-aws-using-ecsecrelb-quickly-using-postgresql-42dd</guid>
      <description>&lt;p&gt;IaSQL is an &lt;a href="https://github.com/iasql/iasql" rel="noopener noreferrer"&gt;open-source&lt;/a&gt; software tool that creates a two-way connection between an unmodified PostgreSQL database and an AWS account so you can manage your infrastructure from a database. In this post, we're going to discover an &lt;a href="https://dev.todocs/module"&gt;IaSQL module&lt;/a&gt; that's built to make deploying to ECS, simplified. Most of the details for deploying a container to AWS ECS are the same (load balancers, security groups, IAM roles, etc), and we have created the &lt;code&gt;aws_ecs_simplified&lt;/code&gt; module for you so that you can give it any Github repo with a &lt;code&gt;Dockerfile&lt;/code&gt; and get your app deployed to ECS in the fastest time possible, with scalability available! All the needed resources are going to be created automatically in your AWS account, and you'll have full access to the details while you're gaining the benefit of a higher-level simple deployment.&lt;/p&gt;

&lt;p&gt;If you have ever tried to deploy your containerized application to ECS, you know that it's not going to be an easy click-to-deploy journey. To get your application up and running on ECS, you have to go through a bunch of resource creation. You'll need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy a load balancer as the point-of-contact for your app&lt;/li&gt;
&lt;li&gt;Create a target group for the load balancer and register the ECS tasks in it&lt;/li&gt;
&lt;li&gt;Add a new listener to your load balancer and connect it to the target group&lt;/li&gt;
&lt;li&gt;Create a security group, and you need to allow the port your app is listening on in that security group&lt;/li&gt;
&lt;li&gt;Attach the security group above to your load balancer&lt;/li&gt;
&lt;li&gt;Create a CloudWatch log group for your ECS task&lt;/li&gt;
&lt;li&gt;Create an ECS cluster, and definitely the task definition as well&lt;/li&gt;
&lt;li&gt;Oh, and create an ECR repository to push your images to be run on the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not going to continue this long list, since I've already got a headache. Doing those steps manually is going to give you a headache as well, so why bother doing all those steps yourself and risking different errors you might face when deploying your containerized app? You don't really have the time for the random IAM-related errors AWS is demanding you to resolve. Besides, you already have your codebase ready and the &lt;code&gt;Dockerfile&lt;/code&gt; is there, so why not just run a simple command doing something that should be simply done?&lt;/p&gt;

&lt;h2&gt;
  
  
  An Example Usage of the &lt;code&gt;aws_ecs_simplified&lt;/code&gt; Module
&lt;/h2&gt;

&lt;p&gt;Let's say we are going to deploy &lt;a href="https://github.com/iasql/iasql/tree/main/examples/ecs-fargate/prisma/app" rel="noopener noreferrer"&gt;this simple Express.js app&lt;/a&gt; to the ECS. It has a &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;package.json&lt;/code&gt; that installs &lt;code&gt;express&lt;/code&gt; on &lt;code&gt;npm install&lt;/code&gt;. &lt;code&gt;npm start&lt;/code&gt; then starts the Express server which listens on port &lt;code&gt;8088&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aws_ecs_simplified&lt;/code&gt; is a high-level module we have created to make scalable ECS deployments easier. For more info on high-level vs low-level modules, you can check &lt;a href="https://iasql.com/docs/low-level-vs-high-level/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's go and deploy the above app to your AWS account. Don't worry if you don't have an IaSQL database already, you can run IaSQL locally and run one locally for free &lt;a href="https://dev.to/docs/"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_install&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_ecs_simplified'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'aws_codebuild'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_begin&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;ecs_simplified&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'simple-express'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8088&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'latest'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_commit&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;ecr_build&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'https://github.com/iasql/iasql/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- the Github repo URL&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;id&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;
      &lt;span class="k"&gt;WHERE&lt;/span&gt;
        &lt;span class="n"&gt;repository_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'simple-express-repository'&lt;/span&gt;
    &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- ECR repo for the image to be pushed&lt;/span&gt;
    &lt;span class="s1"&gt;'./examples/ecs-fargate/prisma/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- the subdirectory in Github repo&lt;/span&gt;
    &lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- the Github branch or ref&lt;/span&gt;
    &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="c1"&gt;-- Github personal access token - can be omitted if public repository&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! Now wait for some time and your app is deployed! While your app is being deployed, let's go through the commands we executed in more depth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;iasql_install&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_ecs_simplified'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'aws_codebuild'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This command installs the &lt;code&gt;aws_ecs_simplified&lt;/code&gt; high-level module. We– at IaSQL– have created that module to make it easy to deploy containerized apps to ECS. The code for it is &lt;a href="https://github.com/iasql/iasql/blob/v0.0.22/src/modules/0.0.23/aws_ecs_simplified/sql/after_install.sql" rel="noopener noreferrer"&gt;here&lt;/a&gt;. But IaSQL is so flexible that anyone can create their own high-level (and of course, low-level) modules and add it to IaSQL.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;ecs_simplified&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'simple-express'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8088&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'latest'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This command creates a new &lt;code&gt;ecs_simplified&lt;/code&gt; app by inserting a new row into the &lt;code&gt;ecs_simplified&lt;/code&gt; table. Seems pretty easy, right? But under the hood, it's creating all the necessary resources like load balancers, security groups, IAM roles, etc.&lt;/li&gt;
&lt;li&gt;You can manually check the tables to see what resources are being created. For example, looking at the &lt;code&gt;load_balancer&lt;/code&gt; table you'll see a load balancer named &lt;code&gt;simple-express-load-balancer&lt;/code&gt; is inserted automatically by running the above insert command.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;iasql_begin()&lt;/code&gt; and &lt;code&gt;iasql_commit()&lt;/code&gt; functions are IaSQL RPCs that are used to start and then end a transaction. We use those two functions to bundle changes to be pushed to the cloud immediately. If you don't wrap the changes in a transaction, they'll be applied to the cloud in an eventually-consistent way.&lt;/li&gt;
&lt;li&gt;For more info on the &lt;code&gt;iasql_begin()&lt;/code&gt; and &lt;code&gt;iasql_commit()&lt;/code&gt; commands, check &lt;a href="https://iasql.com/docs/transaction/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; on how it works.&lt;/li&gt;
&lt;li&gt;After the transaction is finished all the necessary resources are now created on the cloud, and their cloud-defined values are synced back to the database, so you can see the ARNs in the database. You can verify this by looking at different tables, eg. &lt;code&gt;iam_role&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To get your load balancer address, you can easily run &lt;code&gt;SELECT load_balancer_dns FROM ecs_simplified WHERE app_name = 'simple-express'&lt;/code&gt; query and get the URL to access your app.&lt;/li&gt;
&lt;li&gt;Now ECS is waiting for an image to be pushed to your ECR repository to run it. You can get the URI for the ECR repository by running the &lt;code&gt;SELECT repository_uri FROM ecs_simplified WHERE app_name = 'simple-express'&lt;/code&gt; query. You &lt;em&gt;could&lt;/em&gt; build your docker image locally and then follow &lt;a href="https://docs.aws.amazon.com/AmazonECR/latest/userguide/getting-started-cli.html" rel="noopener noreferrer"&gt;Steps 2 and 4 from this guide&lt;/a&gt; to connect your local docker CLI to your ECR repository and push that docker image into your ECR repository, but we have a simpler solution next.&lt;/li&gt;
&lt;li&gt;In the next step, we'll automatically build an image for the code in Github repo and then push it to this URI (all through SQL and using another high-level function named &lt;code&gt;ecr_build&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;ecr_build&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'https://github.com/iasql/iasql/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- the Github repo URL&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;id&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;
      &lt;span class="k"&gt;WHERE&lt;/span&gt;
        &lt;span class="n"&gt;repository_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'simple-express-repository'&lt;/span&gt;
    &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- ECR repo for the image to be pushed&lt;/span&gt;
    &lt;span class="s1"&gt;'./examples/ecs-fargate/prisma/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- the subdirectory in Github repo&lt;/span&gt;
    &lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- the Github branch or ref&lt;/span&gt;
    &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="c1"&gt;-- Github personal access token - can be omitted if public repository&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This command tells IaSQL to clone the &lt;code&gt;iasql&lt;/code&gt; repository, build an image on the subdirectory specified, and then push it to the ECR repository created earlier by the &lt;code&gt;aws_ecs_simplified&lt;/code&gt; module. Running the above command will automatically create a CodeBuild project and the related roles, etc. Then it'll start a build, and after it's successful all the created resources are deleted to ensure there won't be any additional charges to your AWS account.&lt;/li&gt;
&lt;li&gt;To access your app on the cloud, get the load balancer address and use your browser to access the live version of it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;load_balancer_dns&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;ecs_simplified&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;app_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'simple-express'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can check if the server is running on the &lt;code&gt;&amp;lt;load_balancer_dns value&amp;gt;:8088/health&lt;/code&gt; address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Low-level Access to Resources
&lt;/h2&gt;

&lt;p&gt;So the &lt;code&gt;aws_ecs_simplified&lt;/code&gt; module simplifies things, right? But what if you still need the level of control you had when you were doing all the steps manually? The traditional PaaS trade-off is that you can't grow your app beyond the built-in limitations as you don't have access to all the small details. The IaSQL approach is not limited in that way.&lt;/p&gt;

&lt;p&gt;Let's say you want your ECS container to be able to use the AWS CLI to provision an EC2 instance, and for that purpose its IAM role needs &lt;code&gt;AmazonEC2FullAccess&lt;/code&gt; policy to work properly. &lt;code&gt;aws_ecs_simplified&lt;/code&gt; does not have a column to configure such a thing, but that doesn't mean we're stuck.&lt;/p&gt;

&lt;p&gt;The good news is that you still have the full control over all resources in the deepest details. Let's fix your app's IAM role access by attaching the needed policy to its IAM role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;
  &lt;span class="n"&gt;iam_role&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="n"&gt;attached_policies_arns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attached_policies_arns&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'arn:aws:iam::aws:policy/AmazonEC2FullAccess'&lt;/span&gt; &lt;span class="c1"&gt;-- attached_policies_arns is of text[] type&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;role_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'simple-express-ecs-task-exec-role'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want additional rules for the container's security group? No problem! Just write the SQL and execute it, and it will be applied to the cloud within seconds. You want 3 copies of your container to be kept running with a round-robin load balancing on them? It's already there, just do an &lt;code&gt;UPDATE ecs_simplified SET desired_count = 3 WHERE app_name = 'simple_express';&lt;/code&gt; and it's there for you.&lt;/p&gt;

&lt;p&gt;With IaSQL and its flexibility, you can benefit from both the high-level and low-level operations. We have created the &lt;code&gt;aws_ecs_simplified&lt;/code&gt; module to show the flexibility and power of IaSQL, but the possibilities are endless. IaSQL is also an open-source project, meaning that you can use it to build your very own modules on top of it. If you're into the idea of empowering other developers to do complex infrastructure tasks simply, why don't you take a look at our &lt;a href="https://github.com/iasql/iasql/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;contributing guide&lt;/a&gt; and join our &lt;a href="https://discord.com/invite/machGGczea" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt;? We'll thoroughly answer any of your questions regarding the usage or development of IaSQL. Looking forward to seeing you in our small, but great community.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>python</category>
      <category>frontend</category>
      <category>career</category>
    </item>
    <item>
      <title>Save on AWS by deleting untagged ECR images</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Wed, 22 Feb 2023 19:50:41 +0000</pubDate>
      <link>https://dev.to/iasql/save-on-aws-by-deleting-untagged-ecr-images-2cao</link>
      <guid>https://dev.to/iasql/save-on-aws-by-deleting-untagged-ecr-images-2cao</guid>
      <description>&lt;p&gt;In this post, we are going to learn how untagged ECR images can rack up your AWS bill unnecessarily and how to get rid of unused repository images with a single query in &lt;a href="https://iasql.com?utm_source=devto&amp;amp;utm_campaign=ecrsave" rel="noopener noreferrer"&gt;IaSQL&lt;/a&gt;: &lt;code&gt;DELETE FROM repository_images WHERE tag = '&amp;lt;untagged&amp;gt;';&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Every time you want to deploy a new version of your code to an ECS container or EKS pod, you build and push a new docker image from your code into an ECR container repository. The latest image in an ECR repository is the only one in use. Stale images remain in the repository and accumulate over long periods by normal usage of ECS, or EKS, as there is typically no workflow to delete untagged images. However, there is a gotcha. AWS charges $0.10 per GB per month for images stored in private or public ECR repositories per the &lt;a href="https://aws.amazon.com/ecr/pricing/" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;. This doesn't sound like a lot, but for context, the size of the IaSQL docker image is around 2 GB. We have a CI job that deploys our staging environment every time we land a pull request to the main branch of our repository. This added up to 845 deploys over only a few months. Those 845 images, which have since been deleted, summed up to roughly 1700 GB which was $170 per month in our case. However, companies with lots of microservices on ECS or large docker containers can have many gigabytes of unused storage that can come out to hundreds or thousands of dollars per month of unnecessary AWS spend.&lt;/p&gt;

&lt;p&gt;How do you get rid of untagged ECR images though? Deleting hundreds, or thousands, of container images across ECR repositories is quite tedious through the AWS console, which involves multiple clicks per deleted image. There are a few other options. IaC tools require state file manipulation, or it is not possible at all as ECR images are not typically modeled in the infrastructure declaration as they are often generated via CI and are quite numerous. Cloud Query and Steampipe let you query your cloud and inspect your ECR images, but do not let you delete the images or any part of your cloud account for that matter as they are read-only. The most common solution is often a script that calls the AWS SDK or CLI directly. IaSQL lets you delete untagged images to eliminate unnecessary AWS costs using the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;iasql_install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_ecr'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;repository_images&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;untagged&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also possible to delete old images, or images past a certain size This query deletes unused images pushed to the repository before 2023 that are bigger than 10 GB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;repository_images&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;untagged&amp;gt;'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pushed_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'2023-01-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;size_in_mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a way to delete all &lt;em&gt;unused&lt;/em&gt; images if you are using ECR with ECS exclusively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;iasql_install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aws_ecs_fargate'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;repository_images&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing the &lt;code&gt;aws_ecs_fargate&lt;/code&gt; &lt;a href="https://iasql.com/docs/module?utm_source=devto&amp;amp;utm_campaign=ecrsave" rel="noopener noreferrer"&gt;module&lt;/a&gt;, which installs the dependant &lt;code&gt;aws_ecr&lt;/code&gt; module if it's not already installed, the schema relations between the AWS ECS service will not let you delete ECR images currently in use and will reinstate the ones that are currently active.&lt;/p&gt;

&lt;p&gt;Questions or issues with AWS? Join our &lt;a href="https://discord.com/invite/machGGczea" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt; to ask away, or just to let us know if you would like to see more AWS cost optimizations like this one.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>docker</category>
      <category>containers</category>
      <category>devops</category>
    </item>
    <item>
      <title>Infra as SQL is in beta</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Wed, 22 Feb 2023 01:16:13 +0000</pubDate>
      <link>https://dev.to/iasql/iasql-is-in-beta-pfc</link>
      <guid>https://dev.to/iasql/iasql-is-in-beta-pfc</guid>
      <description>&lt;p&gt;IaSQL lets developers manage their cloud infrastructure as data in PostgreSQL as an alternative to the AWS console and infrastructure as code (IaC) tools like Pulumi and Terraform. We open-sourced IaSQL's Alpha version (v0.0.x) in &lt;a href="https://iasql.com/blog/os-iasql?utm_source=devto&amp;amp;utm_campaign=beta" rel="noopener noreferrer"&gt;April 2022&lt;/a&gt;. We still have a long way to go, but we feel ready for what is next. Today, we’re moving IaSQL to Beta (v0.x)!&lt;/p&gt;

&lt;p&gt;We’ve been fortunate to work with lots of early adopters who helped us shape the product and prioritize the features to build. We have been quietly fixing bugs and adding goodies to IaSQL non-stop for the past few months. Some of the bugs have led to some difficult outages. We love open source and that includes airing our dirty laundry. We keep our postmortems &lt;a href="https://github.com/iasql/iasql/tree/main/postmortems" rel="noopener noreferrer"&gt;here&lt;/a&gt; in case you are curious.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Ffc588munoi7e5faskylb.gif" class="article-body-image-wrapper"&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%2Farticles%2Ffc588munoi7e5faskylb.gif" width="214" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, we completely redid our architecture to scale and our UX to make it more intuitive and just plain simpler. More on that later. Several hundred SQL queries and cloud resources have been created on top of IaSQL. Blood, sweat, and tears went into the Alpha phase. Okay, that may be an overdramatization, but it did take a lot of thoughtful hours from us to help you manage cloud infrastructure more seamlessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Ff52chpgc1r1nerr1pldz.gif" class="article-body-image-wrapper"&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%2Farticles%2Ff52chpgc1r1nerr1pldz.gif" width="498" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Alpha phase in numbers​
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;26 Alpha versions&lt;/li&gt;
&lt;li&gt;170 databases created&lt;/li&gt;
&lt;li&gt;199 GitHub stars&lt;/li&gt;
&lt;li&gt;11 GitHub contributors&lt;/li&gt;
&lt;li&gt;481 GitHub issues closed&lt;/li&gt;
&lt;li&gt;140,755 lines of code and markdown&lt;/li&gt;
&lt;li&gt;24 AWS services covered&lt;/li&gt;
&lt;li&gt;90 Discord members&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  New features
&lt;/h2&gt;

&lt;p&gt;Here are the new features that ship in the Beta versions of IaSQL:&lt;/p&gt;

&lt;h3&gt;
  
  
  🏡 Home is where your local env is
&lt;/h3&gt;

&lt;p&gt;We made IaSQL easier to run locally by bundling up our dashboard into the IaSQL docker container and publishing it to &lt;a href="https://hub.docker.com/r/iasql/iasql" rel="noopener noreferrer"&gt;Dockerhub&lt;/a&gt;. This makes IaSQL easier to try out without having your cloud credentials ever leave your local environment. It is as simple as running the command below and going to &lt;code&gt;http://localhost:9876&lt;/code&gt; on your preferred browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 9876:9876 &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 &lt;span class="nt"&gt;--name&lt;/span&gt; iasql iasql/iasql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎛️ AWS Multiregion
&lt;/h3&gt;

&lt;p&gt;Support for multiple AWS regions with default region behavior in part because we also hate changing regions in the AWS console. The default region is defined when connecting your database to your AWS account. Thereon, IaSQL's data model will assume the default data model unless you explicitly override it in the column that represents your cloud resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/iasql/iasql/blob/main/rfcs/003%20-%20Multi-Region%20Support%20RFC.md" rel="noopener noreferrer"&gt;Learn more about it in the RFC for this feature →&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🪄 Infrastructure changes as transactions, please
&lt;/h3&gt;

&lt;p&gt;We redid the UX to allow handling infrastructure changes automatically and for delicate, or complex, changes to your cloud account use an &lt;a href="https://dev.to/docs/transaction"&gt;IaSQL transaction&lt;/a&gt; akin to transactions in a regular database. Do you want to programmatically modify your infra or control plane? We got you!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/iasql/iasql/blob/main/rfcs/004%20-%20Continuous%20Two-Way%20Synchronization%20RFC.md" rel="noopener noreferrer"&gt;Learn more about it in the RFC for this feature →&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🎚️ Moar coverage of AWS services
&lt;/h3&gt;

&lt;p&gt;Increased AWS service coverage for EC2, CodeDeploy, CodeBuild, CodePipeline, SNS, ACM, Route53 amongst a few others. Additionally, we have a new &lt;code&gt;aws_sdk&lt;/code&gt; module that lets you invoke the AWS SDK directly using PostgreSQL functions with added type safety.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://iasql.com/docs/modules?utm_source=devto&amp;amp;utm_campaign=beta" rel="noopener noreferrer"&gt;See an up-to-date list of covered AWS services →&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  💨 Breeze through a simplified AWS
&lt;/h3&gt;

&lt;p&gt;AWS is well... complicated. Our modules let you create, update, and delete your cloud resources as relational tables with the configurability AWS provides, but sometimes those details are not relevant to what you are trying to accomplish. So we have developed simplified modules that focus on specific use cases. For instance, deploying a docker container to ECS and exposing it to the internet which is not just ECS but involves ECR, ECS, ACM, and Route53. These simplified modules are written in pure SQL on top of the existing IaSQL modules and are meant to abstract the complexity of coordinating multiple AWS services while still letting you peek under the hood when needed. Think of a simplified module as a PaaS hosted in your AWS account that is built on top of known AWS services but also lets you eject back into these AWS services if necessary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://iasql.com/blog/ecs-simplified?utm_source=devto&amp;amp;utm_campaign=beta" rel="noopener noreferrer"&gt;Learn more about simplified modules here →&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📈 Scale for what?
&lt;/h3&gt;

&lt;p&gt;Re-architected the product to scale the SaaS beyond a handful of users and allow automatic database version upgrades for &lt;a href="https://dev.to/docs/modules"&gt;modules&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/iasql/iasql/blob/main/rfcs/005%20-%20Unsurprising%20Functions%20and%20Scalability%20RFC.md" rel="noopener noreferrer"&gt;Learn more about it in the RFC for this feature →&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?​
&lt;/h2&gt;

&lt;p&gt;The next features are going to be about making IaSQL easier to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more &lt;a href="https://iasql.com/blog/tags/tutorial?utm_source=devto&amp;amp;utm_campaign=beta" rel="noopener noreferrer"&gt;examples&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SQL templates for common security vulnerabilities and cost optimizations in AWS&lt;/li&gt;
&lt;li&gt;continuously improving our documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Longer term, we’ll add support for 3rd party high-level modules, extensive support for AWS, and support for more cloud providers. If there is something in particular you would like to see please drop us a line on Discord or email via hello at our domain.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to stay in the loop? → &lt;a href="https://iasql.com/updates?utm_source=devto&amp;amp;utm_campaign=beta" rel="noopener noreferrer"&gt;Join our newsletter!&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>opensource</category>
      <category>cloud</category>
      <category>sql</category>
    </item>
    <item>
      <title>UPDATE iasql SET source = 'open';</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Thu, 14 Apr 2022 20:59:07 +0000</pubDate>
      <link>https://dev.to/iasql/update-iasql-set-source-open-4pip</link>
      <guid>https://dev.to/iasql/update-iasql-set-source-open-4pip</guid>
      <description>&lt;p&gt;We are excited to announce that &lt;a href="https://iasql.com" rel="noopener noreferrer"&gt;IaSQL&lt;/a&gt; is now open source! The main repository is under &lt;a href="https://github.com/iasql/iasql-engine" rel="noopener noreferrer"&gt;https://github.com/iasql/iasql-engine&lt;/a&gt;. As perfectionists, we feel like IaSQL will never be truly ready. However, we believe IaSQL is at the point where it can start to be useful for developers managing infrastructure in the cloud. IaSQL is a SaaS that lets you model your infrastructure as data by maintaining a 2-way connection between your AWS account and a Postgres SQL database to represent the definitive state (and status) of your cloud which cannot be achieved with a static infrastructure declaration. This means that when you connect an AWS account to an IaSQL instance it automatically backfills the database with your existing cloud resources. No need to painstakingly redefine or reconcile your existing infrastructure, and IaSQL's &lt;a href="https://docs.iasql.com/module/" rel="noopener noreferrer"&gt;module system&lt;/a&gt; means you can specify which parts of your cloud infrastructure you wish to control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fiasql.com%2Flib_TQbMwqDvYvWhOqVJ%2Fae7kf13uek0k6mul.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fiasql.com%2Flib_TQbMwqDvYvWhOqVJ%2Fae7kf13uek0k6mul.gif" alt="2-way connection" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IaSQL also makes it possible to express infrastructure changes as code that can be version controlled. This can be done with any &lt;a href="https://en.wikipedia.org/wiki/Schema_migration" rel="noopener noreferrer"&gt;migration system&lt;/a&gt; for schema and data changes, or in a script using &lt;a href="https://www.prisma.io/dataguide/postgresql/inserting-and-modifying-data/insert-on-conflict" rel="noopener noreferrer"&gt;idempotent SQL inserts&lt;/a&gt; more akin to &lt;a href="https://en.wikipedia.org/wiki/Infrastructure_as_code" rel="noopener noreferrer"&gt;IaC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PostgreSQL IaSQL databases can be provisioned and configured via our &lt;a href="https://app.iasql.com" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;. The dashboard calls the &lt;a href="https://github.com/iasql/iasql-engine" rel="noopener noreferrer"&gt;engine&lt;/a&gt; which is a Node.js server written in Typescript that provisions unmodified PostgreSQL instances loaded with tables representing AWS services controlled via the &lt;a href="https://www.npmjs.com/package/aws-sdk" rel="noopener noreferrer"&gt;AWS SDK&lt;/a&gt;. AWS is our main focus at the moment, but we plan to support GCP, Azure and other cloud providers soon. This is an &lt;a href="https://github.com/iasql/iasql-engine#cloud-providers" rel="noopener noreferrer"&gt;updated list&lt;/a&gt; of the AWS services that we currently support. Let us know if you need a specific AWS service and we should be able prioritize it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fiasql.com%2Flib_VymYUCjjDarpARbl%2F87bwoyulswfitjo5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fiasql.com%2Flib_VymYUCjjDarpARbl%2F87bwoyulswfitjo5.png" alt="dashboard" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to make it easier to write IaSQL modules, reduce waiting times when provisioning infrastructure, add more functionality to the existing AWS services, and so on. The list of things we want to build into IaSQL is long, but we want to do it in the open with your feedback and help. Drop us a line on &lt;a href="https://discord.com/invite/machGGczea" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Infrastructure as SQL</title>
      <dc:creator>Luis Fernando De Pombo</dc:creator>
      <pubDate>Wed, 15 Sep 2021 01:39:45 +0000</pubDate>
      <link>https://dev.to/iasql/infrastructure-as-sql-81i</link>
      <guid>https://dev.to/iasql/infrastructure-as-sql-81i</guid>
      <description>&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Since this post was originally written the schema of IaSQL has changed and the queries below no longer work. Please refer to the &lt;a href="https://iasql.com/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for how to use IaSQL. The IaSQL modules include example queries that are tested and kept up-to-date, including the &lt;a href="https://iasql.com/docs/modules/aws/aws_ec2/" rel="noopener noreferrer"&gt;aws_ec2 module&lt;/a&gt; that this post covers.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;What software you have deployed on what services and the interactions between them and the outside world is not a program, it is information about your infrastructure. Changing your infrastructure &lt;em&gt;is&lt;/em&gt; a set of operations to perform, a program. A SQL database is a set of information and SQL queries read or change that data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure State is Data, Infrastructure Change is Code. It's as simple as that.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And manipulating your infrastructure in this way is natural.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;aws_ec2&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ami_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec2_instance_type_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ait&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;amis&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;image_name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'amzn-ami-hvm-%'&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;creation_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ami&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;ait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'t2.micro'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Relations and Types Matter for Infrastructure&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Infrastructure as Code solutions do not have a good way of encoding dependencies across infrastructure pieces in a micro services architecture which makes it really hard to make and revert changes to infrastructure.&lt;/p&gt;

&lt;p&gt;Representing your infrastructure as SQL resolves the primary issue of YAML-based infrastructure tools by making the relations between pieces of your infrastructure first-class citizens, and enforcing type safety on the data and changes to it.&lt;/p&gt;

&lt;p&gt;You can't set the EC2 instance type as &lt;code&gt;t2.mucro&lt;/code&gt; and have your deploy system try and fail to create such an instance. The &lt;code&gt;insert&lt;/code&gt; statement will fail and tell you zero rows were inserted and you can quickly see why.&lt;/p&gt;

&lt;p&gt;Similarly, if you have a record in the &lt;code&gt;security_group&lt;/code&gt; table, you can't delete it if there are any references to it in the &lt;code&gt;ec2_security_groups&lt;/code&gt; join table. The relational structure of IaSQL prevents you from putting your infrastructure into an invalid state.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;New Powers: Explore, Query, and Automate Your Infrastructure&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Because your infrastructure is presented as a SQL database, you can connect to it with a SQL client of your choice and explore what you have and what the possibilities are.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can query for unusual usage patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_ec2&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ait&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;ait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2_instance_type_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vcpus&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vcpus&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And since it is a database, you can create your own tables with their own meaning and associate them with your infrastructure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;aws_ec2&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;company_team_ec2s&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cte&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;cte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_ec2_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;company_teams&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;company_team_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Data Engineering'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, your applications can know much more about what infrastructure they need than any auto-scaler solution out there. If you had a very infrequent but CPU/GPU-intensive job you need to handle at an unknown interval, you could give your application access to your IaSQL database and let it temporarily create and then destroy those resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ec2_instance_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;iasql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  INSERT INTO aws_ec2 (ami_id, ec2_instance_type_id)
  SELECT ami.id, ait.id
  FROM ec2_instance_type as ait, (
      SELECT id
      FROM amis
      WHERE image_name = 'application-job-runner'
  ) as ami
  WHERE ait.instance_name = 'g3.4xlarge'
  RETURNING id;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;iasql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  INSERT INTO ec2_security_groups (ec2_id, security_group_id)
  SELECT &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ec2_instance_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, sg.id
  FROM security_groups AS sg
  WHERE sg.name = 'application-job-group';
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Only large-enough job runners will take it based on job metadata&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myMassiveJob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;iasql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  DELETE FROM aws_ec2
  WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ec2_instance_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;You Don't Need to Learn a New API (Probably)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Nearly all cloud backend systems depend on a database, and most likely a SQL database, so you do not need to learn a new language to manipulate the infrastructure in this way.&lt;/p&gt;

&lt;p&gt;And likely you're using a &lt;a href="https://en.wikipedia.org/wiki/Schema_migration" rel="noopener noreferrer"&gt;migration system&lt;/a&gt; in your backend to review changes to your database, which you can continue to use here, making it code to be reviewed, just like Infrastructure-as-Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;You Can Test, Too&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Since the safety guarantees are provided by the types and relations between tables, you can simply copy your production infrastructure database into a local database and run your changes/migration against that and verify it works before you run it on your actual Infrastructure-as-SQL database.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Recover With Ease&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;It's 3AM and your service has gone down. You reverted the most recent IaSQL migration, but that didn't resolve the issue, and you aren't sure which change across which service today caused the outage. So, you simply replace the state of the IaSQL database with a snapshot from yesterday to bring everything back online to a known-good-state, and then take your time after you're well-rested to figure out what actually went wrong.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sign up for our waitlist &lt;a href="https://iasql.com" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>terraform</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
