👉 AWS Debug Games (Beta) - Prove your AWS expertise by solving tricky challenges.

👉 AWS Debug Games - Prove your AWS expertise.

Terraform, can you keep a secret?

Andreas Wittig – 29 May 2020

Did you know that Terraform state can - and most likely does - contain sensitive data? A few examples of sensitive information stored in the Terraform state:

Terraform, can you keep a secret?

When using Terraform to provision cloud infrastructure on AWS, it is common to use S3 and DynamoDB to store the Terraform state as well. When doing so, Terraform will store sensitive information on S3. By default, the confidential data is neither encrypted at rest nor protected from access from other users or roles from the same AWS account.

Terraform remote state with S3 backend

When using the S3 backend to manage your Terraform state, you should not forget to enable encryption-at-rest and tight access control to the S3 bucket.

Read on to learn how to protect your sensitive information.


Looking for a new challenge?

  • DEMICON

    Cloud Operations Lead

    DEMICON • AWS Advanced Consulting Partner • Remote (Europe)
    service-delivery-management hiring devops platform

Encryption-at-Rest

Enabling S3 Default Encryption will automatically encrypt the Terraform state when stored on S3. It’s only server-side encryption, but still much better than storing your sensitive information unencrypted.

For full control, I recommend using a customer-managed CMK managed by the Key Management Service (KMS) when configuring the default encryption for your S3 bucket.

The following snippet shows how to enable default encryption with CloudFormation.

StateBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
KMSMasterKeyID: {'Fn::ImportValue': !Sub '${ParentKmsKeyStack}-KeyArn'}
SSEAlgorithm: 'aws:kms'
BucketName: !Ref TerrformStateIdentifier

Access Control

First of all, use a separate S3 bucket to store your Terraform state. I recommend creating an S3 bucket per AWS account and region.

Next, we need to follow the least privilege principle for read and write requests to the S3 bucket. The best way to restrict access to an S3 bucket very tightly is to make use of a bucket policy.

The following bucket policy grants the IAM role tfadmin full access to administer the S3 bucket. The IAM user tfuser is only granted read and write access to the objects within the bucket. Everyone else is neither allowed to modify the bucket nor to access the data stored within the bucket.

As the bucket policy uses Deny statements with an NotPrincipal element, it is necessary to specify the account (arn:aws:iam::111111111111:root in my example) as well as the assumed-role user when using IAM roles. Check out NotPrincipal with Deny to learn more.

{
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Deny",
"NotPrincipal": {
"AWS": [
"arn:aws:iam::111111111111:root",
"arn:aws:iam::111111111111:role/tfadmin",
"arn:aws:sts::111111111111:assumed-role/tfadmin/session",
]
},
"NotAction": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::tfstate-cloudonaut-demo",
"arn:aws:s3:::tfstate-cloudonaut-demo/*"
]
},
{
"Effect": "Deny",
"NotPrincipal": {
"AWS": [
"arn:aws:iam::111111111111:root"
"arn:aws:iam::111111111111:user/tfuser",
"arn:aws:iam::111111111111:role/tfadmin",
"arn:aws:sts::111111111111:assumed-role/tfadmin/session",
]
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::tfstate-cloudonaut-demo",
"arn:aws:s3:::tfstate-cloudonaut-demo/*"
]
}
]
}

Summary

Terraform will not keep your secrets! Sensitive information like database passwords, secrets stored within the Parameter Store, or shared keys for a VPN connection is at risk. Therefore, you should enable encryption-at-rest and use a bucket policy to tightly control who can access your Terraform state when using Terraform’s S3 backend.

Are you looking for a ready-to-use implementation? I’ve added a CloudFormation template to our open-source project widdix/aws-cf-templates that you can use to create an S3 bucket and DynamoDB optimized for the use as Terraform state backend. Check out the documentation to get started.

Become a cloudonaut supporter

Andreas Wittig

Andreas Wittig ( Email Twitter LinkedIn Mastodon )

We launched the cloudonaut blog in 2015. Since then, we have published 365 articles, 68 podcast episodes, and 68 videos. It's all free and means a lot of work in our spare time. We enjoy sharing our AWS knowledge with you.

Please support us

Have you learned something new by reading, listening, or watching our content? With your help, we can spend enough time to keep publishing great content in the future. Learn more

$
Amount must be a multriply of 5. E.g, 5, 10, 15.

Thanks to Alan Leech, Alex DeBrie, Christopher Hipwell, Jason Yorty, Jeff Finley, jhoadley, Johannes Konings, John Culkin, Jonathan Deamer, Juraj Martinka, Ken Snyder, Markus Ellers, Oriol Rodriguez, Ross Mohan, sam onaga, Satyendra Sharma, Simon Devlin, Todd Valentine, Victor Grenu, and all anonymous supporters for your help! We also want to thank all supporters who purchased a cloudonaut t-shirt.