Security best practices - AWS Prescriptive Guidance

Security best practices

Properly managing authentication, access controls, and security is critical for secure usage of the Terraform AWS Provider. This section outlines best practices around:

  • IAM roles and permissions for least-privilege access

  • Securing credentials to help prevent unauthorized access to AWS accounts and resources

  • Remote state encryption to help protect sensitive data

  • Infrastructure and source code scanning to identify misconfigurations

  • Access controls for remote state storage

  • Sentinel policy enforcement to implement governance guardrails

Following these best practices helps strengthen your security posture when you use Terraform to manage AWS infrastructure.

Follow the principle of least privilege

Least privilege is a fundamental security principle that refers to granting only the minimum permissions required for a user, process, or system to perform its intended functions. It's a core concept in access control and a preventative measure against unauthorized access and potential data breaches.

The principle of least privilege is emphasized multiple times in this section because it directly relates to how Terraform authenticates and runs actions against cloud providers such as AWS.

When you use Terraform to provision and manage AWS resources, it acts on behalf of an entity (user or role) that requires appropriate permissions to make API calls. Not following least privilege opens up major security risks:

  • If Terraform has excessive permissions beyond what's needed, an unintended misconfiguration could make undesired changes or deletions.

  • Overly permissive access grants increase the scope of impact if Terraform state files or credentials are compromised.

  • Not following least privilege goes against security best practices and regulatory compliance requirements for granting minimal required access.

Use IAM roles

Use IAM roles instead of IAM users wherever possible to enhance security with the Terraform AWS Provider. IAM roles provide temporary security credentials that automatically rotate, which eliminates the need to manage long-term access keys. Roles also offer precise access controls through IAM policies.

Grant least privilege access by using IAM policies

Carefully construct IAM policies to ensure that roles and users have only the minimum set of permissions that are required for their workload. Start with an empty policy and iteratively add allowed services and actions. To accomplish this:

  • Enable IAM Access Analyzer to evaluate policies and highlight unused permissions that can be removed.

  • Manually review policies to remove any capabilities that aren't essential for the role's intended responsibility.

  • Use IAM policy variables and tags to simplify permission management.

Well-constructed policies grant just enough access to accomplish the workload's responsibilities and nothing more. Define actions at the operation level, and allow calls only to required APIs on specific resources.

Following this best practice reduces the scope of impact and follows the fundamental security principles of separation of duties and least privilege access. Start strict and open access gradually as needed, instead of starting open and trying to restrict access later.

Assume IAM roles for local authentication

When you run Terraform locally, avoid configuring static access keys. Instead, use IAM roles to grant privileged access temporarily without exposing long-term credentials.

First, create an IAM role with the necessary minimum permissions and add a trust relationship that allows the IAM role to be assumed by your user account or federated identity. This authorizes temporary usage of the role.

Trust relationship policy example:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/terraform-execution" }, "Action": "sts:AssumeRole" } ] }

Then, run the AWS CLI command aws sts assume-role to retrieve short-lived credentials for the role. These credentials are typically valid for one hour.

AWS CLI command example:

aws sts assume-role --role-arn arn:aws:iam::111122223333:role/terraform-execution --role-session-name terraform-session-example

The output of the command contains an access key, secret key, and session token that you can use to authenticate to AWS:

