Bird
Raised Fist0
Terraformcloud~5 mins

Least privilege for Terraform service accounts - Commands & Configuration

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
When Terraform manages cloud resources, it needs permission to do so. Giving it only the exact permissions it needs keeps your cloud safe from mistakes or attacks.
When you want Terraform to create and manage resources but limit what it can change.
When you have multiple teams using Terraform and want to keep their access separate.
When you want to reduce risk by not giving Terraform full admin rights.
When you want to follow security rules that require minimal access.
When you want to audit and control what Terraform can do in your cloud.
Config File - main.tf
main.tf
provider "aws" {
  region = "us-east-1"
  assume_role {
    role_arn = aws_iam_role.terraform_role.arn
  }
}

resource "aws_iam_role" "terraform_role" {
  name = "terraform-service-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Principal = {
          Service = "ec2.amazonaws.com"
        },
        Action = "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_policy" "terraform_policy" {
  name        = "terraform-least-privilege-policy"
  description = "Policy with least privilege for Terraform to manage S3 buckets"

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = [
          "s3:ListBucket",
          "s3:GetBucketLocation"
        ],
        Resource = ["arn:aws:s3:::example-terraform-bucket"]
      },
      {
        Effect = "Allow",
        Action = [
          "s3:PutObject",
          "s3:GetObject",
          "s3:DeleteObject"
        ],
        Resource = ["arn:aws:s3:::example-terraform-bucket/*"]
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "attach_policy" {
  role       = aws_iam_role.terraform_role.name
  policy_arn = aws_iam_policy.terraform_policy.arn
}

This Terraform file creates a service role for Terraform with a policy that only allows managing a specific S3 bucket.

aws_iam_role: Defines the role Terraform will use.

assume_role_policy: Allows EC2 service to assume this role (common for Terraform running on EC2).

aws_iam_policy: Defines the least privilege permissions for S3 bucket actions.

aws_iam_role_policy_attachment: Attaches the policy to the role.

Commands
Initializes Terraform in the current directory to download providers and prepare the environment.
Terminal
terraform init
Expected OutputExpected
Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/aws... - Installing hashicorp/aws v4.0.0... - Installed hashicorp/aws v4.0.0 (signed by HashiCorp) 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.
Shows what Terraform will do based on the configuration, so you can review permissions and resources before applying.
Terminal
terraform plan
Expected OutputExpected
An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_iam_role.terraform_role will be created + resource "aws_iam_role" "terraform_role" { + arn = (known after apply) + assume_role_policy = jsonencode(...) + id = (known after apply) + name = "terraform-service-role" + unique_id = (known after apply) } # aws_iam_policy.terraform_policy will be created + resource "aws_iam_policy" "terraform_policy" { + arn = (known after apply) + description = "Policy with least privilege for Terraform to manage S3 buckets" + id = (known after apply) + name = "terraform-least-privilege-policy" + policy = jsonencode(...) } # aws_iam_role_policy_attachment.attach_policy will be created + resource "aws_iam_role_policy_attachment" "attach_policy" { + id = (known after apply) + policy_arn = (known after apply) + role = "terraform-service-role" } Plan: 3 to add, 0 to change, 0 to destroy.
Applies the configuration to create the role and policy with least privilege permissions without asking for confirmation.
Terminal
terraform apply -auto-approve
Expected OutputExpected
aws_iam_role.terraform_role: Creating... aws_iam_policy.terraform_policy: Creating... aws_iam_role.terraform_role: Creation complete after 2s [id=terraform-service-role] aws_iam_policy.terraform_policy: Creation complete after 2s [id=terraform-least-privilege-policy] aws_iam_role_policy_attachment.attach_policy: Creating... aws_iam_role_policy_attachment.attach_policy: Creation complete after 1s [id=terraform-service-role-terraform-least-privilege-policy] Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
-auto-approve - Skip confirmation prompt to apply changes immediately
Verifies the IAM role exists and shows its details to confirm creation.
Terminal
aws iam get-role --role-name terraform-service-role
Expected OutputExpected
{ "Role": { "Path": "/", "RoleName": "terraform-service-role", "RoleId": "AROAXXXXXXXXEXAMPLE", "Arn": "arn:aws:iam::123456789012:role/terraform-service-role", "CreateDate": "2024-06-01T12:00:00Z", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } } }
Key Concept

If you remember nothing else from this pattern, remember: always give Terraform only the permissions it absolutely needs to reduce risk.

Common Mistakes
Giving Terraform full admin permissions instead of least privilege.
This exposes your cloud to accidental or malicious changes beyond what Terraform needs.
Create and attach a policy that only allows the specific actions Terraform requires.
Not attaching the policy to the service account role.
Terraform will not have any permissions and will fail to create or manage resources.
Always attach the least privilege policy to the Terraform service role.
Using overly broad resource ARNs in the policy.
This grants more access than necessary, defeating least privilege.
Specify exact resource ARNs that Terraform should manage.
Summary
Create an IAM role for Terraform with an assume role policy.
Define a policy with only the permissions Terraform needs.
Attach the policy to the IAM role to enforce least privilege.
Use terraform init, plan, and apply to deploy the configuration.
Verify the role and permissions exist with AWS CLI commands.

Practice

(1/5)
1. What does the principle of least privilege mean for Terraform service accounts?
easy
A. Give only the permissions Terraform needs to do its job
B. Give Terraform full admin access to all cloud resources
C. Allow Terraform to access resources only during business hours
D. Share Terraform service account credentials with all team members

