{"id":5551,"date":"2019-09-18T11:35:16","date_gmt":"2019-09-18T15:35:16","guid":{"rendered":"\/db-blog\/?p=5551"},"modified":"2020-03-11T19:19:12","modified_gmt":"2020-03-11T23:19:12","slug":"aws-terraform-provision-a-lightsail-instance-using-infrastructure-as-code","status":"publish","type":"post","link":"https:\/\/droidbasement.com\/db-blog\/aws-terraform-provision-a-lightsail-instance-using-infrastructure-as-code\/","title":{"rendered":"AWS\/Terraform \u2013 Provision a Lightsail instance using Infrastructure as Code"},"content":{"rendered":"\n<p>AWS has introduced <a rel=\"noreferrer noopener\" aria-label=\"Lightsail (opens in a new tab)\" href=\"https:\/\/aws.amazon.com\/lightsail\/\" target=\"_blank\">Lightsail<\/a> to compete with Digital Ocean, Linode, etc. for an inexpensive VPS (Virtual Private Server) offering.<\/p>\n\n\n\n<p>In this article we will use <a href=\"https:\/\/www.terraform.io\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Terraform (opens in a new tab)\">Terraform<\/a> (Infrastructure as Code) to swiftly bring up an AWS Lightsail instance in us-east-1 on a static IP, add a DNS Zone 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> 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;Get started with Lightsail for free&#8217;<\/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>Upgrade the AWS CLI on your host:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ pip3 install awscli --upgrade --user &amp;&amp; chmod 754 ~\/.local\/bin\/aws<\/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.9\/terraform_0.12.9_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:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ unzip terraform_0.12.9_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\/myweb\/scripts &amp;&amp; cd terraform\/myweb<\/pre>\n\n\n\n<p>Add an IAM Policy to the container-admin group so it will have access to the Lightsail API:<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                 \"lightsail:GetRelationalDatabaseEvents\",\n                 \"lightsail:GetActiveNames\",\n                 \"lightsail:GetOperations\",\n                 \"lightsail:GetBlueprints\",\n                 \"lightsail:GetRelationalDatabaseMasterUserPassword\",\n                 \"lightsail:ExportSnapshot\",\n                 \"lightsail:UnpeerVpc\",\n                 \"lightsail:GetRelationalDatabaseLogEvents\",\n                 \"lightsail:GetRelationalDatabaseBlueprints\",\n                 \"lightsail:GetRelationalDatabaseBundles\",\n                 \"lightsail:CopySnapshot\",\n                 \"lightsail:GetRelationalDatabaseMetricData\",\n                 \"lightsail:PeerVpc\",\n                 \"lightsail:IsVpcPeered\",\n                 \"lightsail:UpdateRelationalDatabaseParameters\",\n                 \"lightsail:GetRegions\",\n                 \"lightsail:GetOperation\",\n                 \"lightsail:GetDisks\",\n                 \"lightsail:GetRelationalDatabaseParameters\",\n                 \"lightsail:GetBundles\",\n                 \"lightsail:GetRelationalDatabaseLogStreams\",\n                 \"lightsail:CreateKeyPair\",\n                 \"lightsail:ImportKeyPair\",\n                 \"lightsail:DeleteKeyPair\",\n                 \"lightsail:GetInstance\",\n                 \"lightsail:CreateInstances\",\n                 \"lightsail:DeleteInstance\",\n                 \"lightsail:GetDomains\",\n                 \"lightsail:GetDomain\",\n                 \"lightsail:CreateDomain\",\n                 \"lightsail:DeleteDomain\",\n                 \"lightsail:GetStaticIp\",\n                 \"lightsail:AllocateStaticIp\",\n                 \"lightsail:AttachStaticIp\",\n                 \"lightsail:DetachStaticIp\",\n                 \"lightsail:ReleaseStaticIp\"\n             ],\n             \"Resource\": \"*<em>\"         <\/em>\n         },\n         {\n             \"Effect\": \"Allow\",\n             \"Action\": \"lightsail:\",\n             \"Resource\": [\n                 \"arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:StaticIp\/*\",\n                 \"arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:ExportSnapshotRecord\/*\",\n                 \"arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:Instance\/*<em>\",<\/em>\n                 <em>\"<\/em>arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:CloudFormationStackRecord<em>\/<\/em>*\",\n                 \"arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:RelationalDatabaseSnapshot\/*<em>\",<\/em>\n                 <em>\"<\/em>arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:RelationalDatabase<em>\/<\/em>*\",\n                 \"arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:InstanceSnapshot\/*<em>\",<\/em>\n                 <em>\"<\/em>arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;<em>:<\/em>Domain<em>\/<\/em>*\",\n                 \"arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:LoadBalancer\/*<em>\",<\/em>\n                 <em>\"<\/em>arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:KeyPair<em>\/<\/em>*\",\n                 \"arn:aws:lightsail::&lt;AWS ACCOUNT ID&gt;:Disk\/*\"\n             ]\n         }\n     ]\n }<\/pre>\n\n\n\n<p>Review Policy -&gt;<\/p>\n\n\n\n<p>Name: AllowLightsail<br>Description: Allow access to Lightsail.<\/p>\n\n\n\n<p>Create Policy.<\/p>\n\n\n\n<p>Groups -&gt; container-admin -&gt; Attach Policy -&gt; Search for AllowLightsail -&gt; Attach Policy.<\/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>Import the public key to Lightsail:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ aws lightsail import-key-pair --key-pair-name myweb --public-key-base64 file:\/\/~\/.ssh\/myweb.pub<\/pre>\n\n\n\n<p>Set the version to greater then or equal to 2.0, 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:<\/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; EOF<\/pre>\n\n\n\n<p>Create a Lightsail DNS Zone of myweb.com, allocate a static IP for it, create a micro instance based off of Ubuntu 18_04, reference an extraneous file for user_data (run once script on Virtual Machine boot), tag it and attach the allocated Static IP to it:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; lightsail.tf\n&gt; resource \"aws_lightsail_domain\" \"myweb\" {\n&gt;   domain_name = \"myweb.com\"\n&gt; }\n&gt;\n&gt; resource \"aws_lightsail_static_ip\" \"myweb\" {\n&gt;   name = \"static-ip_myweb\"\n&gt; }\n&gt;\n&gt; resource \"aws_lightsail_instance\" \"myweb\" {\n&gt;   name                    = \"site_myweb\"\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           = \"myweb\"\n&gt;   user_data               = \"${data.template_file.init_script.rendered}\"\n&gt;\n&gt;   tags = {\n&gt;         Site = \"myweb.com\"\n&gt;     }\n&gt; }\n&gt;\n&gt; resource \"aws_lightsail_static_ip_attachment\" \"myweb\" {\n&gt;   static_ip_name = \"${aws_lightsail_static_ip.myweb.name}\"\n&gt;   instance_name  = \"${aws_lightsail_instance.myweb.name}\"\n&gt; }\n&gt; EOF<\/pre>\n\n\n\n<p>Output our allocated and attached static IP after creation:<\/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 = \"${aws_lightsail_static_ip.myweb.ip_address}\"\n&gt; }\n&gt; EOF<\/pre>\n\n\n\n<p>Create a template file to reference a run-once on boot user_data script:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ cat &lt;&lt; 'EOF' &gt; install.tf\n&gt; data \"template_file\" \"init_script\" {\n&gt;   template = \"${file(\"scripts\/install.sh\")}\"\n&gt; }\n&gt; EOF<\/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 | sudo 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<\/pre>\n\n\n\n<p>Provision:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ terraform apply -auto-approve<\/pre>\n\n\n\n<p>Log on to the instance (up to ~30 seconds may be needed for the attachment of the static IP to the instance):<\/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 -destroy <\/pre>\n\n\n\n<p>Tear down the instance:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ terraform destroy -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>AWS has introduced Lightsail to compete with Digital Ocean, Linode, etc. for an inexpensive VPS (Virtual Private Server) offering. In this article we will use Terraform (Infrastructure as Code) to swiftly bring up an AWS Lightsail instance in us-east-1 on a static IP, add a DNS Zone for the site in mention and install docker\/docker-compose [&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-5551","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\/5551","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=5551"}],"version-history":[{"count":55,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/posts\/5551\/revisions"}],"predecessor-version":[{"id":6213,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/posts\/5551\/revisions\/6213"}],"wp:attachment":[{"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/media?parent=5551"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/categories?post=5551"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/droidbasement.com\/db-blog\/wp-json\/wp\/v2\/tags?post=5551"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}