{ "AssumedRoleUser": { "AssumedRoleId": "AROA3XFRBF535PLBIFPI4:terraform-session-example", "Arn": "arn:aws:sts::111122223333:assumed-role/terraform-execution/terraform-session-example" }, "Credentials": { "SecretAccessKey": " wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "SessionToken": " AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4OlgkBN9bkUDNCJiBeb/AXlzBBko7b15fjrBs2+cTQtpZ3CYWFXG8C5zqx37wnOE49mRl/+OtkIKGO7fAE", "Expiration": "2024-03-15T00:05:07Z", "AccessKeyId": "ASIAIOSFODNN7EXAMPLE" } }

The AWS Provider can also automatically handle assuming the role.

Provider configuration example for assuming an IAM role:

provider "aws" { assume_role { role_arn = "arn:aws:iam::111122223333:role/terraform-execution" session_name = "terraform-session-example" } }

This grants elevated privilege strictly for the Terraform session's duration. The temporary keys cannot be leaked because they expire automatically after the maximum duration of the session.

The key benefits of this best practice include improved security compared with long-lived access keys, fine-grained access controls on the role for least privileges, and the ability to easily revoke access by modifying the role's permissions. By using IAM roles, you also avoid having to directly store secrets locally in scripts or on disk, which helps you share Terraform configuration securely across a team.

Use IAM roles for Amazon EC2 authentication

When you run Terraform from Amazon Elastic Compute Cloud (Amazon EC2) instances, avoid storing long-term credentials locally. Instead, use IAM roles and instance profiles to grant least-privilege permissions automatically.

First, create an IAM role with the minimum permissions and assign the role to the instance profile. The instance profile allows EC2 instances to inherit the permissions defined in the role. Then, launch instances by specifying that instance profile. The instance will authenticate through the attached role.

Before you run any Terraform operations, verify that the role is present in the instance metadata to confirm that the credentials were successfully inherited.

TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/iam/security-credentials/

This approach avoids hardcoding permanent AWS keys into scripts or Terraform configuration within the instance. The temporary credentials are made available to Terraform transparently through the instance role and profile.

The key benefits of this best practice include improved security over long-term credentials, reduced credential management overhead, and consistency between development, test, and production environments. IAM role authentication simplifies Terraform runs from EC2 instances while enforcing least-privilege access.

Use dynamic credentials for HCP Terraform workspaces

HCP Terraform is a managed service provided by HashiCorp that helps teams use Terraform to provision and manage infrastructure across multiple projects and environments. When you run Terraform in HCP Terraform, use dynamic credentials to simplify and secure AWS authentication. Terraform automatically exchanges temporary credentials on each run without needing IAM role assumption.

Benefits include easier secret rotation, centralized credential management across workspaces, least-privilege permissions, and eliminating hardcoded keys. Relying on hashed ephemeral keys enhances security compared with long-lived access keys.

Use IAM roles in AWS CodeBuild

In AWS CodeBuild, run your builds by using an IAM role that's assigned to the CodeBuild project. This allows each build to automatically inherit temporary credentials from the role instead of using long-term keys.

Run GitHub Actions remotely on HCP Terraform

Configure GitHub Actions workflows to run Terraform remotely on HCP Terraform workspaces. Rely on dynamic credentials and remote state locking instead of GitHub secrets management.

Use GitHub Actions with OIDC and configure the AWS Credentials action

Use the OpenID Connect (OIDC) standard to federate GitHub Actions identity through IAM. Use the Configure AWS Credentials action to exchange the GitHub token for temporary AWS credentials without needing long-term access keys.

Use GitLab with OIDC and the AWS CLI

Use the OIDC standard to federate GitLab identities through IAM for temporary access. By relying on OIDC, you avoid having to directly manage long-term AWS access keys within GitLab. Credentials are exchanged just-in-time, which improves security. Users also gain least privilege access according to the permissions in the IAM role.

Use unique IAM users with legacy automation tools

If you have automation tools and scripts that lack native support for using IAM roles, you can create individual IAM users to grant programmatic access. The principle of least privilege still applies. Minimize policy permissions and rely on separate roles for each pipeline or script. As you migrate to more modern tools or tools, begin supporting roles natively and gradually transition to them.

Warning

IAM users have long-term credentials, which present a security risk. To help mitigate this risk, we recommend that you provide these users with only the permissions they require to perform the task and that you remove these users when they are no longer needed.

Use the Jenkins AWS Credentials plugin

Use the AWS Credentials plugin in Jenkins to centrally configure and inject AWS credentials into builds dynamically. This avoids checking secrets into source control.

Continuously monitor, validate, and optimize least privilege

Over time, additional permissions might get granted that can exceed the minimum policies required. Continuously analyze access to identify and remove any unnecessary entitlements.

Continuously monitor access key usage

If you cannot avoid using access keys, use IAM credential reports to find unused access keys that are older than 90 days, and revoke inactive keys across both user accounts and machine roles. Alert administrators to manually confirm the removal of keys for active employees and systems.

Monitoring key usage helps you optimize permissions because you can identify and remove unused entitlements. When you follow this best practice with access key rotation, it limits credential lifespan and enforces least privilege access.

AWS provides several services and features that you can use to set up alerts and notifications for administrators. Here are some options:

  • AWS Config: You can use AWS Config rules to evaluate the configuration settings of your AWS resources, including IAM access keys. You can create custom rules to check for specific conditions, such as unused access keys that are older than a specific number of days. When a rule is violated, AWS Config can start an evaluation for remediation or send notifications to an Amazon Simple Notification Service (Amazon SNS) topic.

  • AWS Security Hub: Security Hub provides a comprehensive view of your AWS account's security posture and can help detect and notify you about potential security issues, including unused or inactive IAM access keys. Security Hub can integrate with Amazon EventBridge and Amazon SNS or AWS Chatbot to send notifications to administrators.

  • AWS Lambda: Lambda functions can be called by various events, including Amazon CloudWatch Events or AWS Config rules. You can write custom Lambda functions to evaluate IAM access key usage, perform additional checks, and send notifications by using services such as Amazon SNS or AWS Chatbot.

Continually validate IAM policies

Use IAM Access Analyzer to evaluate policies that are attached to roles and identify any unused services or excess actions that were granted. Implement periodic access reviews to manually verify that policies match current requirements.

Compare the existing policy with the policy generated by IAM Access Analyzer and remove any unnecessary permissions. You should also provide reports to users and automatically revoke unused permissions after a grace period. This helps ensure that minimal policies remain in effect.

Proactively and frequently revoking obsolete access minimizes the credentials that might be at risk during a breach. Automation provides sustainable, long-term credential hygiene and permissions optimization. Following this best practice limits the scope of impact by proactively enforcing least privilege across AWS identities and resources.

Secure remote state storage

Remote state storage refers to storing the Terraform state file remotely instead of locally on the machine where Terraform is running. The state file is crucial because it keeps track of the resources that are provisioned by Terraform and their metadata.

Failure to secure remote state can lead to serious issues such as loss of state data, inability to manage infrastructure, inadvertent resource deletion, and exposure of sensitive information that might be present in the state file. For this reason, securing remote state storage is crucial for production-grade Terraform usage.

Enable encryption and access controls

Use Amazon Simple Storage Service (Amazon S3) server-side encryption (SSE) to encrypt remote state at rest.

Limit direct access to collaborative workflows

  • Structure collaboration workflows in HCP Terraform or in a CI/CD pipeline within your Git repository to limit direct state access.

  • Rely on pull requests, run approvals, policy checks, and notifications to coordinate changes.

Following these guidelines helps secure sensitive resource attributes and avoids conflicts with team members' changes. Encryption and strict access protections help reduce the attack surface, and collaboration workflows enable productivity.

Use AWS Secrets Manager

There are many resources and data sources in Terraform that store secret values in plaintext in the state file. Avoid storing secrets in state―use AWS Secrets Manager instead.

Instead of attempting to manually encrypt sensitive values, rely on Terraform's built-in support for sensitive state management. When exporting sensitive values to output, make sure that the values are marked as sensitive.

Continuously scan infrastructure and source code

Proactively scan both infrastructure and source code continuously for risks such as exposed credentials or misconfigurations to harden your security posture. Address findings promptly by reconfiguring or patching resources.

Use AWS services for dynamic scanning

Use AWS native tools such as Amazon Inspector, AWS Security Hub, Amazon Detective, and Amazon GuardDuty to monitor provisioned infrastructure across accounts and Regions. Schedule recurring scans in Security Hub to track deployment and configuration drift. Scan EC2 instances, Lambda functions, containers, S3 buckets, and other resources.

Perform static analysis

Embed static analyzers such as Checkov directly into CI/CD pipelines to scan Terraform configuration code (HCL) and identify risks preemptively before deployment. This moves security checks to an earlier point in the development process (referred to as shifting left) and prevents misconfigured infrastructure.

Ensure prompt remediation

For all scan findings, ensure prompt remediation by either updating Terraform configuration, applying patches, or reconfiguring resources manually as appropriate. Lower risk levels by addressing the root causes.

Using both infrastructure scanning and code scanning provides layered insight across Terraform configurations, the provisioned resources, and application code. This maximizes the coverage of risk and compliance through preventative, detective, and reactive controls while embedding security earlier into the software development lifecycle (SDLC).

Enforce policy checks

Use code frameworks such as HashiCorp Sentinel policies to provide governance guardrails and standardized templates for infrastructure provisioning with Terraform.

Sentinel policies can define requirements or restrictions on Terraform configuration to align with organizational standards and best practices. For example, you can use Sentinel policies to:

  • Require tags on all resources.

  • Restrict instance types to an approved list.

  • Enforce mandatory variables.

  • Prevent the destruction of production resources.

Embedding policy checks into Terraform configuration lifecycles enables proactive enforcement of standards and architecture guidelines. Sentinel provides shared policy logic that helps accelerate development while preventing unapproved practices.