Implementing Security Best Practices
1. Using Least Privilege IAM
| Resource | Permission | Description | Notes |
|---|---|---|---|
| aws_iam_role | iam:CreateRole |
Create an IAM role | Required for creating roles with specific policies |
| aws_iam_policy | iam:CreatePolicy |
Create an IAM policy | Used to define permissions for roles and users |
| aws_iam_role_policy_attachment | iam:AttachRolePolicy |
Attach a policy to an IAM role | Assigns policies to roles for specific permissions |
Example: Define a minimal IAM role with least privilege
resource "aws_iam_role" "minimal_role" {
name = "minimal-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "minimal_role_policy" {
role = aws_iam_role.minimal_role.name
policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}
Note: The
PowerUserAccess policy is used here as an example. In production, you should define custom policies with only the necessary permissions.
Warning: Never use
AdministratorAccess policy in production environments. It grants full access to all AWS services and resources.
2. Encrypting State Files
| Option | Values | Notes |
|---|---|---|
| backend | "s3" |
Use S3 backend for state storage with encryption |
| encrypt | true |
Enable server-side encryption for state files |
| kms_key_id | "arn:aws:kms:us-east-1:123456789012:key/abcd1234-a1b2-c3d4-e5f6-7890ghij1234" |
KMS key ARN for encryption (optional) |
Example: S3 Backend with Encryption
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/abcd1234-a1b2-c3d4-e5f6-7890ghij1234"
}
}
Note: Ensure the IAM role or user has permissions to access the KMS key and S3 bucket.
3. Enabling State Locking
State locking in Terraform prevents concurrent operations that could lead to data corruption or inconsistent state. It ensures only one person or process can modify the state at a time.
| Option | Values | Description |
|---|---|---|
| backend | "s3" |
Specifies the backend to use for state storage, which supports state locking via DynamoDB. |
| dynamodb_table | "terraform-locks" |
Name of the DynamoDB table used for state locking. |
| lock_table | "terraform-locks" |
Alias for dynamodb_table, used in some backends. |
Example: Enabling State Locking with S3 Backend
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-locks"
}
}
Note: State locking is not enabled by default in all backends. Ensure that the backend supports state locking and configure it explicitly.
4. Validating Input Variables
| Property | Description | Validation Rule | Example |
|---|---|---|---|
| variable "instance_type" | Instance type for EC2 | Must be one of allowed types | |
| variable "vpc_cidr" | IP range for VPC | Must be valid CIDR block | |
| variable "environment" | Environment name | Must match pattern | |
Example: Validating Input with Condition
variable "allowed_regions" {
type = list(string)
default = ["us-east-1", "us-west-2"]
}
variable "region" {
type = string
validation {
condition = contains(var.allowed_regions, var.region)
error_message = "Region must be one of: us-east-1, us-west-2"
}
}
Note: Validation rules are enforced at plan time and prevent invalid configurations from being applied.
5. Using Sentinel Policies
| Policy Type | Description | Usage | Notes |
|---|---|---|---|
| Sentinel | Policy as Code framework for governance | Enforce compliance rules in Terraform | Can be applied at plan or apply time |
| Policy Enforcement Level | Enforcement levels (hard-mandatory, soft-advisory, or disabled) | Configured in policy configuration | Hard-mandatory stops execution if violated |
Example: Basic Sentinel Policy
import "tfplan"
# Enforce that all resources have a specific tag
main = rule {
all tfplan.resources where .type == "aws_instance" {
# Check if the resource has a specific tag
all .tags where .key == "Environment" {
# Ensure the value is not empty
.value != ""
}
}
}
Note: Sentinel policies are evaluated during the Terraform plan and apply phases, ensuring that infrastructure changes adhere to organizational standards.
Warning: Policies must be carefully designed to avoid blocking legitimate infrastructure changes. Always test policies in a non-production environment.
6. Scanning for Security Issues
| Tool | Description | Features | Integration |
|---|---|---|---|
| Terraform Cloud/Enterprise | Platform for managing Terraform configurations with built-in security scanning | NEW Policy as Code, automated drift detection | Native integration with Terraform CLI and HCL |
| Checkov | Static analysis tool for identifying misconfigurations in infrastructure as code | Supports Terraform, CloudFormation, Kubernetes, Dockerfile | CLI, GitHub Actions, GitLab CI, Jenkins |
| tfsec | Static analysis tool focused on security best practices in Terraform configurations | Open-source, checks for common security issues like exposed secrets or public S3 buckets | CLI, GitHub Actions, GitLab CI, CircleCI |
| Snyk | Security platform that includes infrastructure as code scanning capabilities | Scans for vulnerabilities in IaC files and integrates with CI/CD pipelines | CLI, GitHub Actions, Bitbucket Pipelines, Jenkins |
Note: Ensure that your scanning tools are updated regularly to detect new security vulnerabilities and misconfigurations.
7. Implementing Network Restrictions
| Resource | Properties | Description |
|---|---|---|
| aws_security_group | name, vpc_id, description |
Defines a security group for EC2 instances or other AWS resources. |
| aws_security_group_rule | security_group_id, type, from_port, to_port, protocol, cidr_blocks |
Defines rules for allowing or denying traffic to a security group. |
Example: Allow HTTPS traffic only from a specific IP
resource "aws_security_group" "allow_https" {
name = "allow-https"
description = "Allow HTTPS traffic from specific IP"
vpc_id = "vpc-12345678"
}
resource "aws_security_group_rule" "allow_https_from_ip" {
security_group_id = aws_security_group.allow_https.id
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["203.0.113.0/24"]
}
Example: Deny all outbound traffic
resource "aws_security_group" "deny_outbound" {
name = "deny-outbound"
description = "Deny all outbound traffic"
vpc_id = "vpc-12345678"
}
resource "aws_security_group_rule" "deny_all_outbound" {
security_group_id = aws_security_group.deny_outbound.id
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Deny all outbound traffic"
}
Note: By default, AWS security groups allow all outbound traffic. It's recommended to explicitly deny outbound traffic unless needed.
Key Concepts
- Security Groups act as virtual firewalls for EC2 instances and other AWS resources.
- Ingress Rules control incoming traffic to a resource.
- Egress Rules control outgoing traffic from a resource.
- Use
cidr_blocksto define IP ranges that are allowed or denied access.
8. Using Private Module Registry
| Feature | Description | Notes |
|---|---|---|
| module.source | registry.terraform.io/organization/module-name |
Path to private module in the registry |
| module.version | v1.2.3 |
Version constraint for module |
| terraform.required_providers | { "aws" = { source = "hashicorp/aws", version = "~> 5.0" } } |
Required provider configuration |
Example: Using Private Module
module "vpc" {
source = "registry.terraform.io/myorg/vpc"
version = "1.2.3"
cidr_block = "10.0.0.0/16"
region = "us-west-2"
}
Note: Private modules must be published to a registry that supports Terraform modules, such as
registry.terraform.io or a self-hosted solution like Terraform Registry Module.
Warning: Ensure that the registry is properly secured and access is restricted to authorized users only.
9. Auditing Changes
| Aspect | Description | Tools/Methods |
|---|---|---|
| Change Logs | Track all changes made to infrastructure configurations | terraform plan -out=tfplan followed by terraform show tfplan |
| Version Control Integration | Ensure all changes are tracked via Git or other VCS | Use git log, git diff to review changes |
| State File Monitoring | Monitor changes to state files for unauthorized modifications | Enable remote state with versioning and encryption |
Note: Regularly review
terraform plan outputs to understand the impact of proposed changes before applying them.
Warning: Ensure that state files are stored securely and access is restricted to authorized personnel only.
Key Points for Auditing Changes
- Always use
terraform planbefore applying changes - Store state files in a secure, remote backend with version control
- Integrate with CI/CD pipelines to automate change review processes
10. Implementing Drift Detection
Example: Using Terraform Cloud for Drift Detection
terraform plan -out=tfplan
terraform apply tfplan
| Feature | Description | Status |
|---|---|---|
| drift_detection | Enables drift detection in Terraform Cloud | NEW |
| drift_detection_enabled | Flag to enable or disable drift detection | NEW |
Note: Drift detection helps identify configuration inconsistencies between actual and expected infrastructure states.
Warning: Drift detection requires Terraform Cloud or Enterprise for full functionality.