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
Install Terraform / OpenTofu and GNU Privacy Guard, have an AWS account, and generate a Private Key pair
Terraform Plan to Create Users
Use the aws_iam_user
and aws_iam_access_key
resources to create users and their access key.
Init, Plan and Apply the Terraform Plan
Apply the terraform plan.
Extract the AWS IAM User Secret Access Key
Decrypt the Secret Access Key using the PGP Key Pair.
You need:
gpg
tool. See how to install and use gpg. 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:
# 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 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=="
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
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?