Solution

  1. Step 1: Understand least privilege concept

    Least privilege means giving only the minimum permissions needed to perform a task.
  2. Step 2: Apply to Terraform service accounts

    Terraform service accounts should have only the permissions required to manage infrastructure, nothing more.
  3. Final Answer:

    Give only the permissions Terraform needs to do its job -> Option A
  4. Quick Check:

    Least privilege = minimal needed permissions [OK]
Hint: Least privilege means minimal permissions only [OK]
Common Mistakes:
  • Giving Terraform full admin rights unnecessarily
  • Sharing credentials widely
  • Setting time-based access without need
2. Which Terraform configuration snippet correctly assigns least privilege to a service account for managing only compute instances?
easy
A. resource "google_project_iam_member" "compute_admin" { project = var.project_id role = "roles/compute.admin" member = "serviceAccount:${var.service_account_email}" }
B. resource "google_project_iam_member" "storage_admin" { project = var.project_id role = "roles/storage.admin" member = "serviceAccount:${var.service_account_email}" }
C. resource "google_project_iam_member" "viewer" { project = var.project_id role = "roles/viewer" member = "serviceAccount:${var.service_account_email}" }
D. resource "google_project_iam_member" "editor" { project = var.project_id role = "roles/editor" member = "serviceAccount:${var.service_account_email}" }

Solution

  1. Step 1: Identify the role for compute instance management

    The role "roles/compute.admin" allows managing compute instances specifically.
  2. Step 2: Match the role to the service account in Terraform

    The snippet assigns "roles/compute.admin" to the service account, limiting permissions to compute resources only.
  3. Final Answer:

    The snippet assigning roles/compute.admin to the service account -> Option A
  4. Quick Check:

    Assign specific roles, not broad ones [OK]
Hint: Match role to exact resource type needed [OK]
Common Mistakes:
  • Using broad roles like editor or admin unnecessarily
  • Assigning unrelated roles like storage.admin
  • Using viewer role which is read-only
3. Given this Terraform IAM binding snippet, what is the effective permission scope for the service account?
resource "google_project_iam_member" "sa_role" {
  project = "my-project"
  role    = "roles/storage.objectViewer"
  member  = "serviceAccount:terraform-sa@my-project.iam.gserviceaccount.com"
}
medium
A. Full access to all storage buckets and objects
B. No access to storage resources
C. Write access to storage buckets
D. Read-only access to storage objects only

Solution

  1. Step 1: Understand the role assigned

    The role "roles/storage.objectViewer" grants read-only access to storage objects.
  2. Step 2: Determine permission scope

    This role does not allow writing or bucket management, only viewing objects.
  3. Final Answer:

    Read-only access to storage objects only -> Option D
  4. Quick Check:

    roles/storage.objectViewer = read-only object access [OK]
Hint: Check role name keywords: viewer means read-only [OK]
Common Mistakes:
  • Confusing viewer with admin or editor roles
  • Assuming bucket write permissions
  • Thinking full storage access is granted
4. You wrote this Terraform code to assign a role to a service account but get an error:
resource "google_project_iam_member" "sa_role" {
  project = var.project_id
  role    = "roles/compute.viewer"
  member  = "serviceAccount:${var.service_account_email}"
  member  = "serviceAccount:extra@domain.com"
}
What is the problem?
medium
A. Role 'roles/compute.viewer' does not exist
B. Duplicate 'member' keys cause a syntax error
C. Service account email format is invalid
D. Project ID variable is missing

Solution

  1. Step 1: Check Terraform resource syntax

    Terraform resource blocks cannot have duplicate keys; 'member' is repeated twice here.
  2. Step 2: Understand correct way to assign multiple members

    To assign multiple members, use 'google_project_iam_binding' or multiple resources, not duplicate keys.
  3. Final Answer:

    Duplicate 'member' keys cause a syntax error -> Option B
  4. Quick Check:

    Duplicate keys in resource block = syntax error [OK]
Hint: No duplicate keys in Terraform blocks [OK]
Common Mistakes:
  • Using duplicate keys instead of lists or multiple resources
  • Assuming role name is invalid without checking
  • Ignoring variable definitions
5. You want to create a Terraform service account with least privilege to manage only network resources in a Google Cloud project. Which approach is best?
hard
A. Assign the role 'roles/owner' to the service account temporarily
B. Assign the role 'roles/editor' to the service account for all resources
C. Assign the role 'roles/compute.networkAdmin' to the service account only
D. Assign no roles and rely on default permissions

Solution

  1. Step 1: Identify the role for network management

    The role 'roles/compute.networkAdmin' grants permissions to manage network resources only.
  2. Step 2: Apply least privilege principle

    Assigning only this role limits the service account to network tasks, avoiding broad permissions.
  3. Step 3: Avoid broad or no permissions

    Roles like 'editor' or 'owner' are too broad; no roles means no access.
  4. Final Answer:

    Assign the role 'roles/compute.networkAdmin' to the service account only -> Option C
  5. Quick Check:

    Least privilege = specific role only [OK]
Hint: Pick the narrowest role matching needed tasks [OK]
Common Mistakes:
  • Using broad roles like editor or owner
  • Not assigning any role and expecting access
  • Assigning multiple unrelated roles