diff --git a/docs/applications/configuration-management/how-to-build-your-infrastructure-using-terraform-and-linode/index.md b/docs/applications/configuration-management/how-to-build-your-infrastructure-using-terraform-and-linode/index.md index ee60cdd8b83..b55cd287005 100644 --- a/docs/applications/configuration-management/how-to-build-your-infrastructure-using-terraform-and-linode/index.md +++ b/docs/applications/configuration-management/how-to-build-your-infrastructure-using-terraform-and-linode/index.md @@ -2,12 +2,12 @@ author: name: Linode Community email: docs@linode.com -description: 'Use Terraform to deploy Linodes containing pre-configured application environments such as Docker or from a manual configuration.' -og_description: 'Use Terraform to deploy Linodes containing pre-configured application environments such as Docker or from a manual configuration.' -keywords: ["Linode", "terraform", "plugin", "infrastructure", "IaC", "Infrastructure as Code"] +description: 'Use Terraform to provision Linode environments.' +og_description: 'Use Terraform to provision Linode environments.' +keywords: ["terraform", "infrastructure", "IaC"] license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' published: 2017-11-06 -modified: 2018-03-13 +modified: 2018-10-19 aliases: ['platform/how-to-build-your-infrastructure-using-terraform-and-linode/'] modified_by: name: Linode @@ -16,9 +16,11 @@ contributor: name: Damaso Sanoja --- -Infrastructure as code (IaC) is software that gives developers the ability to build, manage, and provision computing environments with a high-level [configuration syntax](https://www.terraform.io/docs/configuration/syntax.html). Some benefits include the ability to enforce DevOps best practices, process automation, and the opportunity to use version control systems for greater visibility and collaboration within a team. +Infrastructure as code (IaC) is a development and operations methodology that allows server deployments and software configuration to be represented as code. This methodology reduces the chance for human error, makes complex systems more manageable, eases collaboration on systems engineering projets, and offers a number of other benefits. -[Terraform](https://www.terraform.io) stands out from other IaC solutions because it's an orchestration tool, which means it's designed specifically for bare-metal server and virtual machines. The commands in this guide should be run from a client machine running Ubuntu 16.04. +Terraform is an IaC tool that focuses on creating, modifying, and destroying servers, instead of managing the software on those servers. Terraform offers plugins to interface with different hosting providers, and an official Linode plugin is available. This guide will show how to get started with Terraform and Linode. + +Linodes created with Terraform can be further configured with container systems like Docker, or with configuration management software like Salt, Puppet, Ansible, or Chef. {{< caution >}} The configurations and commands used in this guide will result in multiple Linodes being added to your account. Be sure to monitor your account closely in the Linode Manager to avoid unwanted charges. @@ -26,618 +28,647 @@ The configurations and commands used in this guide will result in multiple Linod ## Before You Begin -- You will need root access to the system and a standard user account with sudo privileges. +- This guide will show how to install and use the Terraform client software from a Linux system. Terraform can be installed on other operating systems, and the instructions for those platforms will be analogous to the commands presented in this guide. -- Create an API key for your Linode account. Be sure to take a screen capture of the API key when it's displayed, it will only appear once. See our guide [Getting Started with the Linode API](/docs/platform/api/getting-started-with-the-linode-api/#get-an-access-token) if you need help. + {{< note >}} +When following this guide, your Linux user may need sudo privileges in order to install supplementary software packages. +{{}} -- You will need [Git](https://git-scm.com/) installed on your system. +- You will need a personal access token for Linode's [v4 API](https://developers.linode.com/api/v4) to use with Terraform. Follow the [Getting Started with the Linode API](/docs/platform/api/getting-started-with-the-linode-api-new-manager/#get-an-access-token) to get a token. -## Configure Client Machine + {{< note >}} +Any Personal Access Tokens generated from the previous Linode Manager are API v3 tokens and will not work with Terraform's Linode provider. +{{}} -### Install Terraform +## Install Terraform - 1. Download the following from [Terraform's website](https://www.terraform.io/downloads.html): +1. Make a Terraform project directory in your home directory and then navigate to it: - - The 64-bit Linux `.zip` archive. - - The SHA256 checksums file. - - The checksum signature file. + mkdir ~/terraform + cd ~/terraform -2. Import the HashiCorp Security [GPG key](https://www.hashicorp.com/security.html): +1. Download the following files from [Terraform's website](https://www.terraform.io/downloads.html). Example `wget` commands are listed using the latest version available at time of publishing (0.11.9). You should inspect the links on the download page to see if a newer version is available and update the `wget` commands to use those URLs instead: - gpg --keyserver keyserver.ubuntu.com --recv 348FFC4C + - The 64-bit Linux `.zip` archive - The output should show that the key was imported: + wget https://releases.hashicorp.com/terraform/0.11.9/terraform_0.11.9_linux_amd64.zip - {{< output >}} - gpg: requesting key 348FFC4C from hkp server keyserver.ubuntu.com - gpg: /root/.gnupg/trustdb.gpg: trustdb created - gpg: key 348FFC4C: public key "HashiCorp Security " imported - gpg: no ultimately trusted keys found - gpg: Total number processed: 1 - gpg: imported: 1 (RSA: 1) - {{}} + - The SHA256 checksums file -3. Verify the checksum file's GPG signature: + wget https://releases.hashicorp.com/terraform/0.11.9/terraform_0.11.9_SHA256SUMS - gpg --verify terraform*.sig terraform*SHA256SUMS + - The checksum signature file - The output should say the signature is good: - {{< output >}} - gpg: Signature made Wed 31 Jan 2018 08:53:21 PM UTC using RSA key ID 348FFC4C - gpg: Good signature from "HashiCorp Security " - gpg: WARNING: This key is not certified with a trusted signature! - gpg: There is no indication that the signature belongs to the owner. - Primary key fingerprint: 91A6 E7F8 5D05 C656 30BE F189 5185 2D87 348F FC4C -{{}} + wget https://releases.hashicorp.com/terraform/0.11.9/terraform_0.11.9_SHA256SUMS.sig -4. Verify that the fingerprint matches what's on [HashiCorp's security page](https://www.hashicorp.com/security.html). -5. Verify the `.zip` archive's checksum: +### Verify the Download - sha256sum -c terraform*SHA256SUMS 2>&1 | grep OK +1. Import the HashiCorp Security GPG key (listed on the [HashiCorp Security](https://www.hashicorp.com/security.html) page under *Secure Communications*): - The output should show the file's name as given in the `terraform*SHA256SUMS` file: - - terraform_0.11.3_linux_amd64.zip: OK - -### Install Golang - -1. Download and extract Go from the project's [downloads page](https://golang.org/dl/). Terraform requires version 1.9: + gpg --recv-keys 51852D87348FFC4C - wget -c https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz - sudo tar -C /usr/local -xzf go*.linux-amd64.tar.gz - -2. Create separate directories for project executables and source code: - - mkdir -p ~/go_projects/{bin,src,pkg} + The output should show that the key was imported: -3. Add Go-specific `PATH` locations to your user's environment. Add these lines to the bottom of your user's `~/.profile` file: + {{< output >}} +gpg: /home/user/.gnupg/trustdb.gpg: trustdb created +gpg: key 51852D87348FFC4C: public key "HashiCorp Security " imported +gpg: no ultimately trusted keys found +gpg: Total number processed: 1 +gpg: imported: 1 +{{}} - {{< file "~/.profile" aconf >}} -export PATH=$PATH:/usr/local/go/bin -export PATH=$PATH:$HOME/go_projects/bin -export GOPATH="$HOME/go_projects" -export GOBIN="$GOPATH/bin" -{{}} {{< note >}} -You can change the variables to any location that suits you, as long as it is included it in the `PATH` variable. +If you receive errors that indicate the `dirmngr` software is missing or inaccessible, install `dirmngr` using your package manager and run the GPG command again. {{< /note >}} -4. Reload your user's environment profile: - - source ~/.profile - -### Build a Linode Plugin for Terraform +1. Verify the checksum file's GPG signature: -1. Download the Terraform repository: - - go get github.com/hashicorp/terraform - -2. Download the custom `terraform-provider-linode` repository: - - go get github.com/LinodeContent/terraform-provider-linode + gpg --verify terraform*.sig terraform*SHA256SUMS -3. Source code is stored in Go's `src` directory by default. Change directories to the location of the Terraform Linode plugin and build the package. Dependencies will be handled automatically by `godeps` that's already in the plugin folder. + The output should contain the `Good signature from "HashiCorp Security "` confirmation message: - cd ~/go_projects/src/github.com/LinodeContent/terraform-provider-linode/bin/terraform-provider-linode - go build -o terraform-provider-linode + {{< output >}} +gpg: Signature made Wed 15 Aug 2018 10:07:05 PM UTC +gpg: using RSA key 51852D87348FFC4C +gpg: Good signature from "HashiCorp Security <security@hashicorp.com>" [unknown] +gpg: WARNING: This key is not certified with a trusted signature! +gpg: There is no indication that the signature belongs to the owner. +Primary key fingerprint: 91A6 E7F8 5D05 C656 30BE F189 5185 2D87 348F FC4C +{{}} -4. Move the newly created binary and the Terraform configuration file to `~/go_projects/bin`: +1. Verify that the fingerprint output matches the fingerprint listed in the *Secure Communications* section of the [HashiCorp Security](https://www.hashicorp.com/security.html) page. - mv ~/go_projects/src/github.com/LinodeContent/terraform-provider-linode/bin/terraform-provider-linode/terraform-provider-linode ~/go_projects/bin - mv ~/go_projects/src/github.com/LinodeContent/terraform-provider-linode/linode-template.tf ~/go_projects/bin +1. Verify the `.zip` archive's checksum: -At this point, you have all the binaries needed. If the rest of your clients use the same operating system, then you can distribute these files among them. There is no need for each client to install `Go` or build the same package. + sha256sum -c terraform*SHA256SUMS 2>&1 | grep OK -### Prepare the Terraform Plugin + The output should show the file's name as given in the `terraform*SHA256SUMS` file: -1. Download the Terraform repository: + {{< output >}} +terraform_0.11.9_linux_amd64.zip: OK +{{< /output >}} - go get github.com/hashicorp/terraform +### Configure the Terraform Environment -2. Get the Linode plugin for Terraform: +1. Unzip `terraform_*_linux_amd64.zip` to your `~/terraform` directory: - wget https://github.com/linode/docs-scripts/raw/master/hosted_scripts/terraform-linode-plugin/terraform-provider-linode + unzip terraform_*_linux_amd64.zip -3. Move the plugin to `~go_projects/bin`: + {{< note >}} +If you receive an error that indicates `unzip` is missing from your system, install the `unzip` package and try again. +{{< /note >}} - mv terraform-provider-linode ~/go_projects/bin/ - chmod 750 ~/go_projects/bin/terraform-provider-linode +1. Edit your `~./profile` to include the `~/terraform` directory in your PATH. Then, reload the Bash profile: -### Configure the Linode Provider + echo 'export PATH="$PATH:$HOME/terraform"' >> ~/.profile + source ~/.profile -Terraform can understand two types of configuration files: JSON and HashiCorp Configuration Language (HCL). This guide [used the HCL format](#install-terraform), designated by the extension `.tf`. +1. Verify Terraform can run by simply calling it with no options or arguments: -1. Open `linode-template.tf` in a text editor and add the snippet displayed below. Fill in your Linode API key, public SSH key, and desired root password where indicated: + terraform - {{< file "~/go_projects/bin/linode-template.tf" aconf >}} + {{< output >}} +Usage: terraform [-version] [-help] [args] + +The available commands for execution are listed below. +The most common, useful commands are shown first, followed by +less common or more advanced commands. If you're just getting +started with Terraform, stick with the common commands. For the +other commands, please read the help and docs before usage. + +Common commands: + apply Builds or changes infrastructure + console Interactive console for Terraform interpolations + destroy Destroy Terraform-managed infrastructure + env Workspace management + fmt Rewrites config files to canonical format + get Download and install modules for the configuration + graph Create a visual graph of Terraform resources + import Import existing infrastructure into Terraform + init Initialize a Terraform working directory + output Read an output from a state file + plan Generate and show an execution plan + providers Prints a tree of the providers used in the configuration + push Upload this Terraform module to Atlas to run + refresh Update local state file against real resources + show Inspect Terraform state or plan + taint Manually mark a resource for recreation + untaint Manually unmark a resource as tainted + validate Validates the Terraform files + version Prints the Terraform version + workspace Workspace management + +All other commands: + debug Debug output management (experimental) + force-unlock Manually unlock the terraform state + state Advanced state management +{{< /output >}} + + +## Building with the Linode Provider + +Terraform uses a declarative approach in which configuration files specify the desired end-state of the infrastructure, so the examples in this guide will simply list the Linodes that we want to create. Terraform can understand two types of configuration files: JSON, and [HashiCorp Configuration Language](https://github.com/hashicorp/hcl) (HCL). This guide uses the HCL format, and HCL files end in the `.tf` extension. + +1. Create the file `linode-terraform-web.tf` in your `~/terraform` directory with the snippet below. Fill in your Linode API token, public SSH key, and desired root password where indicated. + + {{< file "~/terraform/linode-terraform-web.tf" aconf >}} provider "linode" { - key = "your-linode-API-key-here" + token = "YOUR_LINODE_API_TOKEN" } -resource "linode_linode" "terraform-example" { - image = "Ubuntu 16.04 LTS" - kernel = "Grub 2" - name = "linode-example" - group = "terraform-test" - region = "Atlanta, GA, USA" - size = 1024 - ssh_key = "your-ssh-id_rsa.pub-here" - root_password = "your-server-password-here" +resource "linode_instance" "terraform-web" { + image = "linode/ubuntu18.04" + label = "Terraform-Web-Example" + group = "Terraform" + region = "us-east" + type = "g6-standard-1" + authorized_keys = [ "YOUR_PUBLIC_SSH_KEY" ] + root_pass = "YOUR_ROOT_PASSWORD" } {{< /file >}} - See [Terraform's documentation](https://www.terraform.io/docs/configuration/syntax.html) for specific information regarding configuration syntax. + This snippet creates a Linode 2GB labelled `Terraform-Web-Example` in a `Terraform` Linodes group. While the server's software won't be configured in this guide, we can imagine for now that the Linode acts as a webserver. -2. Navigate to `~/go_projects/bin` and initialize the Terraform configuration: + {{< note >}} +See [Terraform's documentation](https://www.terraform.io/docs/configuration/syntax.html) for more information on configuration syntax. +{{< /note >}} + +1. Initialize the Terraform configuration: - cd ~/go_projects/bin terraform init Terraform will confirm successful initialization: + {{< output >}} - Terraform has been successfully initialized! -{{}} +Initializing provider plugins... +- Checking for available provider plugins on https://releases.hashicorp.com... +- Downloading plugin for provider "linode" (1.0.0)... -3. If an error occurs, run the command again in debug mode: +The following providers do not have any version constraints in configuration, +so the latest version was installed. - TF_LOG=debug terraform init +To prevent automatic upgrades to new major versions that may contain breaking +changes, it is recommended to add version = "..." constraints to the +corresponding provider blocks in configuration, with the constraint strings +suggested below. -## Use Terraform to Deploy a Linode +* provider.linode: version = "~> 1.0" +Terraform has been successfully initialized! +{{}} -### Single Server Basic Linode + {{< note >}} +If an error occurs, run the command again in debug mode: -1. Check your Terraform plan: + TF_LOG=debug terraform init +{{< /note >}} + +1. Run Terraform's [plan](https://www.terraform.io/docs/commands/plan.html) command: terraform plan - You will see: -{{< output >}} - Refreshing Terraform state in-memory prior to plan... - The refreshed state will be used to calculate this plan, but will not be - persisted to local or remote state storage. - - - ------------------------------------------------------------------------ - - An execution plan has been generated and is shown below. - Resource actions are indicated with the following symbols: - + create - - Terraform will perform the following actions: - - + linode_linode.your-terraform-name-here - id: - disk_expansion: "false" - group: "your-linode-group-name-here" - helper_distro: "true" - image: "Ubuntu 16.04 LTS" - ip_address: - kernel: "Grub 2" - manage_private_ip_automatically: "true" - name: "TFtest" - plan_storage: - plan_storage_utilized: - private_ip_address: - region: "Atlanta, GA, USA" - root_password: "wAZ9SvTofwDbrGO2FWgoI3BZFy0bvqxnQnNF1qn9pIQ=" - size: "1024" - ssh_key: "QLWOVauEwNxWGbj2ErWF9vFYIXsxW/2duL/og8gtV84=" - status: - swap_size: "512" - - - Plan: 1 to add, 0 to change, 0 to destroy. - - ------------------------------------------------------------------------ - - Note: You didn't specify an "-out" parameter to save this plan, so Terraform - can't guarantee that exactly these actions will be performed if - "terraform apply" is subsequently run. + {{< output >}} +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + linode_instance.terraform-web + id: <computed> + alerts.#: <computed> + authorized_keys.#: "1" + authorized_keys.0: "ssh-rsa ..." + backups.#: <computed> + backups_enabled: <computed> + boot_config_label: <computed> + group: "Terraform" + image: "linode/ubuntu18.04" + ip_address: <computed> + ipv4.#: <computed> + ipv6: <computed> + label: "web" + private_ip_address: <computed> + region: "us-east" + root_pass: <sensitive> + specs.#: <computed> + status: <computed> + swap_size: <computed> + type: "g6-standard-1" + watchdog_enabled: "true" + + +Plan: 1 to add, 0 to change, 0 to destroy. + +------------------------------------------------------------------------ + +Note: You didn't specify an "-out" parameter to save this plan, so Terraform +can't guarantee that exactly these actions will be performed if +"terraform apply" is subsequently run. {{}} - If you need to fix any issues, activate debug mode: + `terraform plan` won't take any action or make any changes on your Linode account. Instead, an analysis is done to determine which actions (i.e. Linode instance creations, deletions, or modifications) are required to achieve the state described in your configuration. - TF_LOG=debug terraform plan + {{< note >}} +Debug mode can be applied to the plan command if you need to perform troubleshooting: - The `terraform plan` command won't take any action or make any changes on your Linode account. Terraform uses a declarative approach in which your configuration file specifies the desired end-state of the infrastructure. When you run `terraform plan`, an analysis is done to determine which actions are required to achieve this state. + TF_LOG=debug terraform plan +{{< /note >}} -2. If there are no errors, start the deployment: +1. If there are no errors, start the deployment: terraform apply - You'll be asked to confirm the action, enter `yes` and press **Enter**: + You'll be asked to confirm the action. Enter `yes` and press **Enter**: {{< output >}} - An execution plan has been generated and is shown below. - Resource actions are indicated with the following symbols: - + create - - Terraform will perform the following actions: - - + linode_linode.your-terraform-name-here - id: - disk_expansion: "false" - group: "your-linode-group-name-here" - helper_distro: "true" - image: "Ubuntu 16.04 LTS" - ip_address: - kernel: "Grub 2" - manage_private_ip_automatically: "true" - name: "your-linode-name-here" - plan_storage: - plan_storage_utilized: - private_ip_address: - region: "Atlanta, GA, USA" - root_password: "wAZ9SvTofwDbrGO2FWgoI3BZFy0bvqxnQnNF1qn9pIQ=" - size: "1024" - ssh_key: "QLWOVauEwNxWGbj2ErWF9vFYIXsxW/2duL/og8gtV84=" - status: - swap_size: "512" - - - Plan: 1 to add, 0 to change, 0 to destroy. - - Do you want to perform these actions? - Terraform will perform the actions described above. - Only 'yes' will be accepted to approve. - - Enter a value: +Do you want to perform these actions? + Terraform will perform the actions described above. + Only 'yes' will be accepted to approve. + + Enter a value: yes + +linode_instance.terraform-web: Creating... + alerts.#: "" => "<computed>" + authorized_keys.#: "" => "1" + authorized_keys.0: "" => "ssh-rsa ..." + backups.#: "" => "<computed>" + backups_enabled: "" => "<computed>" + boot_config_label: "" => "<computed>" + group: "" => "Terraform" + image: "" => "linode/ubuntu18.04" + ip_address: "" => "<computed>" + ipv4.#: "" => "<computed>" + ipv6: "" => "<computed>" + label: "" => "web" + private_ip_address: "" => "<computed>" + region: "" => "us-east" + root_pass: "<sensitive>" => "<sensitive>" + specs.#: "" => "<computed>" + status: "" => "<computed>" + swap_size: "" => "<computed>" + type: "" => "g6-standard-1" + watchdog_enabled: "" => "true" +linode_instance.terraform-web: Still creating... (10s elapsed) +linode_instance.terraform-web: Still creating... (20s elapsed) +linode_instance.terraform-web: Still creating... (30s elapsed) +linode_instance.terraform-web: Still creating... (40s elapsed) +linode_instance.terraform-web: Still creating... (50s elapsed) +linode_instance.terraform-web: Creation complete after 52s (ID: 10975739) + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. {{}} -3. Return to the Linode Manager. You should see the `linode-test` Linode has been added to your account. - -### Two-Server Configuration - -Now that you have the `linode-example` Linode created using Terraform, imagine you need to implement a web and database server deployment. - -It's important to remember that: +1. Visit the Linode Manager. You should see that the `Terraform-Web-Example` Linode has been added to your account. -* Terraform loads all files with the ".tf" extension present in the working directory into memory. As a result, all files are concatenated (in memory) and you don't need to define the provider in this file, since it was declared in `linode-template.tf`. +### Provision Additional Servers -* Resources can't be duplicated, so you need to assign a unique name for each one. +In the previous step, you used Terraform to provision a Linode that could act as a webserver. To illustrate how to add another Linode via Terraform, let's say you now also need a separate database server. To do this, you can create another Terraform configuration file for the second Linode. -* In this example the same SSH key and root password are being used. You should change these values in production environments. +{{< note >}} +When deploying multiple Linodes with Terraform, remember that you need to assign a unique name for each Linode. -* A new parameter `swap_size` is used to override the default value of 512Mb. You can check all available options for `terraform-provider-linode` in the plugin GitHub repository [readme.md](https://github.com/LinodeContent/terraform-provider-linode). +In production environments, your SSH key and root password should be unique for each resource. Having said that, the example Linodes in this guide will share keys and root passwords. +{{< /note >}} -1. From the `linode-template.tf` create another file called `linode-www.tf` (don't delete `linode-template.tf`): +1. Create another file called `linode-terraform-db.tf`. Substitute in your SSH key and root password where indicated. **Do not delete** `linode-terraform-web.tf`. - {{< file "~/go_projects/bin/linode-www.tf" aconf >}} -resource "linode_linode" "terraform-www" { - image = "CentOS 7" - kernel = "Grub 2" - name = "www" - group = "web" - region = "Dallas, TX, USA" - size = 2048 + {{< file "~/terraform/linode-terraform-db.tf" aconf >}} +resource "linode_instance" "terraform-db" { + image = "linode/centos7" + label = "Terraform-Db-Example" + group = "Terraform" + region = "us-south" + type = "g6-standard-1" swap_size = 1024 - ssh_key = "your-ssh-id_rsa.pub-here" - root_password = "your-server-password-here" + authorized_keys = [ "YOUR_PUBLIC_SSH_KEY" ] + root_pass = "YOUR_ROOT_PASSWORD" } {{< /file >}} -2. Check your plan for errors: - - terraform plan - -3. Apply all changes: + You may notice that the Terraform provider is not specified in this file as it was in `linode-terraform-web.tf`. Terraform loads into memory and concatenates all files present in the working directory which have a `.tf` extension. This means you don't need to define the provider again in new `.tf` files. - terraform apply + {{< note >}} +In this configuration a new parameter, `swap_size`, is used to override the default value of 512MB. You can check all available options for the Linode Terraform provider in the plugin's GitHub repository [readme.md](https://github.com/LinodeContent/terraform-provider-linode). +{{< /note >}} -4. Check the Linode Manager to ensure that the `www` Linode was added to the `web` display group on your account. +1. Review the Terraform plan: -### Adjust Deployment + terraform plan -Imagine you want to change the first server name and tag to something more relevant, and also increase the size to match the newly created Linode. + Terraform knows that your Terraform-Web-Example Linode still exists, so the plan only shows that the Terraform-Db-Example Linode would be created: -1. Modify the `linode-template.tf` + {{< output >}} +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create - {{< file "~/go_projects/bin/linode-template.tf" aconf >}} -provider "linode" { - key = "your-linode-API-key-here" -} +Terraform will perform the following actions: -resource "linode_linode" "terraform-example" { - image = "Ubuntu 16.04 LTS" - kernel = "Grub 2" - name = "database" - group = "web" - region = "Atlanta, GA, USA" - size = 2048 - swap_size = 1024 - ssh_key = "your-ssh-id_rsa.pub-here" - root_password = "your-server-password-here" -} + + linode_instance.terraform-db + # [...] -{{< /file >}} +Plan: 1 to add, 0 to change, 0 to destroy. +# [...] +{{< /output >}} -2. Check your plan: - - terraform plan - -3. Apply your changes: +1. Apply the configuration: terraform apply - {{< caution >}} -Changing the size of your Linode will force your server to be powered off and migrated to a different host in the same data center. The associated disk migration will take approximately 1 minute for every 3-5 gigabytes of data. For more information about resizing read the [Resizing a Linode](/docs/platform/disk-images/resizing-a-linode/) guide. -{{< /caution >}} - -4. Return to the Linode Manager to verify the changes. - -### Advanced Configuration Example +1. Check the Linode Manager to ensure that the `Terraform-Db-Example` Linode was added to your account. -Up to this point, the procedure for adding a new node to your infrastructure was to create a new file and run the `terraform apply` command. But what happens when your planned infrastructure has dozens of servers? In this example, you will use a very simplistic version of a Terraform configuration file that uses variables. +### Destroy Servers -1. For the purpose of this example you will need to delete previous nodes: - - terraform plan -destroy - - That returns: - - {{< output >}} - Refreshing Terraform state in-memory prior to plan... - The refreshed state will be used to calculate this plan, but will not be - persisted to local or remote state storage. +Terraform includes a [destroy](https://www.terraform.io/docs/commands/destroy.html) command to remove servers managed by Terraform. Prior to running the destroy command, you can run the plan command with the `-destroy` option to see which servers would be removed: - linode_linode.your-terraform-name-here: Refreshing state... (ID: 6630470) + terraform plan -destroy - ------------------------------------------------------------------------ +{{< output >}} +# [...] - An execution plan has been generated and is shown below. - Resource actions are indicated with the following symbols: - - destroy +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + - destroy - Terraform will perform the following actions: +Terraform will perform the following actions: - - linode_linode.TFtest + - linode_instance.terraform-db + - linode_instance.terraform-web - Plan: 0 to add, 0 to change, 1 to destroy. - ------------------------------------------------------------------------ +Plan: 0 to add, 0 to change, 2 to destroy. - Note: You didn't specify an "-out" parameter to save this plan, so Terraform - can't guarantee that exactly these actions will be performed if - "terraform apply" is subsequently run. -{{}} +# [...] +{{< /output >}} -2. Similar to `terraform plan`, the above command checks your infrastructure before doing any change. To perform the deletion, run: +1. Run the destroy command to remove the servers from the last section. Confirm the deletion with `yes` when prompted: terraform destroy - That will return: - - {{< output >}} - linode_linode.your-terraform-name-here: Refreshing state... (ID: 6630470) - - An execution plan has been generated and is shown below. - Resource actions are indicated with the following symbols: - - destroy - - Terraform will perform the following actions: +1. Verify that the Linodes were removed in the Linode Manager. - - linode_linode.TFtest +1. Remove the configuration files: + rm *.tf - Plan: 0 to add, 0 to change, 1 to destroy. +### Provision Multiple Servers Using Variables - Do you really want to destroy? - Terraform will destroy all your managed infrastructure, as shown above. - There is no undo. Only 'yes' will be accepted to confirm. +Up to this point, the procedure for adding a new node to your infrastructure was to create a new file and run `terraform apply`. There are some downsides to this approach: - Enter a value: yes +- You need to repeatedly copy certain values across each file, like your SSH key. - linode_linode.your-terraform-name-here: Destroying... (ID: 6630470) - linode_linode.your-terraform-name-here: Destruction complete after 0s - - Destroy complete! Resources: 1 destroyed. -{{}} +- If you want to change certain parameters across multiple servers, like the Linodes' `group` attribute, then you need to change each file. -3. Verify the deletion in the Linode Manager. +To solve these issues, Terraform allows you to declare variables and insert those variables' values into your configurations: -4. Delete (or move to a different location) all Terraform files. +1. Create a new file to define your variable names and optional default variable values. This file can have any name; for this example, use `variables.tf`: - rm *.tf* - -5. Create a new file to define variables. You can use any name but for this example we'll use `variables.tf`: - - {{< file "~/go_projects/bin/variables.tf" aconf >}} -variable "linode_key" {} -variable "ssh_key" {} -variable "root_password" {} + {{< file "~/terraform/variables.tf" aconf >}} +variable "token" {} +variable "authorized_keys" {} +variable "root_pass" {} variable "region" { - default = "Atlanta, GA, USA" + default = "us-southeast" } {{< /file >}} -6. Create the file `terraform.tfvars` to store your variables. **You can't change this filename** after creating it: +1. Create the file `terraform.tfvars` to store your variables' values. Substitute in your API token, SSH key, and root password where indicated. **You cannot change this file's name** after creating it. - {{< file "~/go_projects/bin/terraform.tfvars" aconf >}} -linode_key = "your-linode-API-key-here" -ssh_key = "your-ssh-id_rsa.pub-here" -root_password ="your-root-password-here" + {{< file "~/terraform/terraform.tfvars" aconf >}} +token = "YOUR_LINODE_API_TOKEN" +authorized_keys = "YOUR_PUBLIC_SSH_KEY" +root_pass ="YOUR_ROOT_PASSWORD" {{< /file >}} -7. Create a new configuration file called `linode-mod-template.tf`: +1. Create a new configuration file called `linode-terraform-template.tf`: - {{< file "~/go_projects/bin/linode-mod-template.tf" aconf >}} + {{< file "~/terraform/linode-terraform-template.tf" aconf >}} # Linode Provider definition - provider "linode" { - key = "${var.linode_key}" + token = "${var.token}" } # Example Web Server - -resource "linode_linode" "www-01" { - image = "CentOS 7" - kernel = "Latest 64 bit" - name = "www" - group = "web" - region = "Dallas, TX, USA" - size = 2048 +resource "linode_instance" "terraform-web" { + image = "linode/centos7" + label = "Terraform-Web-Example" + group = "Terraform" + region = "${var.region}" + type = "g6-standard-1" swap_size = 1024 - ssh_key = "${var.ssh_key}" - root_password = "${var.root_password}" + authorized_keys = [ "${var.authorized_keys}" ] + root_pass = "${var.root_pass}" } # Example Database Server - -resource "linode_linode" "db-01" { - image = "Ubuntu 16.04 LTS" - kernel = "Latest 64 bit" - name = "database" - group = "web" +resource "linode_instance" "terraform-db" { + image = "linode/ubuntu18.04" + label = "Terraform-Db-Example" + group = "Terraform" region = "${var.region}" - size = 2048 + type = "g6-standard-1" swap_size = 1024 - ssh_key = "${var.ssh_key}" - root_password = "${var.root_password}" + authorized_keys = [ "${var.authorized_keys}" ] + root_pass = "${var.root_pass}" } +{{< /file >}} + +1. Check your new deployment for errors: + + terraform plan + +1. Apply the configuration: + + terraform apply + + The end result should be the same as before. + +### Modify Live Deployments + +Terraform allows you to change a server's name, size, or other attributes without needing to destroy and rebuild it. Terraform handles this through changes to the configuration files. +{{< caution >}} +Changing the size of your Linode will force your server to be powered off and migrated to a different host in the same data center. The associated disk migration will take approximately 1 minute for every 3-5 gigabytes of data. See our [Resizing a Linode](/docs/platform/disk-images/resizing-a-linode/) guide for more information. +{{< /caution >}} + +1. Modify `linode-terraform-template.tf` and update the `type` value to `g6-standard-4` for the `terraform-db` resource. + + {{< file "~/terraform/linode-terraform-template.tf" aconf >}} +# [...] + +resource "linode_instance" "terraform-db" { + # [...] + type = "g6-standard-4" + # [...] +} {{< /file >}} -8. Check your new deployment for errors: +1. Review the plan: terraform plan -9. Apply all changes: + {{< output >}} +# [...] + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + ~ linode_instance.terraform-db + type: "g6-standard-1" => "g6-standard-4" + + +Plan: 0 to add, 1 to change, 0 to destroy. + +# [...] +{{< /output >}} + +1. Apply your changes: terraform apply - The end result is the same as before. The use of variables gives Terraform great flexibility, not only to store repetitive data (as keys) but also to assign default values to any field. +1. Verify the changes in the Linode Manager. -## Manage your Infrastructure with Terraform -### Terraform Modules +## Terraform Modules -The idea behind any code-driven solution is to avoid repetitive blocks. Terraform uses a concept called *modules* to group common server requirements and configurations. Think of modules as similar to *functions* in programming languages. +Terraform uses a concept called [*modules*](https://www.terraform.io/docs/modules/index.html) to group common server requirements and configurations. Think of modules as similar to *functions* in programming languages. -Take a look at the following file structure: +As an example, let's say that you run a web agency and need to deploy identical pairs of webservers and database servers for different clients. To facilitate this, you can create a reusable Terraform module which describes the webserver and database server pairing. -![Terraform Modules Tree](terraform-modules-tree.jpg) +The module's description allows for variable substition of relevant attributes (passwords, keys, etc), just as in the configuration from the previous section. Once the module is configured, new servers can be instantiated for each of your clients by combining the module code with a new set of variable values. -There is a directory called `modules` containing the reusable code blocks (in this case `appserver`) and a `testing` directory containing the specific configuration to implement. It's a minimal layout but enough to highlight benefits. +### Basic Module Structure -#### Basic Module Structure +The module structure is flexible, so you can use as many Terraform files as needed to describe your infrastructure. This example contains just one configuration file describing the reusable code. -The module structure is flexible, so you can use as many Terraform files as needed to describe your infrastructure. This example contains just one main configuration file describing the reusable code: +1. Create a `modules/app-deployment/` directory to hold the module configuration: -{{< file "~/go_projects/bin/modules/appserver/main.tf" aconf >}} -# Application Server + cd ~/terraform + mkdir -p modules/app-deployment -resource "linode_linode" "appserver" { - image = "Ubuntu 16.04 LTS" - kernel = "Latest 64 bit" - name = "${var.appserver_name}" - group = "web" +1. Create a `main.tf` configuration file inside `modules/app-deployment/`: + + {{< file "~/terraform/modules/app-deployment/main.tf" aconf >}} +# Web Server +resource "linode_instance" "terraform-web" { + image = "linode/ubuntu18.04" + label = "${var.webserver_label}" + group = "Terraform" region = "${var.region}" - size = 2048 + type = "g6-standard-1" swap_size = 1024 - ssh_key = "${var.ssh_key}" - root_password = "${var.root_password}" + authorized_keys = "${var.authorized_keys}" + root_pass = "${var.root_pass}" } # Database Server - -resource "linode_linode" "dbserver" { - image = "CentOS 7" - kernel = "Latest 64 bit" - name = "${var.dbserver_name}" - group = "web" +resource "linode_instance" "terraform-db" { + image = "linode/centos7" + label = "${var.dbserver_label}" + group = "Terraform" region = "${var.region}" - size = "${var.db_size}" + type = "${var.db_type}" swap_size = 1024 - ssh_key = "${var.ssh_key}" - root_password = "${var.root_password}" + authorized_keys = "${var.authorized_keys}" + root_pass = "${var.root_pass}" } {{< /file >}} -The configuration above reproduces the previous examples using variables. The next file contains variable definitions: +1. The configuration above reproduces the previous examples using variables. The next file contains variable definitions. Assign a default value for each variable. That value will be used if you don't override it when you call the module. -{{< file "~/go_projects/bin/modules/appserver/variables.tf" aconf >}} -variable "appserver_name" { - description = "The name for the Application Server" - default = "default-app" + Substitute in your SSH key and root password where indicated: + + {{< file "~/terraform/modules/app-deployment/variables.tf" aconf >}} +variable "webserver_label" { + description = "The name for the Web Server" + default = "default-web" } -variable "dbserver_name" { +variable "dbserver_label" { description = "The name for the Database Server" default = "default-db" } -variable "db_size" { +variable "db_type" { description = "The size (plan) for your Database Linode" - default = "1024" + default = "g6-standard-1" } variable "region" { description = "The default Linode region to deploy the infrastructure" - default = "default-region" + default = "us-east" } -variable "ssh_key" { +variable "authorized_keys" { description = "The Public id_rsa.pub key used for secure SSH connections" - default = "default-ssh-key" + default = ["default-ssh-public-key"] } -variable "root_password" { +variable "root_pass" { description = "The default root password for the Linode server" - default = "default-root-pwd" + default = "default-root-password" } - {{< /file >}} -{{< note >}} -Assign a default value for each variable. That value will be used if you don't override it when you call the module. -{{< /note >}} +### Working with Modules -Create a `main.tf` configuration file that uses the module you just created: +Create a deployment for an imaginary client: -{{< file "~/go_projects/bin/testing/main.tf" aconf >}} -# Newark Testing Environment Infrastructure +1. Create a `client1` directory: + cd ~/terraform + mkdir client1 + +1. Create a `main.tf` configuration file inside `client1/` that uses your module. The module is referenced by providing the path to the module's configuration. Substitute in your API token, SSH key, and root password where indicated: + + {{< file "~/terraform/testing/main.tf" aconf >}} +# Client 1 Infrastructure provider "linode" { - key = "your-linode-API-key-here" + token = "YOUR_LINODE_API_TOKEN" } -module "appserver" { - source = "/your/absolute/path/to/modules/appserver" +module "app-deployment" { + source = "../modules/app-deployment" # Variables Specific to this Deployment - -region = "Newark, NJ, USA" -ssh_key = "your-ssh-id_rsa" -root_password ="your-root-password-here" +region = "us-east" +authorized_keys = [ "YOUR_PUBLIC_SSH_KEY" ] +root_pass ="YOUR_ROOT_PASSWORD" # Variables Specific to Servers - -appserver_name = "NJ-app" -dbserver_name = "NJ-db" -db_size = "8192" - +webserver_label = "client1-web" +dbserver_label = "client1-db" +db_type = "g6-standard-8" } {{< /file >}} -To use a module, call it by name with the command `module` and indicate the absolute path where it is saved. Then you can assign values to each field defined by a variable. The final result will be the same as if you pasted in all of the reusable code in the main configuration file. - - cd ~/go_projects/bin/testing/ - terraform init - terraform planned - terraform apply - -The possibilities of modules are endless. You can use several modules at once, you can mix the use of modules with traditional `resource` definitions, or you can even call modules from remote sources. For more information read the Terraform [modules documentation](https://www.terraform.io/docs/modules/index.html). - -## Server Configuration +1. The file structure for your module and for `client1` should now look as follows. This structure is not mandated by Terraform, but it is useful as as simple example: -Terraform offers many ways to set up and provision your Linode, using: + {{< output >}} +client1 +└── main.tf +modules +└── app-deployment +    ├── main.tf +    └── variables.tf +{{< /output >}} -* Custom scripts, which can be included on configuration file itself or called from a local or remote file. -* Specialized software tools integrated with Terraform like Chef or Puppet. -* Container-based solutions like Docker or Kubernetes. -* Terraform plugin-based solutions. +1. Initiatize the Terraform configuration for the client, review the plan, and apply it: -There are also plenty of [provisioners](https://www.terraform.io/docs/provisioners/index.html), [providers](https://github.com/terraform-providers), and even [modules](https://registry.terraform.io) available. + cd ~/terraform/client1/ + terraform init + terraform plan + terraform apply diff --git a/docs/applications/configuration-management/how-to-build-your-infrastructure-using-terraform-and-linode/terraform-modules-tree.jpg b/docs/applications/configuration-management/how-to-build-your-infrastructure-using-terraform-and-linode/terraform-modules-tree.jpg deleted file mode 100644 index a3359bcb6b5..00000000000 Binary files a/docs/applications/configuration-management/how-to-build-your-infrastructure-using-terraform-and-linode/terraform-modules-tree.jpg and /dev/null differ diff --git a/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-my-profile-small.png b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-my-profile-small.png new file mode 100644 index 00000000000..d3aef64835b Binary files /dev/null and b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-my-profile-small.png differ diff --git a/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-my-profile.png b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-my-profile.png new file mode 100644 index 00000000000..bacfcfa4c69 Binary files /dev/null and b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-my-profile.png differ diff --git a/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-new-token.png b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-new-token.png new file mode 100644 index 00000000000..d5394fdfd27 Binary files /dev/null and b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-new-token.png differ diff --git a/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-select-my-profile.png b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-select-my-profile.png new file mode 100644 index 00000000000..a18cf85de34 Binary files /dev/null and b/docs/platform/api/getting-started-with-the-linode-api-new-manager/get-started-with-linode-api-select-my-profile.png differ diff --git a/docs/platform/api/getting-started-with-the-linode-api-new-manager/index.md b/docs/platform/api/getting-started-with-the-linode-api-new-manager/index.md new file mode 100644 index 00000000000..ad615bcefcd --- /dev/null +++ b/docs/platform/api/getting-started-with-the-linode-api-new-manager/index.md @@ -0,0 +1,185 @@ +--- +author: + name: Jared Kobos + email: docs@linode.com +description: 'This guide introduces the Linode API and demonstrates several basic queries. It also covers authentication and the process of creating a new Linode through the API.' +og_description: 'This guide introduces the Linode API and demonstrates several basic queries. It also covers authentication and the process of creating a new Linode through the API.' +keywords: ["linode api", "api v4", "access token"] +license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' +modified: 2018-08-20 +modified_by: + name: Linode +published: 2018-04-03 +title: Getting Started with the Linode API +external_resources: + - '[API Documentation](https://developers.linode.com/v4/introduction)' + - '[Linode CLI](https://github.com/linode/linode-cli)' + - '[Linode API Python Library](https://github.com/linode/python-linode-api)' +hiddenguide: true +--- + +## Create a Linode Using the Linode API + +The Linode API allows you to automate any task that can be performed by the Linode Manager, such as creating Linodes, managing IP addresses and DNS, and opening support tickets. + +For example, this command creates a new 2GB Linode, deploys a Debian 9 image, and boots the system: + + curl -X POST https://api.linode.com/v4/linode/instances \ + -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" \ + -d '{"type": "g5-standard-2", "region": "us-east", "image": "linode/debian9", "root_pass": "root_password", "label": "prod-1"}' + +This guide will help you get set up to run this example. Note that if you run this command, you will create and be [charged for a 2GB Linode](/pricing). + +## Get an Access Token + +Only authorized users can add Linodes and make changes to your account, and each request must be authenticated with an access token. + +The easiest way to get a token is through the [early access Linode Manager](https://cloud.linode.com). + + {{< note >}} +If you are building an application which will need to authenticate multiple users (for example, a custom interface to Linode's infrastructure for your organization), you can set up an [OAuth authentication flow](https://developers.linode.com/v4/access) to generate tokens for each user. +{{< /note >}} + +### Create an API Token + +1. Log in to the Manager and select the **API Tokens** tab from the **My Profile** menu: + + ![Select My Profile.](get-started-with-linode-api-select-my-profile.png "Select My Profile.") + + ![Select API Tokens tab in My Profile Settings.](get-started-with-linode-api-my-profile-small.png "Select the API Tokens tab in My Profile Settings.") + +2. Click on **Add a Personal Access Token** and choose the access rights you want users authenticated with the new token to have. Privileges are cascading, so a token with *Create* access will also have *Modify* and *View* access. + + ![Add a Personal Access Token](get-started-with-linode-api-new-token.png "Add a Personal Access Token") + + When you have finished, click **Submit** to generate an API token string. Copy the token and save it in a secure location. **You will not be able to view the token through the Manager after closing the popup.** + +### Authenticate Requests + +This token must be sent as a header on all requests to authenticated endpoints. The header should use the format: + + Authorization: Bearer + +Store the token as a temporary shell variable to simplify repeated requests. Replace `` in this example: + + TOKEN= + +## Get Configuration Parameters + +Specify the type, region, and image for the new Linode. + +1. Review the list of available images: + + curl https://api.linode.com/v4/images/ + + Choose one of the images from the resulting list and make a note of the `id` field. + +1. Repeat this procedure to choose a type: + + curl https://api.linode.com/v4/linode/types/ + +1. Choose a region: + + curl https://api.linode.com/v4/regions + +## Build the Final Query + +Replace the values in the command below with your chosen type, region, and image, and choose a label and secure password. + + curl -X POST https://api.linode.com/v4/linode/instances \ + -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" \ + -d '{"type": "g5-standard-2", "region": "us-east", "image": "linode/debian9", "root_pass": "root_password", "label": "prod-1"}' + +## Advanced Query Options + +### Pagination + +If a results list contains more than 100 items, the response will be split into multiple pages. Each response will include the total number of pages and the current page. To view additional pages, add a `page` parameter to the end of the URL. For example, querying the available kernels produces more than 200 results: + + curl https://api.linode.com/v4/linode/kernels + + + {{< highlight json "linenos=table,hl_lines=2 25" >}} +{ + "results": 214, + "page": 1, + "data": [ + { + "kvm": false, + "architecture": "i386", + "version": "2.6.28", + "xen": true, + "label": "2.6.28.3-linode17", + "id": "linode/2.6.28.3-linode17", + "pvops": true + }, + { + "kvm": false, + "architecture": "i386", + "version": "2.6.18", + "xen": true, + "label": "2.6.18.8-linode16", + "id": "linode/2.6.18.8-linode16", + "pvops": false + }, + ] + ... + "pages": 3 +} +{{< /highlight >}} + +The `pages` field indicates that the results are divided into three pages. View the second page: + + curl https://api.linode.com/v4/linode/kernels?page=2 + +If you prefer a smaller number of items per page, you can override the default value with the `page_size` parameter: + + curl https://api.linode.com/v4/linode/kernels?page_size=50 + +### Filter Results + +The API also supports filtering lists of results. Filters are passed using the `X-Filter` header and use JSON format. You can filter on almost any field that appears in a response object and the [API documentation](https://developers.linode.com/v4/introduction) specifies which fields are filterable. + +The following query uses the `deprecated` and `vendor` fields to return all current Debian images: + + curl https://api.linode.com/v4/images/ -H 'X-Filter: { "vendor": "Debian", "deprecated": false}' + + {{< highlight json "linenos=table" >}} +{ + "page": 1, + "pages": 1, + "data": [ + { + "size": 1024, + "type": "manual", + "label": "Debian 8", + "created_by": "linode", + "vendor": "Debian", + "is_public": true, + "created": "2015-04-27T20:26:41", + "deprecated": false, + "id": "linode/debian8", + "description": "" + }, + { + "size": 1100, + "type": "manual", + "label": "Debian 9", + "created_by": "linode", + "vendor": "Debian", + "is_public": true, + "created": "2017-06-16T20:02:29", + "deprecated": false, + "id": "linode/debian9", + "description": null + } + ], + "results": 2 +} +{{< /highlight >}} + +More complex searches are possible through the use of logical operators. Use `or` to return a list of all Debian and Ubuntu images: + + curl https://api.linode.com/v4/images/ -H "{"+or": [{"vendor":"Debian"}, {"vendor":"Ubuntu"}]}" + +See the [Linode API documentation](https://developers.linode.com/v4/filtering) for a full list of supported operators.