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

Leave a Reply

Your email address will not be published. Required fields are marked *


Related Cloud Tutorials

Securing your Infrastructure: Encrypting Terraform State Files with OpenTofu
Using the Terraform aws_route53_delegation_set, aws_route53_zone, and aws_route53_record resource blocks to configure DNS in AWS.
Using the Terraform aws_db_instance resource block to configure, launch, and secure RDS instances.
How to use the Terraform aws_instance resource block to configure, launch, and secure EC2 instances.
How to configure and use the Terraform aws_ami data source block to find and use AWS AMIs as templates (root volume snapshot with operating system and applications) for EC2 instances.
Javier Ruiz Cloud and SaaS Expert

Javier Ruiz

IT Wonder Lab tutorials are based on the diverse experience of Javier Ruiz, who founded and bootstrapped a SaaS company in the energy sector. His company, 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 over 25 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.

Are you 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