{"id":2215,"date":"2019-08-27T08:03:21","date_gmt":"2019-08-27T05:03:21","guid":{"rendered":"https:\/\/upcloud.com\/global\/us\/resources\/tutorials\/automate-terraform-deployments\/"},"modified":"2019-08-27T08:03:21","modified_gmt":"2019-08-27T05:03:21","slug":"automate-terraform-deployments","status":"publish","type":"tutorial","link":"https:\/\/upcloud.com\/global\/resources\/tutorials\/automate-terraform-deployments\/","title":{"rendered":"How to automate Terraform deployments"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Maintaining <a href=\"https:\/\/upcloud.com\/global\/products\/cloud-servers\">cloud infrastructure<\/a> of any substantial size can often require you to perform the same actions or processes over and over. While some might enjoy the routine, there is however another way. Automating repetitive and reactionary tasks is the perfect solution to saving time and guaranteeing results, and what better way to manage this than using <a href=\"https:\/\/upcloud.com\/global\/blog\/upcloud-verified-terraform-provider\/\">Terraform<\/a>!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/terraform-logo-1-3.png\" alt=\"-\" class=\"wp-image-9088\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">In this guide, we\u2019ll look into what you need to consider when automating infrastructure deployments using Terraform.&nbsp;If you haven\u2019t yet set up Terraform with the UpCloud module on your computer, head over to our <a href=\"https:\/\/upcloud.com\/global\/resources\/tutorials\/get-started-terraform\" target=\"_blank\" rel=\"noreferrer noopener\">starters guide<\/a> first to get going.<\/p>\n\n\n\n\n\n<h2 class=\"wp-block-heading\">Creating a new configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Automating tasks like Terraform deployments is a great way, for example, to create reactive scalability for a load-balanced website. If you expect to be deploying similar setups on multiple occasions, it\u2019ll be worth templating the configuration from the start.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, make a new directory for the template configuration.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">mkdir -p ~\/terraform\/template &amp;&amp; cd&nbsp;~\/terraform\/template<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next, let\u2019s create the template itself. Open a new file in your text editor, for example, using nano as below.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">nano template.tf<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then add a following basic config as a starting point. The example here already uses variables for most fields, we\u2019ll fill these in later.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">provider \"upcloud\" {\n  # export UPCLOUD_USERNAME=\"Username for Upcloud API user\"\n  # export UPCLOUD_PASSWORD=\"Password for Upcloud API user\"\n}\n\nresource \"upcloud_server\" \"template\" {\n  hostname = var.hostname\n  zone     = var.zones[var.zone]\n  plan     = var.plans[var.plan]\n\n  network_interface {\n    type = \"public\"\n  }\n  template {\n    size    = lookup(var.storages, var.plans[var.plan])\n    storage = var.templates[var.template]\n  }\n  \n  login {\n    user = \"root\"\n    keys = [\n      var.public_key,\n    ]\n  }\n  connection {\n    host        = self.network_interface[0].ip_address\n    type        = \"ssh\"\n    user        = \"root\"\n    private_key = file(var.private_key_path)\n  }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Make sure to include your <a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/use-ssh-keys-authentication\/\" target=\"_blank\" rel=\"noreferrer noopener\">SSH keys<\/a>, the public key in the login section, and the private key file path in the connection. These are needed for automation in order to avoid password authentication.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With a simple Terraform template created, you should initialize the working directory so that we can make a test deployment further down the line.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Initializing a new Terraform directory generally does not need input but it accepts the following parameter for non-attended use.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform init -input=false<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">Initializing provider plugins...\n\nTerraform has been successfully initialized!<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now that Terraform has been initialized with the required plugin continue below on how to use variables to automate Terraform deployments.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/signup.upcloud.com\">Try Terraform for free!<\/a><\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Customising deployment with variables<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform variables are very useful for reusing the same configuration file with different outcomes. With Terraform, you can include variables straight from the command line or add them in the configuration files.&nbsp;If you\u2019re not entirely up to speed on Terraform variables and how to use them, learn more in <a href=\"https:\/\/upcloud.com\/global\/resources\/tutorials\/terraform-variables\" target=\"_blank\" rel=\"noreferrer noopener\">the guide to Terraform variables<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Below is an example of setting a variable on the command line during planning.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform plan -var=\"variable=value\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The above is handy for setting a couple of variables but not in large numbers. To set multiple variables at a time, it is more convenient to specify their values in a variable definitions file. Terraform automatically loads any filename ending in either <tt>.tfvars<\/tt> or <tt>.tfvars.json<\/tt> within the same working directory but the variable file can also be included at the command line with <tt>-var-file<\/tt> parameter.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform plan -var-file=\"variables.tfvars\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, to give Terraform some variables to work with, let\u2019s create the two files as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">touch config-options.tf default.tfvars<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then add the following variables to the <tt>config-options.tf<\/tt> file.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">variable \"private_key_path\" {\n  type = string\n  default = \"<span style=\"color: #ff0000;\">\/home\/user\/.ssh\/terraform_rsa<\/span>\"\n}\n\nvariable \"public_key\" {\n  type = string\n  default = \"<span style=\"color: #ff0000;\">ssh-rsa terraform_public_key<\/span>\"\n}\n\nvariable plans {\n  type = map\n  default = {\n    \"5USD\"  = \"1xCPU-1GB\"\n    \"10USD\" = \"1xCPU-2GB\"\n    \"20USD\" = \"2xCPU-4GB\"\n  }\n}\n\nvariable storages {\n  type = map\n  default = {\n    \"1xCPU-1GB\" = \"25\"\n    \"1xCPU-2GB\" = \"50\"\n    \"2xCPU-4GB\" = \"80\"\n  }\n}\n\nvariable zones {\n  type = map\n  default = {\n    \"amsterdam\" = \"nl-ams1\"\n    \"london\"    = \"uk-lon1\"\n    \"frankfurt\" = \"de-fra1\"\n    \"helsinki1\" = \"fi-hel1\"\n    \"helsinki2\" = \"fi-hel2\"\n    \"chicago\"   = \"us-chi1\"\n    \"singapore\" = \"sg-sin1\"\n  }\n}\n\nvariable templates {\n  type = map\n  default = {\n    \"ubuntu\" = \"01000000-0000-4000-8000-000030080200\"\n    \"centos\" = \"01000000-0000-4000-8000-000050010300\"\n    \"debian\" = \"01000000-0000-4000-8000-000020040100\"\n  }\n}\n\nvariable hostname { default = \"template\" }\nvariable plan { default = \"5USD\" }\nvariable zone { default = \"amsterdam\" }\nvariable template { default = \"debian\" }<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Afterwards, add the example values in the <tt>example.tfvars<\/tt> file.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\"hostname\" = \"example\"\n\"plan\"     = \"5USD\"\n\"zone\"     = \"frankfurt\"\n\"template\" = \"ubuntu\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once you\u2019ve set up the variable files, test the template by running the plan command as below.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform plan -var-file=example.tfvars<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Setting up variables this way allows you to configure your Terraform deployment quickly and easily without needing to edit the files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The above examples for setting variables can be used together in any combination. If the same variable is assigned multiple values, Terraform uses the last input, overriding any previous values.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform loads variables in a predefined order. Later sources take precedence over earlier ones.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Environment variables<\/li>\n\n\n\n<li>The terraform.tfvars file, if present.<\/li>\n\n\n\n<li>The terraform.tfvars.json file, if present.<\/li>\n\n\n\n<li>Any *.auto.tfvars or *.auto.tfvars.json files, processed in lexical order of their filenames.<\/li>\n\n\n\n<li>Any -var and -var-file options on the command line, in the order they are provided.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Next up, let\u2019s have a look at how to test the template deployment.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Plan, apply, destroy<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Planning and deploying Terraform configurations is pretty simple and only requires a couple of extra steps to automate the process.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Continuing our examples above where we set up a template plan with configurable variables. The following plan command would then be used to test out our configuration.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform plan -input=false -var-file=example.tfvars -var=hostname=test -out=tfplan<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This command includes a number of command-line parameters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>-input=false&nbsp;disables<\/em> Terraform from asking to input variables if not directly set<\/li>\n\n\n\n<li><em>-var-file=defaults.tfvars<\/em> includes your variables file<\/li>\n\n\n\n<li><em>-var=hostname=test<\/em> set a hostname for the new server<\/li>\n\n\n\n<li><em>-out=tfplan<\/em> saves the plan to a local file called <em>tfplan<\/em> for applying<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Next, granted that everything in the plan looks correct, we can deploy the new cloud server with the Terraform\u2019s apply command.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Since we already saved the plan at the previous step, the output file can now be used to apply the configuration. Using a saved plan we can be sure of the actions Terraform is going to take before committing the changes.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform apply tfplan -auto-approve<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The only additional parameter comes with the need to skip the manual approval check by adding <em>-auto-approve<\/em> to automatically answer yes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lastly, when you no longer need a particular cloud resource, remove the server either using the Terraform destroy command or planning for destroying.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform destroy -auto-approve<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform plan -destroy -out=tfdestroy\nterraform apply tfdestroy -auto-approve<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That\u2019s just how simple Terraform commands are to automate!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the destroy command will delete all resources configured in that Terraform directory. If you want to remove only a part of your cloud resources defined by the same Terraform deployment, remove the resource from the configuration and use the apply command to update your infrastructure. Terraform will then figure out the differences to the live deployment and apply the necessary changes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Automating Terraform<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We\u2019ve now got the required basics down and have everything we need to automate a deployment using Terraform.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One of the advantages of cloud servers is how quickly they can be deployed and deleted. If you only need to run something for a short amount of time, deploying a new server is not just a clean starting point but also cost-effective.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s test this in practice by scripting an automated benchmark for the cloud server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open your favourite text editor, for example, nano with the command below.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">nano ~\/benchmark.sh<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then add the following to the script.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/bash\n\n# Create a new Terraform configuration from the template\nmkdir ~\/terraform\/geekbench &amp;&amp; cd ~\/terraform\/geekbench\n\n# Copy only the plan and variable files\ncp ~\/terraform\/template\/*.tf ~\/terraform\/geekbench\/\ncp ~\/terraform\/template\/*.tfvars ~\/terraform\/geekbench\/\n\n# Renaming the resource\nsed -i 's\/\"template\"\/\"geekbench\"\/g' ~\/terraform\/geekbench\/template.tf\n\n# Opening the last set of brackets in the resource\nsed -i '$ s\/^}$\/\/g' ~\/terraform\/geekbench\/template.tf\n\n# Adding a remote execution script to the configuration\ncat &lt;&gt; ~\/terraform\/geekbench\/template.tf\n  provisioner \"remote-exec\" {\n    inline = [\n      \"curl http:\/\/cdn.geekbench.com\/Geekbench-4.4.1-Linux.tar.gz -o ~\/geekbench.tar.gz\",\n      \"tar -zxf ~\/geekbench.tar.gz\",\n      \"~\/Geekbench-4.4.1-Linux\/geekbench4 | grep 'https:\/\/browser.geekbench.com'\",\n    ]\n  }\n}\nEOT\n\n# Initialise the configuration\nterraform init -input=false\n# Plan and deploy\nterraform plan -input=false -var=hostname=geekbench -var=plan=5USD -out=tfplan\nterraform apply tfplan\n\n# Terraform will run the above script to benchmark the server\n# Find your results in the Terraform output\n\n# Once finished, destroy the server\nterraform plan -destroy -out=tfdestroy\nterraform apply tfdestroy\n\n# Cleaning up\nrm -r ~\/terraform\/geekbench\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When done, save the file and exit the editor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Make the script executable<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">sudo chmod +x ~\/benchmark.sh<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then test the benchmark script.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">~\/benchmark.sh<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can follow the deployment as Terraform outputs information about various parts of the process. Once deployed, Terraform will download and run the Geekbench script. The benchmarking might take a couple of minutes, you\u2019ll see an output similar to the one below with the URL to your results.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">upcloud_server.geekbench: Still creating... (5m50s elapsed)\nupcloud_server.geekbench (remote-exec):   https:\/\/browser.geekbench.com\/v4\/cpu\/14430944\nupcloud_server.geekbench (remote-exec):   https:\/\/browser.geekbench.com\/v4\/cpu\/14430944\/claim?key=432214\nupcloud_server.geekbench: Creation complete after 5m57s (ID: 00249d8a-89e1-4c29-833c-93e8ffd433ae)\n\nApply complete! Resources: 1 added, 0 changed, 0 destroyed.<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Afterwards, Terraform will destroy the server and clean up the configuration files without ever touching the original template.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you want to try benchmarking servers with more resources, simply edit the planning command of your script. For example, select the 1xCPU 2GB RAM plan like shown in the example underneath.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">terraform plan -input=false -var=hostname=geekbench -var=plan=10USD -out=tfplan<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then run the script again as before.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Automating deployments with Terraform is a great way of creating reliable and easy to customise configurations for many use cases. The example in this guide is but a scratch on the surface of what is possible with Terraform automation. For ideas on other uses, have a look at the Terraform documentation about <a rel=\"noopener\" href=\"https:\/\/www.terraform.io\/docs\/provisioners\/file.html\" target=\"_blank\">different types of provisioners<\/a>.<\/p>\n","protected":false},"author":3,"featured_media":27461,"comment_status":"open","ping_status":"closed","template":"","community-category":[256,235],"class_list":["post-2215","tutorial","type-tutorial","status-publish","has-post-thumbnail","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/2215","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial"}],"about":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/types\/tutorial"}],"author":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/comments?post=2215"}],"version-history":[{"count":0,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/2215\/revisions"}],"wp:attachment":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/media?parent=2215"}],"wp:term":[{"taxonomy":"community-category","embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/community-category?post=2215"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}