294 lines
11 KiB
Markdown
294 lines
11 KiB
Markdown
---
|
|
layout: "intro"
|
|
page_title: "Build Infrastructure"
|
|
sidebar_current: "gettingstarted-build"
|
|
description: |-
|
|
With Terraform installed, let's dive right into it and start creating some infrastructure.
|
|
---
|
|
|
|
# Build Infrastructure
|
|
|
|
With Terraform installed, let's dive right into it and start creating
|
|
some infrastructure.
|
|
|
|
We'll build infrastructure on
|
|
[AWS](https://aws.amazon.com) for the getting started guide
|
|
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,
|
|
[create one now](https://aws.amazon.com/free/).
|
|
For the getting started guide, we'll only be using resources
|
|
which qualify under the AWS
|
|
[free-tier](https://aws.amazon.com/free/),
|
|
meaning it will be free.
|
|
If you already have an AWS account, you may be charged some
|
|
amount of money, but it shouldn't be more than a few dollars
|
|
at most.
|
|
|
|
~> **Warning!** If you're not using an account that qualifies under the AWS
|
|
[free-tier](https://aws.amazon.com/free/), you may be charged to run these
|
|
examples. The most you should be charged should only be a few dollars, but
|
|
we're not responsible for any charges that may incur.
|
|
|
|
## 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.
|
|
|
|
```hcl
|
|
provider "aws" {
|
|
access_key = "ACCESS_KEY_HERE"
|
|
secret_key = "SECRET_KEY_HERE"
|
|
region = "us-east-1"
|
|
}
|
|
|
|
resource "aws_instance" "example" {
|
|
ami = "ami-2757f631"
|
|
instance_type = "t2.micro"
|
|
}
|
|
```
|
|
|
|
~> **Note**: The above configuration is designed to work on most EC2 accounts,
|
|
with access to a default VPC. For EC2 Classic users, please use `t1.micro` for
|
|
`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.
|
|
|
|
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.
|
|
|
|
~> **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.
|
|
|
|
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
|
|
our case "aws." A provider is responsible for creating and
|
|
managing resources. Multiple provider blocks can exist if a
|
|
Terraform configuration is composed of multiple providers,
|
|
which is a common situation.
|
|
|
|
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
|
|
an AMI for Ubuntu, and request a "t2.micro" instance so we
|
|
qualify under the free tier.
|
|
|
|
## 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.
|
|
|
|
Terraform uses a plugin based architecture to support the numerous infrastructure
|
|
and service providers available. As of Terraform version 0.10.0, each "Provider" is its
|
|
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:
|
|
|
|
|
|
```
|
|
$ 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.
|
|
|
|
## Execution Plan
|
|
|
|
Next, let's see what Terraform would do if we asked it to
|
|
apply this configuration. In the same directory as the
|
|
`example.tf` file you created, run `terraform plan`. You
|
|
should see output similar to what is copied below. We've
|
|
truncated some of the output to save space.
|
|
|
|
```
|
|
$ terraform plan
|
|
# ...
|
|
|
|
+ aws_instance.example
|
|
ami: "ami-2757f631"
|
|
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>"
|
|
```
|
|
|
|
`terraform plan` shows what changes Terraform will apply to
|
|
your infrastructure given the current state of your infrastructure
|
|
as well as the current contents of your configuration.
|
|
|
|
If `terraform plan` failed with an error, read the error message
|
|
and fix the error that occurred. At this stage, it is probably a
|
|
syntax error in the configuration.
|
|
|
|
The output format is similar to the diff format generated by tools
|
|
such as Git. The output has a "+" next to "aws\_instance.example",
|
|
meaning that Terraform will create this resource. Beneath that,
|
|
it shows the attributes that will be set. When the value displayed
|
|
is `<computed>`, it means that the value won't be known
|
|
until the resource is created.
|
|
|
|
## Apply
|
|
|
|
The plan looks good, our configuration appears valid, so it's time to
|
|
create real resources. Run `terraform apply` in the same directory
|
|
as your `example.tf`, and watch it go! It will take a few minutes
|
|
since Terraform waits for the EC2 instance to become available.
|
|
|
|
```
|
|
$ terraform apply
|
|
aws_instance.example: Creating...
|
|
ami: "" => "ami-2757f631"
|
|
instance_type: "" => "t2.micro"
|
|
[...]
|
|
|
|
aws_instance.example: Still creating... (10s elapsed)
|
|
aws_instance.example: Creation complete
|
|
|
|
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
|
|
|
|
# ...
|
|
```
|
|
|
|
Done! You can go to the AWS console to prove to yourself that the
|
|
EC2 instance has been created.
|
|
|
|
Terraform also puts some state into the `terraform.tfstate` file
|
|
by default. This state file is extremely important; it maps various
|
|
resource metadata to actual resource IDs 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
|
|
[setup remote state](https://www.terraform.io/docs/state/remote.html)
|
|
when working with Terraform. This will mean that any potential secrets
|
|
stored in the state file, will not be checked into version control
|
|
|
|
|
|
You can inspect the state using `terraform show`:
|
|
|
|
```
|
|
$ terraform show
|
|
aws_instance.example:
|
|
id = i-32cf65a8
|
|
ami = ami-2757f631
|
|
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
|
|
```
|
|
|
|
You can see that by creating our resource, we've also gathered
|
|
a lot more metadata about it. This metadata can actually be referenced
|
|
for other resources or outputs, which will be covered later in
|
|
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
|
|
[Packer](https://www.packer.io)), then this is all you need.
|
|
|
|
However, many infrastructures still require some sort of initialization
|
|
or software provisioning step. Terraform supports
|
|
provisioners,
|
|
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).
|