2014-07-28 19:43:00 +02:00
|
|
|
---
|
|
|
|
layout: "intro"
|
|
|
|
page_title: "Build Infrastructure"
|
2014-10-22 23:47:44 +02:00
|
|
|
sidebar_current: "gettingstarted-build"
|
2014-10-22 05:21:56 +02:00
|
|
|
description: |-
|
|
|
|
With Terraform installed, let's dive right into it and start creating some infrastructure.
|
2014-07-28 19:43:00 +02:00
|
|
|
---
|
|
|
|
|
|
|
|
# Build Infrastructure
|
|
|
|
|
|
|
|
With Terraform installed, let's dive right into it and start creating
|
|
|
|
some infrastructure.
|
|
|
|
|
|
|
|
We'll build infrastructure on
|
2018-10-22 20:03:07 +02:00
|
|
|
[AWS](https://aws.amazon.com) for this Getting Started guide
|
2014-07-28 19:43:00 +02:00
|
|
|
since it is popular and generally understood, but Terraform
|
|
|
|
can [manage many providers](/docs/providers/index.html),
|
|
|
|
including multiple providers in a single configuration.
|
|
|
|
Some examples of this are in the
|
|
|
|
[use cases section](/intro/use-cases.html).
|
|
|
|
|
|
|
|
If you don't have an AWS account,
|
2016-01-14 21:55:39 +01:00
|
|
|
[create one now](https://aws.amazon.com/free/).
|
2014-07-28 19:43:00 +02:00
|
|
|
For the getting started guide, we'll only be using resources
|
|
|
|
which qualify under the AWS
|
2016-01-14 21:55:39 +01:00
|
|
|
[free-tier](https://aws.amazon.com/free/),
|
2014-07-28 19:43:00 +02:00
|
|
|
meaning it will be free.
|
|
|
|
|
2014-10-22 16:01:17 +02:00
|
|
|
~> **Warning!** If you're not using an account that qualifies under the AWS
|
2016-01-14 21:55:39 +01:00
|
|
|
[free-tier](https://aws.amazon.com/free/), you may be charged to run these
|
2014-10-22 16:01:17 +02:00
|
|
|
examples. The most you should be charged should only be a few dollars, but
|
|
|
|
we're not responsible for any charges that may incur.
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
## Configuration
|
|
|
|
|
|
|
|
The set of files used to describe infrastructure in Terraform is simply
|
|
|
|
known as a Terraform _configuration_. We're going to write our first
|
|
|
|
configuration now to launch a single AWS EC2 instance.
|
|
|
|
|
|
|
|
The format of the configuration files is
|
|
|
|
[documented here](/docs/configuration/index.html).
|
|
|
|
Configuration files can
|
|
|
|
[also be JSON](/docs/configuration/syntax.html), but we recommend only using JSON when the
|
|
|
|
configuration is generated by a machine.
|
|
|
|
|
|
|
|
The entire configuration is shown below. We'll go over each part
|
|
|
|
after. Save the contents to a file named `example.tf`. Verify that
|
|
|
|
there are no other `*.tf` files in your directory, since Terraform
|
|
|
|
loads all of them.
|
|
|
|
|
2017-04-06 20:02:56 +02:00
|
|
|
```hcl
|
2014-07-28 19:43:00 +02:00
|
|
|
provider "aws" {
|
2016-05-20 23:19:01 +02:00
|
|
|
access_key = "ACCESS_KEY_HERE"
|
|
|
|
secret_key = "SECRET_KEY_HERE"
|
|
|
|
region = "us-east-1"
|
2014-07-28 19:43:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
resource "aws_instance" "example" {
|
2017-03-24 21:51:56 +01:00
|
|
|
ami = "ami-2757f631"
|
2016-05-20 23:19:01 +02:00
|
|
|
instance_type = "t2.micro"
|
2014-07-28 19:43:00 +02:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2016-09-01 12:30:55 +02:00
|
|
|
~> **Note**: The above configuration is designed to work on most EC2 accounts,
|
2016-05-20 23:19:01 +02:00
|
|
|
with access to a default VPC. For EC2 Classic users, please use `t1.micro` for
|
2017-06-16 09:18:29 +02:00
|
|
|
`instance_type`, and `ami-408c7f28` for the `ami`. If you use a region other than
|
|
|
|
`us-east-1` then you will need to choose an AMI in that region
|
|
|
|
as AMI IDs are region specific.
|
2016-05-20 23:19:01 +02:00
|
|
|
|
2014-07-28 19:43:00 +02:00
|
|
|
Replace the `ACCESS_KEY_HERE` and `SECRET_KEY_HERE` with your
|
|
|
|
AWS access key and secret key, available from
|
|
|
|
[this page](https://console.aws.amazon.com/iam/home?#security_credential).
|
|
|
|
We're hardcoding them for now, but will extract these into
|
|
|
|
variables later in the getting started guide.
|
|
|
|
|
2017-03-22 16:30:21 +01:00
|
|
|
~> **Note**: If you simply leave out AWS credentials, Terraform will
|
|
|
|
automatically search for saved API credentials (for example,
|
|
|
|
in `~/.aws/credentials`) or IAM instance profile credentials.
|
|
|
|
This option is much cleaner for situations where tf files are checked into
|
|
|
|
source control or where there is more than one admin user.
|
|
|
|
See details [here](https://aws.amazon.com/blogs/apn/terraform-beyond-the-basics-with-aws/).
|
|
|
|
Leaving IAM credentials out of the Terraform configs allows you to leave those
|
|
|
|
credentials out of source control, and also use different IAM credentials
|
|
|
|
for each user without having to modify the configuration files.
|
|
|
|
|
2014-07-28 19:43:00 +02:00
|
|
|
This is a complete configuration that Terraform is ready to apply.
|
|
|
|
The general structure should be intuitive and straightforward.
|
|
|
|
|
|
|
|
The `provider` block is used to configure the named provider, in
|
2018-01-03 21:00:19 +01:00
|
|
|
our case "aws". A provider is responsible for creating and
|
2018-10-22 20:03:07 +02:00
|
|
|
managing resources. Multiple provider blocks can exist in a
|
|
|
|
Terraform configuration if the infrastructure needs them.
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
The `resource` block defines a resource that exists within
|
|
|
|
the infrastructure. A resource might be a physical component such
|
|
|
|
as an EC2 instance, or it can be a logical resource such as
|
|
|
|
a Heroku application.
|
|
|
|
|
|
|
|
The resource block has two strings before opening the block:
|
|
|
|
the resource type and the resource name. In our example, the
|
|
|
|
resource type is "aws\_instance" and the name is "example."
|
|
|
|
The prefix of the type maps to the provider. In our case
|
|
|
|
"aws\_instance" automatically tells Terraform that it is
|
|
|
|
managed by the "aws" provider.
|
|
|
|
|
|
|
|
Within the resource block itself is configuration for that
|
|
|
|
resource. This is dependent on each resource provider and
|
|
|
|
is fully documented within our
|
|
|
|
[providers reference](/docs/providers/index.html). For our EC2 instance, we specify
|
2016-05-20 23:19:01 +02:00
|
|
|
an AMI for Ubuntu, and request a "t2.micro" instance so we
|
2014-07-28 19:43:00 +02:00
|
|
|
qualify under the free tier.
|
|
|
|
|
2017-06-03 01:50:32 +02:00
|
|
|
## Initialization
|
|
|
|
|
|
|
|
The first command to run for a new configuration -- or after checking out
|
|
|
|
an existing configuration from version control -- is `terraform init`, which
|
|
|
|
initializes various local settings and data that will be used by subsequent
|
|
|
|
commands.
|
|
|
|
|
2017-08-21 22:44:27 +02:00
|
|
|
Terraform uses a plugin based architecture to support the numerous infrastructure
|
2017-09-01 20:42:06 +02:00
|
|
|
and service providers available. As of Terraform version 0.10.0, each "Provider" is its
|
2017-08-21 22:44:27 +02:00
|
|
|
own encapsulated binary distributed separately from Terraform itself. The
|
|
|
|
`terraform init` command will automatically download and install any Provider
|
|
|
|
binary for the providers in use within the configuration, which in this case is
|
|
|
|
just the `aws` provider:
|
|
|
|
|
2017-06-03 01:50:32 +02:00
|
|
|
|
|
|
|
```
|
|
|
|
$ terraform init
|
|
|
|
Initializing the backend...
|
|
|
|
Initializing provider plugins...
|
|
|
|
- downloading plugin for provider "aws"...
|
|
|
|
|
|
|
|
The following providers do not have any version constraints in configuration,
|
|
|
|
so the latest version was installed.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
* provider.aws: version = "~> 1.0"
|
|
|
|
|
|
|
|
Terraform has been successfully initialized!
|
|
|
|
|
|
|
|
You may now begin working with Terraform. Try running "terraform plan" to see
|
|
|
|
any changes that are required for your infrastructure. All Terraform commands
|
|
|
|
should now work.
|
|
|
|
|
|
|
|
If you ever set or change modules or backend configuration for Terraform,
|
|
|
|
rerun this command to reinitialize your environment. If you forget, other
|
|
|
|
commands will detect it and remind you to do so if necessary.
|
|
|
|
```
|
|
|
|
|
|
|
|
The `aws` provider plugin is downloaded and installed in a subdirectory of
|
|
|
|
the current working directory, along with various other book-keeping files.
|
|
|
|
|
|
|
|
The output specifies which version of the plugin was installed, and suggests
|
|
|
|
specifying that version in configuration to ensure that running
|
|
|
|
`terraform init` in future will install a compatible version. This step
|
|
|
|
is not necessary for following the getting started guide, since this
|
|
|
|
configuration will be discarded at the end.
|
|
|
|
|
2017-10-30 21:33:27 +01:00
|
|
|
## Apply Changes
|
2014-07-28 19:43:00 +02:00
|
|
|
|
2017-10-30 21:33:27 +01:00
|
|
|
~> **Note:** The commands shown in this guide apply to Terraform 0.11 and
|
|
|
|
above. Earlier versions require using the `terraform plan` command to
|
|
|
|
see the execution plan before applying it. Use `terraform version`
|
|
|
|
to confirm your running version.
|
|
|
|
|
|
|
|
In the same directory as the `example.tf` file you created, run
|
|
|
|
`terraform apply`. You should see output similar to below, though we've
|
|
|
|
truncated some of the output to save space:
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
```
|
2017-10-30 21:33:27 +01:00
|
|
|
$ terraform apply
|
2017-04-06 20:02:56 +02:00
|
|
|
# ...
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
+ aws_instance.example
|
2017-03-24 21:51:56 +01:00
|
|
|
ami: "ami-2757f631"
|
2016-05-20 23:19:01 +02:00
|
|
|
availability_zone: "<computed>"
|
|
|
|
ebs_block_device.#: "<computed>"
|
|
|
|
ephemeral_block_device.#: "<computed>"
|
|
|
|
instance_state: "<computed>"
|
|
|
|
instance_type: "t2.micro"
|
|
|
|
key_name: "<computed>"
|
|
|
|
placement_group: "<computed>"
|
|
|
|
private_dns: "<computed>"
|
|
|
|
private_ip: "<computed>"
|
|
|
|
public_dns: "<computed>"
|
|
|
|
public_ip: "<computed>"
|
|
|
|
root_block_device.#: "<computed>"
|
|
|
|
security_groups.#: "<computed>"
|
|
|
|
source_dest_check: "true"
|
|
|
|
subnet_id: "<computed>"
|
|
|
|
tenancy: "<computed>"
|
|
|
|
vpc_security_group_ids.#: "<computed>"
|
2014-07-28 19:43:00 +02:00
|
|
|
```
|
|
|
|
|
2017-10-30 21:33:27 +01:00
|
|
|
This output shows the _execution plan_, describing which actions Terraform
|
|
|
|
will take in order to change real infrastructure to match the configuration.
|
2014-07-28 19:43:00 +02:00
|
|
|
The output format is similar to the diff format generated by tools
|
2017-10-30 21:33:27 +01:00
|
|
|
such as Git. The output has a `+` next to `aws_instance.example`,
|
2014-07-28 19:43:00 +02:00
|
|
|
meaning that Terraform will create this resource. Beneath that,
|
2017-04-06 20:02:56 +02:00
|
|
|
it shows the attributes that will be set. When the value displayed
|
2016-03-25 15:34:47 +01:00
|
|
|
is `<computed>`, it means that the value won't be known
|
2014-07-28 19:43:00 +02:00
|
|
|
until the resource is created.
|
|
|
|
|
|
|
|
|
2017-10-30 21:33:27 +01:00
|
|
|
If `terraform apply` failed with an error, read the error message
|
|
|
|
and fix the error that occurred. At this stage, it is likely to be a
|
|
|
|
syntax error in the configuration.
|
|
|
|
|
|
|
|
If the plan was created successfully, Terraform will now pause and wait for
|
|
|
|
approval before proceeding. If anything in the plan seems incorrect or
|
|
|
|
dangerous, it is safe to abort here with no changes made to your infrastructure.
|
|
|
|
In this case the plan looks acceptable, so type `yes` at the confirmation
|
|
|
|
prompt to proceed.
|
|
|
|
|
|
|
|
Executing the plan will take a few minutes since Terraform waits for the EC2
|
|
|
|
instance to become available:
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
```
|
2017-10-30 21:33:27 +01:00
|
|
|
# ...
|
2014-07-28 19:43:00 +02:00
|
|
|
aws_instance.example: Creating...
|
2017-03-24 21:51:56 +01:00
|
|
|
ami: "" => "ami-2757f631"
|
2016-05-20 23:19:01 +02:00
|
|
|
instance_type: "" => "t2.micro"
|
|
|
|
[...]
|
|
|
|
|
|
|
|
aws_instance.example: Still creating... (10s elapsed)
|
|
|
|
aws_instance.example: Creation complete
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
|
|
|
|
|
2017-04-06 20:02:56 +02:00
|
|
|
# ...
|
2014-07-28 19:43:00 +02:00
|
|
|
```
|
|
|
|
|
2018-10-22 20:03:07 +02:00
|
|
|
After this, Terraform is all done! You can go to the EC2 console to see the newly
|
2017-10-30 21:33:27 +01:00
|
|
|
created EC2 instance. (Make sure you're looking at the same region that was
|
|
|
|
configured in the provider configuration!)
|
2014-07-28 19:43:00 +02:00
|
|
|
|
2017-10-30 21:33:27 +01:00
|
|
|
Terraform also wrote some data into the `terraform.tfstate` file. This state
|
|
|
|
file is extremely important; it keeps track of the IDs of created resources
|
|
|
|
so that Terraform knows what it is managing. This file must be saved and
|
|
|
|
distributed to anyone who might run Terraform. It is generally recommended to
|
2017-03-15 18:20:26 +01:00
|
|
|
[setup remote state](https://www.terraform.io/docs/state/remote.html)
|
2017-10-30 21:33:27 +01:00
|
|
|
when working with Terraform, to share the state automatically, but this is
|
|
|
|
not necessary for simple situations like this Getting Started guide.
|
2014-07-28 19:43:00 +02:00
|
|
|
|
2017-10-30 21:33:27 +01:00
|
|
|
You can inspect the current state using `terraform show`:
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
```
|
2014-10-13 20:57:43 +02:00
|
|
|
$ terraform show
|
2014-07-28 19:43:00 +02:00
|
|
|
aws_instance.example:
|
2016-05-20 23:19:01 +02:00
|
|
|
id = i-32cf65a8
|
2017-03-24 21:51:56 +01:00
|
|
|
ami = ami-2757f631
|
2016-05-20 23:19:01 +02:00
|
|
|
availability_zone = us-east-1a
|
|
|
|
instance_state = running
|
|
|
|
instance_type = t2.micro
|
|
|
|
private_ip = 172.31.30.244
|
|
|
|
public_dns = ec2-52-90-212-55.compute-1.amazonaws.com
|
|
|
|
public_ip = 52.90.212.55
|
|
|
|
subnet_id = subnet-1497024d
|
|
|
|
vpc_security_group_ids.# = 1
|
|
|
|
vpc_security_group_ids.3348721628 = sg-67652003
|
2014-07-28 19:43:00 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
You can see that by creating our resource, we've also gathered
|
2017-10-30 21:33:27 +01:00
|
|
|
a lot of information about it. These values can actually be referenced
|
|
|
|
to configure other resources or outputs, which will be covered later in
|
2014-07-28 19:43:00 +02:00
|
|
|
the getting started guide.
|
|
|
|
|
|
|
|
## Provisioning
|
|
|
|
|
|
|
|
The EC2 instance we launched at this point is based on the AMI
|
|
|
|
given, but has no additional software installed. If you're running
|
|
|
|
an image-based infrastructure (perhaps creating images with
|
2015-01-03 19:31:53 +01:00
|
|
|
[Packer](https://www.packer.io)), then this is all you need.
|
2014-07-28 19:43:00 +02:00
|
|
|
|
|
|
|
However, many infrastructures still require some sort of initialization
|
2017-10-30 21:33:27 +01:00
|
|
|
or software provisioning step. Terraform supports provisioners,
|
2014-07-28 19:43:00 +02:00
|
|
|
which we'll cover a little bit later in the getting started guide,
|
|
|
|
in order to do this.
|
|
|
|
|
|
|
|
## Next
|
|
|
|
|
|
|
|
Congratulations! You've built your first infrastructure with Terraform.
|
|
|
|
You've seen the configuration syntax, an example of a basic execution
|
|
|
|
plan, and understand the state file.
|
|
|
|
|
|
|
|
Next, we're going to move on to [changing and destroying infrastructure](/intro/getting-started/change.html).
|