Creating an AWS IAM user with Terraform
Tags: terraform, dataops, devops, cloud
Published: 2023-10-10 10:06:46

## TL;DR

Here's the Terraform code to create an IAM user with access to EC2 and RDS instances:

```terraform
data "aws_iam_policy_document" "my_project_policy" {
    statement {
        resources = [aws_instance.my_project_ec2.arn]
        actions = ["ec2:*"]
        effect = "Allow" # Default is Allow
    }

    statement {
        resources = ["aws_db_instance.my_project_db.arn"]
        actions = ["rds:DescribeDBInstances",
                   "rds:DescribeDBClusters",
                   "rds:DescribeGlobalClusters"]
    }             
}

# Provides an IAM user
resource "aws_iam_user" "my_project_user" {
  name = "my-project-user"
}

```


--- 

Now that we already created an IAM role so the EC2 instance can access the RDS instance, it's time to create an IAM user with specific (and restricted) access to use/manage the infrastructure we just created.

Here's a quick recap on IAM:
- Identity and Access Management (IAM) is a service that allows you to manage access to AWS resources securely
- IAM enables you to create and manage AWS users and groups and control their level of access to AWS resources
- We create groups with permissions and then add users to these groups 
- We create roles and assign them to AWS services so they communicate with each other 

## Creating an IAM user

Here are the things we'll need to add to our Terraform code:

- A policy defining permissions
- An IAM user
- Combine the two above

Let's start creating the policy:

```terraform

data "aws_iam_policy_document" "my_project_policy" {
    statement {
        resources = [aws_instance.my_project_ec2.arn]
        actions = ["ec2:*"]
        effect = "Allow" # Default is Allow
    }

    statement {
        resources = ["aws_db_instance.my_project_db.arn"]
        actions = ["rds:DescribeDBInstances",
                   "rds:DescribeDBClusters",
                   "rds:DescribeGlobalClusters"]
    }             
}

# Provides an IAM user
resource "aws_iam_user" "my_project_user" {
  name = "my-project-user"
}

# Provides an IAM policy attached to a user
resource "aws_iam_user_policy" "my_project_user_policy" {
    name = "my-project-policy"
    user = aws_iam_user.my_project_user.name
    policy = data.aws_iam_policy_document.my_project_user_policy.json
}
```

You probably noticed this is the first time we're using a `data` block instead of a `resource` block. The key difference here is that resource blocks are used to create/manage infrastructure resources, while data blocks are used to fetch and reference information from external services.

Now we create the user:

```terraform
# Provides an IAM user
resource "aws_iam_user" "my_project_user" {
  name = "my-project-user"
}
```

And attach the policy to the user:

```terraform
# Provides an IAM policy attached to a user
resource "aws_iam_user_policy" "my_project_user_policy" {
    name = "my-project-policy"
    user = aws_iam_user.my_project_user.name
    policy = data.aws_iam_policy_document.my_project_user_policy.json
}
```

## Wrapping up

That's it, now we're really done with our little project. After going through all those steps, here's what we've got:

- An EC2 instance where we can deploy our Python project
- An RDS Postgres instance where that Python project can persist data
- An IAM role that allows the EC2 instance to connect to the RDS Postgres instance
- An IAM user with proper permissions to interact with the EC2 and RDS instances