{"id":5978,"date":"2020-02-29T20:47:12","date_gmt":"2020-03-01T01:47:12","guid":{"rendered":"\/db-blog\/?p=5978"},"modified":"2020-03-12T10:30:24","modified_gmt":"2020-03-12T14:30:24","slug":"aws-terraform-provision-an-ec2-instance-using-infrastructure-as-code","status":"publish","type":"post","link":"https:\/\/droidbasement.com\/db-blog\/aws-terraform-provision-an-ec2-instance-using-infrastructure-as-code\/","title":{"rendered":"AWS\/Terraform \u2013 Provision an EC2 instance using Infrastructure as Code"},"content":{"rendered":"\n<p><em>Note: Some of this is a duplicate of the <a rel=\"noreferrer noopener\" aria-label=\"AWS Lightsail (opens in a new tab)\" href=\"https:\/\/droidbasement.com\/db-blog\/aws-terraform-provision-a-lightsail-instance-using-infrastructure-as-code\/\" target=\"_blank\">AWS Lightsail<\/a> article, modified for EC2, a recent Terraform 0.12.x version and accommodates the previous Lightsail implementation.  If you would like to use Lightsail, please follow the IAM specific instructions in that article.<\/em><\/p>\n\n\n\n<p><a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/aws.amazon.com\/ec2\/\" target=\"_blank\">EC2<\/a> is the compute service in AWS.  It is flexible, adaptable, scalable and is able to run Virtual Machine workloads to fit most every need.<\/p>\n\n\n\n<p>In this article we will use <a rel=\"noreferrer noopener\" aria-label=\"Terraform (opens in a new tab)\" href=\"https:\/\/www.terraform.io\/\" target=\"_blank\">Terraform<\/a> (Infrastructure as Code) to swiftly bring up an AWS EC2 instance in us-east-1 on a static IP (Elastic IP), in a new VPC with an Internet Gateway, add a DNS Zone (Route 53)  for the site in mention and install docker\/docker-compose on it.<\/p>\n\n\n\n<p>We will use &#8216;myweb&#8217; as an example in this article, using the same base path of  &#8216;dev&#8217; that was <a rel=\"noreferrer noopener\" aria-label=\"previously created (opens in a new tab)\" href=\"https:\/\/droidbasement.com\/db-blog\/?p=5346\" target=\"_blank\">previously created<\/a>, the <a rel=\"noreferrer noopener\" aria-label=\"container-admin group (opens in a new tab)\" href=\"https:\/\/droidbasement.com\/db-blog\/?p=5501\" target=\"_blank\">container-admin group<\/a> (some of the IAM policy implemented there will be in use here) and using ~\/.local\/bin for the binary.<\/p>\n\n\n\n<p>Please use <a rel=\"noreferrer noopener\" aria-label=\"'Get started with Lightsail for free' (opens in a new tab)\" href=\"https:\/\/portal.aws.amazon.com\/billing\/signup?client=lightsail&amp;fid=1A3F6B376ECAC516-2C15C39C5ACECACB&amp;redirect_url=https%3A%2F%2Flightsail.aws.amazon.com%2Fls%2Fsignup#\/start\" target=\"_blank\">&#8216;<\/a><a rel=\"noreferrer noopener\" aria-label=\"Get started with Lightsail for free (opens in a new tab)\" href=\"https:\/\/aws.amazon.com\/free\/?all-free-tier.sort-by=item.additionalFields.SortRank&amp;all-free-tier.sort-order=asc\" target=\"_blank\">AWS Free Tie<\/a><a href=\"https:\/\/aws.amazon.com\/free\/?all-free-tier.sort-by=item.additionalFields.SortRank&amp;all-free-tier.sort-order=asc\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Get started with Lightsail for free (opens in a new tab)\">r<\/a><a rel=\"noreferrer noopener\" aria-label=\"'Get started with Lightsail for free' (opens in a new tab)\" href=\"https:\/\/portal.aws.amazon.com\/billing\/signup?client=lightsail&amp;fid=1A3F6B376ECAC516-2C15C39C5ACECACB&amp;redirect_url=https%3A%2F%2Flightsail.aws.amazon.com%2Fls%2Fsignup#\/start\" target=\"_blank\">&#8216;<\/a> prior to commencing with this article.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>&#8211;&gt;<br>Go in to the dev directory\/link located within your home directory:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cd ~\/dev<\/pre>\n\n\n\n<p>Grab Terraform:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ wget https:\/\/releases.hashicorp.com\/terraform\/0.12.21\/terraform_0.12.21_linux_amd64.zip<\/pre>\n\n\n\n<p>Install Unzip if you do not have it installed:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ sudo apt update &amp;&amp; sudo apt -y install unzip<\/pre>\n\n\n\n<p>Unzip it to ~\/.local\/bin and set permissions accordingly on it (type y and hit enter to replace if upgrading, at the prompt):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ unzip terraform_0.12.21_linux_amd64.zip -d ~\/.local\/bin &amp;&amp; chmod 754 ~\/.local\/bin\/terraform<\/pre>\n\n\n\n<p>Create a work folder and change in to it:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ mkdir -p terraform\/aws\/myweb\/scripts &amp;&amp; cd terraform\/aws\/myweb<\/pre>\n\n\n\n<p>Add an IAM Policy to the container-admin group so it will have access to EC2 and related (EIP\/VPC\/Routes\/IGW\/Route 53\/SG\/KeyPair):<br>AWS UI Console -&gt; Services -&gt; Security, Identity, &amp; Compliance -&gt; IAM -&gt; Policies -&gt; Create Policy -&gt; JSON (replace &lt;AWS ACCOUNT ID&gt; in the Resource arn with your Account\u2019s ID (shown under the top right drop-down (of your name) within the My Account page next to the Account Id: under Account Settings)):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> {\n     \"Version\": \"2012-10-17\",\n     \"Statement\": [\n         {\n             \"Effect\": \"Allow\",\n             \"Action\": [\n                 \"ec2:TerminateInstances\",\n                 \"route53:GetChange\",\n                 \"route53:GetHostedZone\",\n                 \"route53:ChangeTagsForResource\",\n                 \"route53:DeleteHostedZone\",\n                 \"route53:ListTagsForResource\" \n             ],\n             \"Resource\": [\n                \"arn:aws:ec2:*:&lt;AWS ACCOUNT ID&gt;:instance\/*\",\n                \"arn:aws:route53:::hostedzone\/*\",\n                \"arn:aws:route53:::change\/*\"\n             ]<em>      <\/em>\n         },\n         {\n             \"Effect\": \"Allow\",\n             \"Action\": [\n                 \"ec2:DisassociateAddress\",\n                 \"ec2:DeleteSubnet\",\n                 \"ec2:DescribeAddresses\",\n                 \"ec2:DescribeInstances\",\n                 \"ec2:DescribeInstanceAttribute\",\n                 \"ec2:CreateVpc\",\n                 \"ec2:AttachInternetGateway\",\n                 \"ec2:DescribeVpcAttribute\",\n                 \"ec2:AssociateRouteTable\",\n                 \"ec2:DescribeInternetGateways\",\n                 \"ec2:DescribeNetworkInterfaces\",\n                 \"ec2:CreateInternetGateway\",\n                 \"ec2:CreateSecurityGroup\",\n                 \"ec2:DescribeVolumes\",\n                 \"ec2:DescribeAccountAttributes\",\n                 \"ec2:ModifyVpcAttribute\",\n                 \"ec2:DescribeKeyPairs\",\n                 \"ec2:DescribeNetworkAcls\",\n                 \"ec2:DescribeRouteTables\",\n                 \"ec2:ReleaseAddress\",\n                 \"ec2:ImportKeyPair\",\n                 \"ec2:DescribeTags\",\n                 \"ec2:DescribeVpcClassicLinkDnsSupport\",\n                 \"ec2:CreateRouteTable\",\n                 \"ec2:DetachInternetGateway\",\n                 \"ec2:DisassociateRouteTable\",\n                 \"ec2:AllocateAddress\",\n                 \"ec2:DescribeInstanceCreditSpecifications\",\n                 \"ec2:DescribeSecurityGroups\",\n                 \"ec2:DescribeVpcClassicLink\",\n                 \"ec2:DescribeImages\",\n                 \"ec2:DescribeVpcs\",\n                 \"ec2:DeleteVpc\",\n                 \"ec2:AssociateAddress\",\n                 \"ec2:CreateSubnet\",\n                 \"ec2:DescribeSubnets\",\n                 \"ec2:DeleteKeyPair\",\n                 \"route53:CreateHostedZone\",\n                 \"sts:GetCallerIdentity\"\n             ],\n             \"Resource\": \"*\"\n         }\n     ]\n }<\/pre>\n\n\n\n<p>Review Policy -&gt;<\/p>\n\n\n\n<p>Name: AllowEC2<br>Description: Allow access to EC2 and related.<\/p>\n\n\n\n<p>Create Policy.<\/p>\n\n\n\n<p>Groups -&gt; container-admin -&gt; Attach Policy -&gt; Search for AllowEC2 -&gt; Attach Policy.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>&#8211;&gt;<br>Note: If you are not using Lightsail then you can disregard this section.<\/p>\n\n\n\n<p>Edit the IAM Policy &#8220;AllowLightsail&#8221; to add an allowance to GetKeyPair in Lightsail:<br>AWS UI Console -&gt; Services -&gt; Security, Identity, &amp; Compliance -&gt; IAM -&gt; Policies -&gt; AllowLightsail -&gt; Edit Policy -&gt; JSON -&gt; <\/p>\n\n\n\n<p>Append lightsail:GetKeyPair after lightsail:DeleteKeyPair and before lightsail:GetInstance.<\/p>\n\n\n\n<p>It will look like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">                  \"lightsail:DeleteKeyPair\",\n                  \"lightsail:GetKeyPair\",\n                  \"lightsail:GetInstance\",<\/pre>\n\n\n\n<p>Review Policy -&gt; Save Changes<br>&lt;&#8211;<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Generate an SSH Key Pair (no password) and restrict permissions on it:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ ssh-keygen -q -t rsa -b 2048 -N '' -f ~\/.ssh\/myweb &amp;&amp; chmod 400 ~\/.ssh\/myweb<\/pre>\n\n\n\n<p>Pin the Terraform version to greater then or equal to 0.12:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; versions.tf\n&gt; terraform {\n&gt;   required_version = \"&gt;= 0.12\"\n&gt; }\n&gt; EOF<\/pre>\n\n\n\n<p>Set the version to greater then or equal to 2.0 for the AWS provider, interpolate the region and use the AWS CLI credentials file:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; provider.tf\n&gt; provider \"aws\" {\n&gt;   version                 = \"&gt;= 2.0\"\n&gt;\n&gt;   region                  = var.region\n&gt;   shared_credentials_file = \"~\/.aws\/credentials\"\n&gt;   profile                 = \"default\"\n&gt; }\n&gt; EOF<\/pre>\n\n\n\n<p>Set the default region as a variable, set prefix of myweb and set lightsail by default to true:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; vars.tf\n&gt; variable \"region\" {\n&gt;   default = \"us-east-1\"\n&gt; }\n&gt;\n&gt; variable \"prefix\" {\n&gt;   default = \"myweb\"\n&gt; }\n&gt;\n&gt; variable \"lightsail\" {\n&gt;   default = true\n&gt; }\n&gt; EOF<\/pre>\n\n\n\n<p>While we are here, let us create a new Lightsail script\/code (if you have completed the previous AWS\/Terraform against Lightsail article, then please overwrite).  This will execute if no override (&#8216;lightsail = false&#8217;) is passed.  This also adds our public key as authorized (as oppose to uploading it manually as in the previous article):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; lightsail.tf\n&gt; # Create a DNS Zone\n&gt; resource \"aws_lightsail_domain\" \"myweb\" {\n&gt;   count       = var.lightsail ? 1 : 0\n&gt;   domain_name = \"${var.prefix}.com\"\n&gt; }\n&gt;\n&gt; # Allocate a Static (Public) IP\n&gt; resource \"aws_lightsail_static_ip\" \"myweb\" {\n&gt;   count = var.lightsail ? 1 : 0\n&gt;   name  = \"static-ip_${var.prefix}\"\n&gt; }\n&gt;\n&gt; # Add Public Key as authorized\n&gt; resource \"aws_lightsail_key_pair\" \"myweb\" {\n&gt;   count      = var.lightsail ? 1 : 0\n&gt;   name       = var.prefix\n&gt;   public_key = file(\"~\/.ssh\/${var.prefix}.pub\")\n&gt; }\n&gt;\n&gt; # Create an Ubuntu Virtual Machine with key based access and run a script on boot\n&gt; resource \"aws_lightsail_instance\" \"myweb\" {\n&gt;   count             = var.lightsail ? 1 : 0\n&gt;   name              = \"site_${var.prefix}\"\n&gt;   availability_zone = \"${var.region}a\"\n&gt;   blueprint_id      = \"ubuntu_18_04\"\n&gt;   bundle_id         = \"micro_2_0\"\n&gt;   key_pair_name     = var.prefix\n&gt;   user_data         = file(\"scripts\/install.sh\")\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Attach the Static (Public) IP\n&gt; resource \"aws_lightsail_static_ip_attachment\" \"myweb\" {\n&gt;   count          = var.lightsail ? 1 : 0\n&gt;   static_ip_name = element(aws_lightsail_static_ip.myweb[*].name, 0)\n&gt;   instance_name  = element(aws_lightsail_instance.myweb[*].name, 0)\n&gt; }\n&gt; EOF<\/pre>\n\n\n\n<p>Note: The below adds a conditional to accommodate the previous implementation against Lightsail.  It will get executed when &#8216;lightsail = false&#8217; is passed.<\/p>\n\n\n\n<p>The following is performed with this script\/code:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>create a Route 53 DNS Zone of myweb.com (no A records will be added)<\/li><li>create a Virtual Private Cloud for network 10.0.0.0\/16 (tenancy is default)<\/li><li>add a subnet of 10.0.1.0\/24 within the VPC<\/li><li>allocate a static Public IP<\/li><li>create a Security Group and add a Security rule for allowing SSH (port 22) Inbound<\/li><li>create an Internet Gateway and add a route out to it<\/li><li>create a T3a.micro instance (tenancy is default) based off of Ubuntu 18_04, our public key added as authorized and reference an extraneous file for user_data (initialization script on Virtual Machine boot).  Elastic\/Root Block Store is GP2<\/li><li>DNS support is enabled but DNS host names is not<\/li><li>tag all resources<\/li><\/ul>\n\n\n\n<p>Note: vpc_security_group_ids is used as oppose to security_groups, as the latter would cause a destroy\/create of the instance every time an apply is performed:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; ec2.tf\n&gt; # Create a DNS Zone \n&gt; resource \"aws_route53_zone\" \"myweb\" {\n&gt;   count   = var.lightsail ? 0 : 1\n&gt;   name    = \"${var.prefix}.com\"\n&gt;   comment = \"${var.prefix}.com (Public)\"\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"${var.prefix}-dn\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Create a Security Group and allow inbound port(s)\n&gt; resource \"aws_security_group\" \"myweb\" {\n&gt;   count       = var.lightsail ? 0 : 1\n&gt;   name        = var.prefix\n&gt;   description = \"Allow Ports\"\n&gt;   vpc_id      = element(aws_vpc.myweb[*].id, 0)\n&gt;\n&gt;   ingress {\n&gt;        from_port   = 22\n&gt;        to_port     = 22\n&gt;        protocol    = \"tcp\"\n&gt;        cidr_blocks = [\"0.0.0.0\/0\"]\n&gt;        description = \"SSH\"\n&gt;     }\n&gt;\n&gt;   egress {\n&gt;        from_port   = 0\n&gt;        to_port     = 0\n&gt;        protocol    = \"-1\"\n&gt;        cidr_blocks = [\"0.0.0.0\/0\"]\n&gt;        description = \"All\"\n&gt;     }\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"${var.prefix}-sg\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Create a Virtual Private Cloud\n&gt; resource \"aws_vpc\" \"myweb\" {\n&gt;   count            = var.lightsail ? 0 : 1\n&gt;   cidr_block       = \"10.0.0.0\/16\"\n&gt;   instance_tenancy = \"default\"\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"${var.prefix}-vpc\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Add a Subnet\n&gt; resource \"aws_subnet\" \"internal\" {\n&gt;   count             = var.lightsail ? 0 : 1\n&gt;   vpc_id            = element(aws_vpc.myweb[*].id, 0)\n&gt;   cidr_block        = \"10.0.1.0\/24\"\n&gt;   availability_zone = \"${var.region}a\"\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"internal\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Create an Internet Gateway\n&gt; resource \"aws_internet_gateway\" \"myweb\" {\n&gt;   count  = var.lightsail ? 0 : 1\n&gt;   vpc_id = element(aws_vpc.myweb[*].id, 0)\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"${var.prefix}-igw\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Allocate a Static Public IP\n&gt; resource \"aws_eip\" \"external\" {\n&gt;   count             = var.lightsail ? 0 : 1\n&gt;   vpc               = true\n&gt;   instance          = element(aws_instance.myweb[*].id, 0)\n&gt;   depends_on        = [aws_internet_gateway.myweb]\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"external\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Add a route to the Internet Gateway\n&gt; resource \"aws_route_table\" \"myweb\" {\n&gt;   count  = var.lightsail ? 0 : 1\n&gt;   vpc_id = element(aws_vpc.myweb[*].id<em>,<\/em> 0)\n&gt;\n&gt;   route {\n&gt;        cidr_block = \"0.0.0.0\/0\"\n&gt;        gateway_id =<em> <\/em>element(aws_internet_gateway.myweb[*].id, 0)\n&gt;     }\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"${var.prefix}-rt\"\n&gt;     }\n&gt; }\n&gt;\n&gt; # Associate the route table with the Subnet\n&gt; resource \"aws_route_table_association\" \"myweb\" {\n&gt;   count          = var.lightsail ? 0 : 1\n&gt;   subnet_id      = element(aws_subnet.internal[*].id, 0)\n&gt;   route_table_id = element(aws_route_table.myweb[*].id, 0)\n&gt; }\n&gt;\n&gt; # Add Public Key as authorized\n&gt; resource \"aws_key_pair\" \"myweb\" {\n&gt;   count      = var.lightsail ? 0 : 1\n&gt;   key_name   = var.prefix\n&gt;   public_key = file(\"~\/.ssh\/${var.prefix}.pub\")\n&gt; }\n&gt;\n&gt; # Select Ubuntu 18.04\n&gt; data \"aws_ami\" \"ubuntu\" {\n&gt;   count       = var.lightsail ? 0 : 1\n&gt;   most_recent = true\n&gt;\n&gt;   filter {\n&gt;         name   = \"name\"\n&gt;         values = [\"ubuntu\/images\/hvm-ssd\/ubuntu-bionic-18.04-amd64-server-*\"]\n&gt;     }\n&gt;\n&gt;   filter {\n&gt;         name   = \"virtualization-type\"\n&gt;         values = [\"hvm\"]\n&gt;     }\n&gt;\n&gt;   owners = [\"099720109477\"] # Canonical\n&gt; }\n&gt;\n&gt; # Create an Ubuntu Virtual Machine with key based access and run a script on boot\n&gt; resource \"aws_instance\" \"myweb\" {\n&gt;   count                    = var.lightsail ? 0 : 1\n&gt;   ami                      = element(data.aws_ami.ubuntu[*].id, 0)\n<em>&gt;   <\/em>instance_type            = \"t3a.micro\"\n&gt;   availability_zone        = \"${var.region}a\"\n&gt;   key_name                 = var.prefix\n&gt;   vpc_security_group_ids   = [element(concat(aws_security_group.myweb[*].id, list(\"\")), 0)]\n&gt;   user_data                = file(\"scripts\/install.sh\")\n&gt;   subnet_id                = element(aws_subnet.internal[*].id, 0)\n&gt;   tenancy                  = \"default\"\n&gt;\n&gt;   tags = {\n&gt;         Site = \"${var.prefix}.com\"\n&gt;         Name = \"${var.prefix}-ec2\"\n&gt;     }\n&gt; } \n&gt; EOF<\/pre>\n\n\n\n<p>Output our allocated and attached static Public IP after creation.  Also output an inventory file to the Ansible workarea for later consumption and accommodate our previous Lightsail implementation:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; output.tf\n&gt; output \"static_public_ip\" {\n&gt;   value = var.lightsail ? element(aws_lightsail_static_ip.myweb[*].ip_address, 0) : element(aws_eip.external[*].public_ip, 0)\n&gt; }\n&gt;\n&gt; resource \"local_file\" \"hosts\" {\n&gt;   content              = \"[vps]\\n${var.lightsail ? element(aws_lightsail_static_ip.myweb[*].ip_address, 0) : element(aws_eip.external[*].public_ip, 0)} ansible_connection=ssh ansible_user=ubuntu ansible_ssh_private_key_file=~\/.ssh\/${var.prefix} instance=${var.lightsail ? element(aws_lightsail_instance.myweb[*].name, 0) : element(aws_instance.myweb[*].tags[\"Name\"], 0)}\"\n&gt;   filename             = pathexpand(\"~\/dev\/ansible\/hosts-aws\")\n&gt;   directory_permission = 0754\n&gt;   file_permission      = 0664\n&gt; }\n&gt; EOF<\/pre>\n\n\n\n<p>If you have gone through the AWS\/Terraform against Lightsail article, then please delete the template file for the install script:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ rm install.tf<\/pre>\n\n\n\n<p>Create the shell script for user_data:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; scripts\/install.sh\n&gt; #!\/bin\/bash\n&gt;\n&gt; MY_HOME=\"\/home\/ubuntu\"\n&gt; export DEBIAN_FRONTEND=noninteractive\n&gt;\n&gt; # Install prereqs\n&gt; apt update\n&gt; apt install -y python3-pip apt-transport-https ca-certificates curl software-properties-common\n&gt; # Install docker\n&gt; curl -fsSL https:\/\/download.docker.com\/linux\/ubuntu\/gpg | apt-key add -\n&gt; add-apt-repository \"deb [arch=amd64] https:\/\/download.docker.com\/linux\/ubuntu $(lsb_release -cs) stable\"\n&gt; apt update\n&gt; apt install -y docker-ce\n&gt; # Install docker-compose\n&gt; su ubuntu -c \"mkdir -p $MY_HOME\/.local\/bin\" \n&gt; su ubuntu -c \"pip3 install docker-compose --upgrade --user &amp;&amp; chmod 754 $MY_HOME\/.local\/bin\/docker-compose\"\n&gt; usermod -aG docker ubuntu\n&gt; # Add PATH\n&gt; printf \"\\nexport PATH=\\$PATH:$MY_HOME\/.local\/bin\\n\" &gt;&gt; $MY_HOME\/.bashrc\n&gt;\n&gt; exit 0\n&gt; EOF<\/pre>\n\n\n\n<p>Initialize the directory:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ terraform init<\/pre>\n\n\n\n<p>Run a dry-run to see what will occur:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ terraform plan -var 'lightsail=false'<\/pre>\n\n\n\n<p>Provision:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ terraform apply -var 'lightsail=false' -auto-approve<\/pre>\n\n\n\n<p>Log on to the instance after a short while:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ ssh -i ~\/.ssh\/myweb ubuntu@&lt;The value of static_public_ip that was reported.  One can also use 'terraform output static_public_ip' to print it again.&gt;<\/pre>\n\n\n\n<p>Type yes and hit enter to accept.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>On the host (a short while is needed for the run-once script to complete):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ docker --version\n$ docker-compose --version\n$ logout<\/pre>\n\n\n\n<p>Tear down what was created by first performing a dry-run to see what will occur:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ terraform plan -var 'lightsail=false' -destroy <\/pre>\n\n\n\n<p>Tear down the instance:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ terraform destroy -var 'lightsail=false' -auto-approve<\/pre>\n\n\n\n<p>&lt;&#8211;<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>References:<\/p>\n\n\n\n<p>Source: <br><a href=\"https:\/\/github.com\/pershoot\/terraform_aws_myweb\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"terraform_aws_myweb (opens in a new tab)\">terraform_aws_myweb<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Note: Some of this is a duplicate of the AWS Lightsail article, modified for EC2, a recent Terraform 0.12.x version and accommodates the previous Lightsail implementation. If you would like to use Lightsail, please follow the IAM specific instructions in that article. EC2 is the compute service in AWS. It is flexible, adaptable, scalable and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-5978","post","type-post","status-publish","format-standard","hentry","category-devops"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/posts\/5978","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/comments?post=5978"}],"version-history":[{"count":105,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/posts\/5978\/revisions"}],"predecessor-version":[{"id":6243,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/posts\/5978\/revisions\/6243"}],"wp:attachment":[{"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/media?parent=5978"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/categories?post=5978"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/tags?post=5978"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}