Kind: Standard (with locking via DynamoDB)
Stores the state as a given key in a given bucket on Amazon S3. This backend also supports state locking and consistency checking via Dynamo DB, which can be enabled by setting the dynamodb_table
field to an existing DynamoDB table name.
Warning! It is highly recommended that you enable Bucket Versioning on the S3 bucket to allow for state recovery in the case of accidental deletions and human error.
terraform { backend "s3" { bucket = "mybucket" key = "path/to/my/key" region = "us-east-1" } }
This assumes we have a bucket created called mybucket
. The Terraform state is written to the key path/to/my/key
.
Note that for the access credentials we recommend using a partial configuration.
Terraform will need the following AWS IAM permissions on the target backend bucket:
s3:ListBucket
on arn:aws:s3:::mybucket
s3:GetObject
on arn:aws:s3:::mybucket/path/to/my/key
s3:PutObject
on arn:aws:s3:::mybucket/path/to/my/key
This is seen in the following AWS IAM Statement:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::mybucket" }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::mybucket/path/to/my/key" } ] }
If you are using state locking, Terraform will need the following AWS IAM permissions on the DynamoDB table (arn:aws:dynamodb:::table/mytable
):
This is seen in the following AWS IAM Statement:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem" ], "Resource": "arn:aws:dynamodb:*:*:table/mytable" } ] }
To make use of the S3 remote state we can use the terraform_remote_state
data source.
data "terraform_remote_state" "network" { backend = "s3" config { bucket = "terraform-state-prod" key = "network/terraform.tfstate" region = "us-east-1" } }
The terraform_remote_state
data source will return all of the root outputs defined in the referenced remote state, an example output might look like:
data.terraform_remote_state.network: id = 2016-10-29 01:57:59.780010914 +0000 UTC addresses.# = 2 addresses.0 = 52.207.220.222 addresses.1 = 54.196.78.166 backend = s3 config.% = 3 config.bucket = terraform-state-prod config.key = network/terraform.tfstate config.region = us-east-1 elb_address = web-elb-790251200.us-east-1.elb.amazonaws.com public_subnet_id = subnet-1e05dd33
The following configuration options or environment variables are supported:
bucket
- (Required) The name of the S3 bucket. key
- (Required) The path to the state file inside the bucket. When using a non-default workspace, the state path will be /workspace_key_prefix/workspace_name/key
region
/ AWS_DEFAULT_REGION
- (Optional) The region of the S3 bucket. endpoint
/ AWS_S3_ENDPOINT
- (Optional) A custom endpoint for the S3 API. encrypt
- (Optional) Whether to enable server side encryption of the state file. acl
- Canned ACL to be applied to the state file. access_key
/ AWS_ACCESS_KEY_ID
- (Optional) AWS access key. secret_key
/ AWS_SECRET_ACCESS_KEY
- (Optional) AWS secret access key. kms_key_id
- (Optional) The ARN of a KMS Key to use for encrypting the state. lock_table
- (Optional, Deprecated) Use dynamodb_table
instead. dynamodb_table
- (Optional) The name of a DynamoDB table to use for state locking and consistency. The table must have a primary key named LockID. If not present, locking will be disabled. profile
- (Optional) This is the AWS profile name as set in the shared credentials file. shared_credentials_file
- (Optional) This is the path to the shared credentials file. If this is not set and a profile is specified, ~/.aws/credentials
will be used. token
- (Optional) Use this to set an MFA token. It can also be sourced from the AWS_SESSION_TOKEN
environment variable. role_arn
- (Optional) The role to be assumed. assume_role_policy
- (Optional) The permissions applied when assuming a role. external_id
- (Optional) The external ID to use when assuming the role. session_name
- (Optional) The session name to use when assuming the role. workspace_key_prefix
- (Optional) The prefix applied to the state path inside the bucket. This is only relevant when using a non-default workspace. This defaults to "env:" skip_credentials_validation
- (Optional) Skip the credentials validation via the STS API. skip_get_ec2_platforms
- (Optional) Skip getting the supported EC2 platforms. skip_region_validation
- (Optional) Skip validation of provided region name. skip_requesting_account_id
- (Optional) Skip requesting the account ID. skip_metadata_api_check
- (Optional) Skip the AWS Metadata API check. A common architectural pattern is for an organization to use a number of separate AWS accounts to isolate different teams and environments. For example, a "staging" system will often be deployed into a separate AWS account than its corresponding "production" system, to minimize the risk of the staging environment affecting production infrastructure, whether via rate limiting, misconfigured access controls, or other unintended interactions.
The S3 backend can be used in a number of different ways that make different tradeoffs between convenience, security, and isolation in such an organization. This section describes one such approach that aims to find a good compromise between these tradeoffs, allowing use of Terraform's workspaces feature to switch conveniently between multiple isolated deployments of the same configuration.
Use this section as a starting-point for your approach, but note that you will probably need to make adjustments for the unique standards and regulations that apply to your organization. You will also need to make some adjustments to this approach to account for existing practices within your organization, if for example other tools have previously been used to manage infrastructure.
Terraform is an administrative tool that manages your infrastructure, and so ideally the infrastructure that is used by Terraform should exist outside of the infrastructure that Terraform manages. This can be achieved by creating a separate administrative AWS account which contains the user accounts used by human operators and any infrastructure and tools used to manage the the other accounts. Isolating shared administrative tools from your main environments has a number of advantages, such as avoiding accidentally damaging the administrative infrastructure while changing the target infrastructure, and reducing the risk that an attacker might abuse production infrastructure to gain access to the (usually more privileged) administrative infrastructure.
Your administrative AWS account will contain at least the following items:
Provide the S3 bucket name and DynamoDB table name to Terraform within the S3 backend configuration using the bucket
and dynamodb_table
arguments respectively, and configure a suitable workspace_key_prefix
to contain the states of the various workspaces that will subsequently be created for this configuration.
For the sake of this section, the term "environment account" refers to one of the accounts whose contents are managed by Terraform, separate from the administrative account described above.
Your environment accounts will eventually contain your own product-specific infrastructure. Along with this it must contain one or more IAM roles that grant sufficient access for Terraform to perform the desired management tasks.
Each Administrator will run Terraform using credentials for their IAM user in the administrative account. IAM Role Delegation is used to grant these users access to the roles created in each environment account.
Full details on role delegation are covered in the AWS documentation linked above. The most important details are:
Since the purpose of the administrative account is only to host tools for managing other accounts, it is useful to give the administrative accounts restricted access only to the specific operations needed to assume the environment account role and access the Terraform state. By blocking all other access, you remove the risk that user error will lead to staging or production resources being created in the administrative account by mistake.
When configuring Terraform, use either environment variables or the standard credentials file ~/.aws/credentials
to provide the administrator user's IAM credentials within the administrative account to both the S3 backend and to Terraform's AWS provider.
Use conditional configuration to pass a different assume_role
value to the AWS provider depending on the selected workspace. For example:
variable "workspace_iam_roles" { default = { staging = "arn:aws:iam::STAGING-ACCOUNT-ID:role/Terraform" production = "arn:aws:iam::PRODUCTION-ACCOUNT-ID:role/Terraform" } } provider "aws" { # No credentials explicitly set here because they come from either the # environment or the global credentials file. assume_role = "${var.workspace_iam_roles[terraform.workspace]}" }
If workspace IAM roles are centrally managed and shared across many separate Terraform configurations, the role ARNs could also be obtained via a data source such as terraform_remote_state
to avoid repeating these values.
With the necessary objects created and the backend configured, run terraform init
to initialize the backend and establish an initial workspace called "default". This workspace will not be used, but is created automatically by Terraform as a convenience for users who are not using the workspaces feature.
Create a workspace corresponding to each key given in the workspace_iam_roles
variable value above:
$ terraform workspace new staging Created and switched to workspace "staging"! ... $ terraform workspace new production Created and switched to workspace "production"! ...
Due to the assume_role
setting in the AWS provider configuration, any management operations for AWS resources will be performed via the configured role in the appropriate environment AWS account. The backend operations, such as reading and writing the state from S3, will be performed directly as the administrator's own user within the administrative account.
$ terraform workspace select staging $ terraform apply ...
Teams that make extensive use of Terraform for infrastructure management often run Terraform in automation to ensure a consistent operating environment and to limit access to the various secrets and other sensitive information that Terraform configurations tend to require.
When running Terraform in an automation tool running on an Amazon EC2 instance, consider running this instance in the administrative account and using an instance profile in place of the various administrator IAM users suggested above. An IAM instance profile can also be granted cross-account delegation access via an IAM policy, giving this instance the access it needs to run Terraform.
To isolate access to different environment accounts, use a separate EC2 instance for each target account so that its access can be limited only to the single account.
Similar approaches can be taken with equivalent features in other AWS compute services, such as ECS.
In a simple implementation of the pattern described in the prior sections, all users have access to read and write states for all workspaces. In many cases it is desirable to apply more precise access constraints to the Terraform state objects in S3, so that for example only trusted administrators are allowed to modify the production state, or to control reading of a state that contains sensitive information.
Amazon S3 supports fine-grained access control on a per-object-path basis using IAM policy. A full description of S3's access control mechanism is beyond the scope of this guide, but an example IAM policy granting access to only a single state object within an S3 bucket is shown below:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::myorg-terraform-states" }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::myorg-terraform-states/myapp/production/tfstate" } ] }
It is not possible to apply such fine-grained access control to the DynamoDB table used for locking, so it is possible for any user with Terraform access to lock any workspace state, even if they do not have access to read or write that state. If a malicious user has such access they could block attempts to use Terraform against some or all of your workspaces as long as locking is enabled in the backend configuration.
© 2018 HashiCorpLicensed under the MPL 2.0 License.
https://www.terraform.io/docs/backends/types/s3.html