How to Create AWS IAM users with Terraform & OpenTofu

Programmatically creating AWS users using IaC tools like Terraform & OpenTofu

This how-to shows using Terraform and OpenTofu to create AWS IAM Users and their corresponding access keys to allow programmatic calls to AWS from the AWS CLI, AWS Tools for PowerShell, AWS SDKs, or direct AWS API calls. It uses a PGP Key Pair to encrypt/decrypt the Secret access key.

How to Create AWS IAM users with Terraform & OpenTofu

  1. Prerequisites

    Install Terraform / OpenTofu and GNU Privacy Guard, have an AWS account, and generate a Private Key pair

  2. Terraform Plan to Create Users

    Use the aws_iam_user and aws_iam_access_key resources to create users and their access key.

  3. Init, Plan and Apply the Terraform Plan

    Apply the terraform plan.

  4. Extract the AWS IAM User Secret Access Key

    Decrypt the Secret Access Key using the PGP Key Pair.

Prerequisites

You need:

  • A working Terraform or OpenTofu installation. See Installing OpenTofu,
  • A Key Pair generated with the PrettyGood Privacy (PGP) gpg tool. See how to install and use gpg.

Terraform Plan to Create Users

Create a Terraform infrastructure definition file.

The resource "aws_iam_access_key" is used to create the Access Key. It tells Terraform to encrypt the value using the public PGP key in base64 format stored in the location specified inside the file path (e.g. ~/keys/itwonderlab.com/ditwl_infradmin_gpg_b64_public.key).

The output of resource "aws_iam_access_key" is stored in two Terraform output variables:

  • project_1_admin_access_key: Access key
  • project_1_admin_encrypted_secret_access_key: Encrypted Secret access key
# Copyright (C) 2018 - 2023 IT Wonder Lab (https://www.itwonderlab.com)
#
# This software may be modified and distributed under the terms
# of the MIT license.  See the LICENSE file for details.
# -------------------------------- WARNING --------------------------------
# IT Wonder Lab's best practices for infrastructure include modularizing 
# Terraform/OpenTofu configuration. 
# In this example, we define everything in a single file. 
# See other tutorials for best practices at itwonderlab.com
# -------------------------------- WARNING --------------------------------

#Define Terrraform Providers and Backend
terraform {
  required_version = "> 1.5"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  # Stores the state as a given key in a given bucket on Amazon S3.
  backend "s3" {
    bucket                  = "ditwonderlab"
    key                     = "ditwl_infradmin/aws-terraform-user/terraform"
    profile                 = "ditwl_infradmin"
    region                  = "us-east-1"
    shared_credentials_file = "~/.aws/credentials"
  }
}

# Provider AWS
provider "aws" {
  shared_credentials_files = ["~/.aws/credentials"]
  profile                  = "ditwl_infradmin"
  region                   = "us-east-1" 
}

# USERS & ACCESS KEYS

resource "aws_iam_user" "project_1_admin" {
  name = "ditwl_user_project_1_admin"
  path = "/ditwl/tutorial/"
  tags = {
    "Name"           = "ditwl_user_project_1_admin"
  }
}

# Create an Access Key for the user and encrypt its value using PGP public key
resource "aws_iam_access_key" "project_1_admin" {
  user    = aws_iam_user.project_1_admin.name
  pgp_key = file("~/keys/itwonderlab.com/ditwl_infradmin_gpg_b64_public.key")
}

# Store the Access key in an Output variables
output "project_1_admin_access_key" {
  value = aws_iam_access_key.project_1_admin.id
}

# Store the Encrypted Secret access key in an Output variables
output "project_1_admin_encrypted_secret_access_key" {
  value = aws_iam_access_key.project_1_admin.encrypted_secret_access_key
}

Init, Plan, and Apply the Terraform infrastructure definition

Init, Plan, and Apply the configuration

