{"id":3719,"date":"2022-07-05T18:50:00","date_gmt":"2022-07-05T18:50:00","guid":{"rendered":"https:\/\/blog.paranoidprofessor.com\/?p=3719"},"modified":"2022-07-04T20:52:25","modified_gmt":"2022-07-04T20:52:25","slug":"a-simplifed-terraform-and-aws-tutorial","status":"publish","type":"post","link":"https:\/\/blog.paranoidprofessor.com\/index.php\/2022\/07\/05\/a-simplifed-terraform-and-aws-tutorial\/","title":{"rendered":"A simplifed Terraform and AWS tutorial"},"content":{"rendered":"\n<p>A lot of tutorials and youtube videos about Terraform go through the process of downloading and installing Terraform.  This process is essentially to <a href=\"https:\/\/www.terraform.io\/downloads\" data-type=\"URL\" data-id=\"https:\/\/www.terraform.io\/downloads\" target=\"_blank\" rel=\"noreferrer noopener\">download a single executable<\/a> and make sure that this executable is in your path.  This process is rather well described and so I will not waste any time on this setup.<\/p>\n\n\n\n<p>If you are not somewhat familiar with Terraform it may be a surprise to learn Terraform is a command line program.  There is no GUI, just a few basic commands that can be used but the lack of a GUI is a strength not a weakness.  This makes it possible to use Terraform in batch processes.  One obvious and common process would be to add Terraform as part of a CI\/CD pipeline.<\/p>\n\n\n\n<p>Terraform as <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.paranoidprofessor.com\/index.php\/2022\/06\/06\/stone-age-to-cloud-age\/\" data-type=\"URL\" data-id=\"https:\/\/blog.paranoidprofessor.com\/index.php\/2022\/06\/06\/stone-age-to-cloud-age\/\" target=\"_blank\">previously mentioned<\/a> allows you to create scripts that will describe your desired infrastructure.  Running Terraform will compare your desired state against what is actually setup.  This middleware layer does slightly abstract you from the underlying platform.  It might not be totally clear from reading other articles but this abstraction doesn&#8217;t allow you to transparently and automatically change between AWS and Azure backend.  The scripts are rather specific to which underlying platform you are writing them for.  Yet, if you write your applications in a rather independent way, they can run on any cloud platform you choose.  <\/p>\n\n\n\n<p>This script actually doesn&#8217;t really create anything.  Terraform uses the AWS CLI tools and so it is important to have these credentials available.  The could be hard coded in a script, defined as a Terraform variable or even defined as an environment variable. <\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>provider &#8220;aws&#8221; {<br>    region = &#8220;eu-central-1&#8221;<br>    access_key = var.AWS_ACCESS_KEY_ID<br>    secret_key = var.AWS_SECRET_ACCESS_KEY<br>}<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>This example shows the access_key and secret_key being assigned from a variable.   These values are typically saved in the .aws directory of the users home directory.  The AWS CLI setup is not described here as there is plenty of <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.aws.amazon.com\/cli\/latest\/userguide\/cli-chap-configure.html\" data-type=\"URL\" data-id=\"https:\/\/docs.aws.amazon.com\/cli\/latest\/userguide\/cli-chap-configure.html\" target=\"_blank\">documentation<\/a> available from Amazon itself.<\/p>\n\n\n\n<p>The first time you run Terraform you may see the following message.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>&gt; terraform plan<\/strong><br>\u2577<br>\u2502 Error: Inconsistent dependency lock file<br>\u2502<br>\u2502 The following dependency selections recorded in the lock file are inconsistent with the current configuration:<br>\u2502 &#8211; provider registry.terraform.io\/hashicorp\/aws: required by this configuration but no version is selected<br>\u2502<br>\u2502 To make the initial dependency selections that will initialize the dependency lock file, run:<br>\u2502 terraform init<br>\u2575<\/td><\/tr><\/tbody><\/table><figcaption>Terraform error if not properly initialized<\/figcaption><\/figure>\n\n\n\n<p>This error is because Terraform either did not have the proper underlying provider defined or because the initialization using that provider was not done.  The important first step once the provider has been defined is to run the initialization command.<\/p>\n\n\n\n<p>        <strong>Terraform init<\/strong><\/p>\n\n\n\n<p>The initialization step will produce output similar to the following.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Initializing the backend\u2026<br>Initializing provider plugins\u2026<br>&#8211; Finding latest version of hashicorp\/aws\u2026<br>&#8211; Installing hashicorp\/aws v4.20.0\u2026<br>&#8211; Installed hashicorp\/aws v4.20.0 (signed by HashiCorp)<br><br>Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run &#8220;terraform init&#8221; in the future.<br><br>Terraform has been successfully initialized!<br><br>You may now begin working with Terraform. Try running &#8220;terraform plan&#8221; to see any changes that are required for your infrastructure. All Terraform commands should now work.<br>If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.<\/td><\/tr><\/tbody><\/table><figcaption>Output from Terraform initialize <\/figcaption><\/figure>\n\n\n\n<p><strong>First Terraform scripts<\/strong><\/p>\n\n\n\n<p>A lot of tutorials on Terraform start with a few scripts with all the values hard coded.  I have even seen a few where the AWS CLI credentials are hard coded.   These examples do explain quite a bit about Terraform but most do not go back and correct the credentials to a proper secure state.  I would rather start with a slightly more complicated solution that is secure from the beginning. <\/p>\n\n\n\n<p>In this tutorial there is a named development.tf where variables for a single environment can be defined.  The file will contain all setup for creating a development environment.  By separating the values for each environment into a separate file it is possible to have a single infrastructure setup defined which can be parameterized with differing infrastructure attributes.  This makes it trivial to have a test environment that is identical to production or perhaps to create multiple but slightly smaller development environments &#8211; one for each developer.<\/p>\n\n\n\n<p>When you separate out various infrastructure values as variables they can be passed to Terraform one by one but what is more common is to pass an entire file full of variables.<\/p>\n\n\n\n<p>        <strong>terraform plan -var-file=development.tfvars<\/strong><\/p>\n\n\n\n<p>The output of this command will show what, if any, changes will be performed.  If the standard output does not alway provide enough information but it is possible to add additional types of output.  This is an example with a few additional values being displayed in the output.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Changes to Outputs:<br>_environment = &#8220;development&#8221;<br>awskey = &#8220;AKI  YOURKEYHERE  PFZ9&#8221;<br>awssecret = &#8220;tnd  your secret would be here 26362383 DS3Wk9C&#8221;<br>region = &#8220;us-east-1&#8221;<br>You can apply this plan to save these new output values to the Terraform state, without changing any real<br>infrastructure.<br>\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<br>Note: You didn&#8217;t use the -out option to save this plan, so Terraform can&#8217;t guarantee to take exactly these actions if<br>you run &#8220;terraform apply&#8221; now.<\/td><\/tr><\/tbody><\/table><figcaption>Output from Terraform plan command<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>It is important to note, that setting up environment variables to hold the AWS key and AWS secret is super for security but displaying it like above is a totally bad idea in real life.  It was done this way simply to show off an example of how to display Terraform variables.<\/p>\n\n\n\n<p>Below is the example code.  This example doesn&#8217;t actually create any infrastructure but does show everything necessary for using the AWS key and secret information from the environment.  Yet, in order for this to work it is necessary to have two environment variables.<\/p>\n\n\n\n<p>TF_VAR_AWS_ACCESS_KEY_ID<br>TF_VAR_AWS_SECRET_ACCESS_KEY<\/p>\n\n\n\n<p>The TF_VAR is the key to allow Terraform to know which environment variables should be passed through to the scripts.<\/p>\n\n\n\n<p>The next blog post will use this as the basis to build actual infrastructure.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>main.tf<\/td><\/tr><tr><td>provider &#8220;aws&#8221; {<br>    region = var.region<br>    access_key = var.AWS_ACCESS_KEY_ID<br>    secret_key = var.AWS_SECRET_ACCESS_KEY<br>}<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>inputs.tf<\/td><\/tr><tr><td>variable &#8220;environment&#8221; {<br>    type = string<br>    description = &#8220;what env we are using&#8221;<br>    default = &#8220;Development&#8221;<br>}<br>variable &#8220;region&#8221; {<br>    type = string<br>    description = &#8220;in which region we will do all of our work&#8221;<br>    default = &#8220;eu-central-1&#8221;<br>}<br>variable &#8220;AWS_ACCESS_KEY_ID&#8221; {<br>    type = string<br>    description = &#8220;user access_key&#8221;<br>}<br>variable &#8220;AWS_SECRET_ACCESS_KEY&#8221; {<br>    type = string<br>    description = &#8220;user secret access key&#8221;<br>}<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>output.tf<\/td><\/tr><tr><td>output &#8220;region&#8221; {<br>    value = var.region<br>}<br>output &#8220;_environment&#8221; {<br>    value = var.environment<br>}<br>output &#8220;awskey&#8221; {<br>    value = var.AWS_ACCESS_KEY_ID<br>}<br>output &#8220;awssecret&#8221; {<br>    value = var.AWS_SECRET_ACCESS_KEY<br>}<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>development.tfvars<\/td><\/tr><tr><td>region = &#8220;us-east-1&#8221;<br>environment = &#8220;development&#8221;<\/td><\/tr><\/tbody><\/table><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>A lot of tutorials and youtube videos about Terraform go through the process of downloading and installing Terraform. This process is essentially to download a single executable and make sure that this executable is in your path. This process is &hellip; <a href=\"https:\/\/blog.paranoidprofessor.com\/index.php\/2022\/07\/05\/a-simplifed-terraform-and-aws-tutorial\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts\/3719"}],"collection":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/comments?post=3719"}],"version-history":[{"count":10,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts\/3719\/revisions"}],"predecessor-version":[{"id":3739,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/posts\/3719\/revisions\/3739"}],"wp:attachment":[{"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/media?parent=3719"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/categories?post=3719"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.paranoidprofessor.com\/index.php\/wp-json\/wp\/v2\/tags?post=3719"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}