$ terraform init
...
$ terraform plan
...
$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_iam_access_key.project_1_admin will be created
  + resource "aws_iam_access_key" "project_1_admin" {
      + create_date                    = (known after apply)
      + encrypted_secret               = (known after apply)
      + encrypted_ses_smtp_password_v4 = (known after apply)
      + id                             = (known after apply)
      + key_fingerprint                = (known after apply)
      + pgp_key                        = <<-EOT
            mQGNBGVThvgBDAC35weEx21DrMjKCE/bODUnzewNpOgMsqWWOUmnYSMrPDVhoWvQ5jt/4YIAlqLy
            ....
            YstYpiGx7Bh1gjnVlXPBWyuYy8LKzGAvdYccT5sM7irYbJOf6sIrQ623A6o6eWxeA/9j+erW/icK
            srxxj4Hr+T4jplnBiyVB4U4/EFEZTc5ofl4qkbtnCP+P58MFxydByJKQy7BK7IpSvw==
        EOT
      + secret                         = (sensitive value)
      + ses_smtp_password_v4           = (sensitive value)
      + status                         = "Active"
      + user                           = "ditwl_user_project_1_admin"
    }

  # aws_iam_user.project_1_admin will be created
  + resource "aws_iam_user" "project_1_admin" {
      + arn           = (known after apply)
      + force_destroy = false
      + id            = (known after apply)
      + name          = "ditwl_user_project_1_admin"
      + path          = "/ditwl/tutorial/"
      + tags          = {
          + "Name" = "ditwl_user_project_1_admin"
        }
      + tags_all      = {
          + "Name" = "ditwl_user_project_1_admin"
        }
      + unique_id     = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + project_1_admin_access_key                  = (known after apply)
  + project_1_admin_encrypted_secret_access_key = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_iam_user.project_1_admin: Creating...
aws_iam_user.project_1_admin: Creation complete after 2s [id=ditwl_user_project_1_admin]
aws_iam_access_key.project_1_admin: Creating...
aws_iam_access_key.project_1_admin: Creation complete after 0s [id=AKIAW....YUX5]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

project_1_admin_access_key = "AKIAW....YUX5"
project_1_admin_encrypted_secret_access_key = "wcDMAzjqv6YGlVfzAQv+LYk0siFvDoOM...0Zuq92r3+3kn8Ww=="
$ tofu init
...
$ tofu plan
...
$ tofu apply

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

OpenTofu will perform the following actions:

  # aws_iam_access_key.project_1_admin will be created
  + resource "aws_iam_access_key" "project_1_admin" {
      + create_date                    = (known after apply)
      + encrypted_secret               = (known after apply)
      + encrypted_ses_smtp_password_v4 = (known after apply)
      + id                             = (known after apply)
      + key_fingerprint                = (known after apply)
      + pgp_key                        = <<-EOT
            mQGNBGVThvgBDAC35weEx21DrMjKCE/bODUnzewNpOgMsqWWOUmnYSMrPDVhoWvQ5jt/4YIAlqLy
            ....
            YstYpiGx7Bh1gjnVlXPBWyuYy8LKzGAvdYccT5sM7irYbJOf6sIrQ623A6o6eWxeA/9j+erW/icK
            srxxj4Hr+T4jplnBiyVB4U4/EFEZTc5ofl4qkbtnCP+P58MFxydByJKQy7BK7IpSvw==
        EOT
      + secret                         = (sensitive value)
      + ses_smtp_password_v4           = (sensitive value)
      + status                         = "Active"
      + user                           = "ditwl_user_project_1_admin"
    }

  # aws_iam_user.project_1_admin will be created
  + resource "aws_iam_user" "project_1_admin" {
      + arn           = (known after apply)
      + force_destroy = false
      + id            = (known after apply)
      + name          = "ditwl_user_project_1_admin"
      + path          = "/ditwl/tutorial/"
      + tags          = {
          + "Name" = "ditwl_user_project_1_admin"
        }
      + tags_all      = {
          + "Name" = "ditwl_user_project_1_admin"
        }
      + unique_id     = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + project_1_admin_access_key                  = (known after apply)
  + project_1_admin_encrypted_secret_access_key = (known after apply)

Do you want to perform these actions?
  OpenTofu will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_iam_user.project_1_admin: Creating...
aws_iam_user.project_1_admin: Creation complete after 1s [id=ditwl_user_project_1_admin]
aws_iam_access_key.project_1_admin: Creating...
aws_iam_access_key.project_1_admin: Creation complete after 0s [id=AKIAW....YUX5]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

project_1_admin_access_key = "AKIAW....YUX5"
project_1_admin_encrypted_secret_access_key = "wcDMAzjqv6YGlVfzAQv+LYk0siFvDoOM...0Zuq92r3+3kn8Ww=="

Extract the Private Secret Access Key

Use the Terraform / OpenTofu output command to access the encrypted access key output project_1_admin_encrypted_secret_access_key variable, decode from base64, and decrypt using the gpg tool. The decrypted Secret Access Key is stored in the file project_1_admin_encrypted_secret

$ terraform output -raw project_1_admin_encrypted_secret_access_key | base64 --decode | gpg --decrypt --output  project_1_admin_encrypted_secret

Related Cloud Tutorials

Terraform OpenTofu AWS EKS
This how-to demonstrates how to use Terraform to create an AWS EKS cluster and deploy an application along with a Load Balancer on top.
Terraform AWS ECS Fargate
How-to use Terraform or OpenTofu to create an AWS ECS (Elastic Container Service) running in Fargate and deploy a Docker container.
GnuPG
Geneation of a PrettyGood Privacy (PGP) Key Pair for automated AWS IAM user access key creation with Terraform.
Terraform Kubernetes
How to publish multiple replicas of an Application (from the Docker Registry) and create a NodePort in Kubernetes using Terraform (in 10 seconds)
Terraform Migration to OpenTofu
How to migrate existing AWS Terraform-managed infrastructure that uses remote backend storage (e.g. S3) to OpenTofu.
Javier Ruiz Cloud and SaaS Expert

Javier Ruiz

IT Wonder Lab tutorials are based on the rich and diverse experience of Javier Ruiz, who founded and bootstrapped a SaaS company in the energy sector. His company, which was later acquired by a NASDAQ traded company, managed over €2 billion per year of electricity for prominent energy producers across Europe and America. Javier has more than 20 years of experience in building and managing IT companies, developing cloud infrastructure, leading cross-functional teams, and transitioning his own company from on-premises, consulting, and custom software development to a successful SaaS model that scaled globally.

Looking for cloud automation best practices tailored to your company?

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram