Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/iam-user/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AWS IAM User Example

Configuration in this directory creates an IAM user with a random password, a pair of IAM access/secret keys and uploads IAM SSH public key.
Configuration in this directory creates an IAM user with a random password, a pair of IAM access/secret keys, uploads IAM SSH public key, and demonstrates inline policy creation.
User password and secret key is encrypted using public key of keybase.io user named `test`.

# Usage
Expand Down
24 changes: 24 additions & 0 deletions examples/iam-user/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@ module "iam_user2" {
create_login_profile = false
create_access_key = true

create_inline_policy = true
inline_policy_permissions = {
s3_read_access = {
effect = "Allow"
actions = [
"s3:GetObject",
"s3:ListBucket"
]
resources = [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket/*"
]
}
cloudwatch_logs = {
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = ["*"]
}
}

tags = local.tags
}

Expand Down
2 changes: 1 addition & 1 deletion modules/iam-role-for-service-accounts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
> Upgrade to use EKS Pod Identity instead of IRSA
> A similar module for EKS Pod Identity is available [here](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity).

> [!INFO]
> [!IMPORTANT]
> The [karpenter](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/modules/karpenter) sub-module contains the necessary AWS resources for running Karpenter, including the Karpenter controller IAM role & policy

Creates an IAM role which can be assumed by AWS EKS `ServiceAccount`s with optional policies for commonly used controllers/custom resources within EKS. The optional policies supported include:
Expand Down
6 changes: 3 additions & 3 deletions modules/iam-role-for-service-accounts/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,11 @@ resource "aws_iam_role_policy_attachment" "this" {
################################################################################

locals {
create_iam_role_inline_policy = var.create && var.create_inline_policy
create_inline_policy = var.create && var.create_inline_policy
}

data "aws_iam_policy_document" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0
count = local.create_inline_policy ? 1 : 0

source_policy_documents = var.source_inline_policy_documents
override_policy_documents = var.override_inline_policy_documents
Expand Down Expand Up @@ -259,7 +259,7 @@ data "aws_iam_policy_document" "inline" {
}

resource "aws_iam_role_policy" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0
count = local.create_inline_policy ? 1 : 0

role = aws_iam_role.this[0].name
name = var.use_name_prefix ? null : var.name
Expand Down
6 changes: 3 additions & 3 deletions modules/iam-role/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,11 @@ resource "aws_iam_role_policy_attachment" "this" {
################################################################################

locals {
create_iam_role_inline_policy = var.create && var.create_inline_policy
create_inline_policy = var.create && var.create_inline_policy
}

data "aws_iam_policy_document" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0
count = local.create_inline_policy ? 1 : 0

source_policy_documents = var.source_inline_policy_documents
override_policy_documents = var.override_inline_policy_documents
Expand Down Expand Up @@ -358,7 +358,7 @@ data "aws_iam_policy_document" "inline" {
}

resource "aws_iam_role_policy" "inline" {
count = local.create_iam_role_inline_policy ? 1 : 0
count = local.create_inline_policy ? 1 : 0

role = aws_iam_role.this[0].name
name = var.use_name_prefix ? null : var.name
Expand Down
8 changes: 7 additions & 1 deletion modules/iam-user/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AWS IAM User Terraform Module

Creates an IAM user with ability to create a login profile, access key, and SSH key.
Creates an IAM user with ability to create a login profile, access key, SSH key, and inline policies.

## Usage

Expand Down Expand Up @@ -52,8 +52,10 @@ No modules.
| [aws_iam_access_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
| [aws_iam_user.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource |
| [aws_iam_user_login_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_login_profile) | resource |
| [aws_iam_user_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy) | resource |
| [aws_iam_user_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy_attachment) | resource |
| [aws_iam_user_ssh_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_ssh_key) | resource |
| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |

## Inputs

Expand All @@ -62,16 +64,20 @@ No modules.
| <a name="input_access_key_status"></a> [access\_key\_status](#input\_access\_key\_status) | Access key status to apply | `string` | `null` | no |
| <a name="input_create"></a> [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no |
| <a name="input_create_access_key"></a> [create\_access\_key](#input\_create\_access\_key) | Whether to create IAM access key | `bool` | `true` | no |
| <a name="input_create_inline_policy"></a> [create\_inline\_policy](#input\_create\_inline\_policy) | Determines whether to create an inline policy | `bool` | `false` | no |
| <a name="input_create_login_profile"></a> [create\_login\_profile](#input\_create\_login\_profile) | Whether to create IAM user login profile | `bool` | `true` | no |
| <a name="input_create_ssh_key"></a> [create\_ssh\_key](#input\_create\_ssh\_key) | Whether to upload a public ssh key to the IAM user | `bool` | `false` | no |
| <a name="input_force_destroy"></a> [force\_destroy](#input\_force\_destroy) | When destroying this user, destroy even if it has non-Terraform-managed IAM access keys, login profile or MFA devices. Without force\_destroy a user with non-Terraform-managed access keys and login profile will fail to be destroyed | `bool` | `false` | no |
| <a name="input_inline_policy_permissions"></a> [inline\_policy\_permissions](#input\_inline\_policy\_permissions) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions | <pre>map(object({<br/> sid = optional(string)<br/> actions = optional(list(string))<br/> not_actions = optional(list(string))<br/> effect = optional(string, "Allow")<br/> resources = optional(list(string))<br/> not_resources = optional(list(string))<br/> principals = optional(list(object({<br/> type = string<br/> identifiers = list(string)<br/> })))<br/> not_principals = optional(list(object({<br/> type = string<br/> identifiers = list(string)<br/> })))<br/> condition = optional(list(object({<br/> test = string<br/> variable = string<br/> values = list(string)<br/> })))<br/> }))</pre> | `null` | no |
| <a name="input_name"></a> [name](#input\_name) | Desired name for the IAM user | `string` | `""` | no |
| <a name="input_override_inline_policy_documents"></a> [override\_inline\_policy\_documents](#input\_override\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no |
| <a name="input_password_length"></a> [password\_length](#input\_password\_length) | The length of the generated password | `number` | `null` | no |
| <a name="input_password_reset_required"></a> [password\_reset\_required](#input\_password\_reset\_required) | Whether the user should be forced to reset the generated password on first login | `bool` | `true` | no |
| <a name="input_path"></a> [path](#input\_path) | Desired path for the IAM user | `string` | `null` | no |
| <a name="input_permissions_boundary"></a> [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the user | `string` | `null` | no |
| <a name="input_pgp_key"></a> [pgp\_key](#input\_pgp\_key) | Either a base-64 encoded PGP public key, or a keybase username in the form `keybase:username`. Used to encrypt password and access key | `string` | `null` | no |
| <a name="input_policies"></a> [policies](#input\_policies) | Policies to attach to the IAM user in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no |
| <a name="input_source_inline_policy_documents"></a> [source\_inline\_policy\_documents](#input\_source\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no |
| <a name="input_ssh_key_encoding"></a> [ssh\_key\_encoding](#input\_ssh\_key\_encoding) | Specifies the public key encoding format to use in the response. To retrieve the public key in ssh-rsa format, use SSH. To retrieve the public key in PEM format, use PEM | `string` | `"SSH"` | no |
| <a name="input_ssh_public_key"></a> [ssh\_public\_key](#input\_ssh\_public\_key) | The SSH public key. The public key must be encoded in ssh-rsa format or PEM format | `string` | `""` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
Expand Down
64 changes: 64 additions & 0 deletions modules/iam-user/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,70 @@ resource "aws_iam_user_policy_attachment" "additional" {
policy_arn = each.value
}

################################################################################
# IAM User Inline policy
################################################################################

locals {
create_inline_policy = var.create && var.create_inline_policy
}

data "aws_iam_policy_document" "inline" {
count = local.create_inline_policy ? 1 : 0

source_policy_documents = var.source_inline_policy_documents
override_policy_documents = var.override_inline_policy_documents

dynamic "statement" {
for_each = var.inline_policy_permissions != null ? var.inline_policy_permissions : {}

content {
sid = try(coalesce(statement.value.sid, statement.key))
actions = statement.value.actions
not_actions = statement.value.not_actions
effect = statement.value.effect
resources = statement.value.resources
not_resources = statement.value.not_resources

dynamic "principals" {
for_each = statement.value.principals != null ? statement.value.principals : []

content {
type = principals.value.type
identifiers = principals.value.identifiers
}
}

dynamic "not_principals" {
for_each = statement.value.not_principals != null ? statement.value.not_principals : []

content {
type = not_principals.value.type
identifiers = not_principals.value.identifiers
}
}

dynamic "condition" {
for_each = statement.value.condition != null ? statement.value.condition : []

content {
test = condition.value.test
values = condition.value.values
variable = condition.value.variable
}
}
}
}
}

resource "aws_iam_user_policy" "inline" {
count = local.create_inline_policy ? 1 : 0

user = aws_iam_user.this[0].name
name = var.name
policy = data.aws_iam_policy_document.inline[0].json
}

################################################################################
# User Login Profile
################################################################################
Expand Down
48 changes: 48 additions & 0 deletions modules/iam-user/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,51 @@ variable "ssh_public_key" {
type = string
default = ""
}

################################################################################
# Inline policy
################################################################################

variable "create_inline_policy" {
description = "Determines whether to create an inline policy"
type = bool
default = false
}

variable "source_inline_policy_documents" {
description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s"
type = list(string)
default = []
}

variable "override_inline_policy_documents" {
description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`"
type = list(string)
default = []
}

variable "inline_policy_permissions" {
description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions"
type = map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
default = null
}
36 changes: 20 additions & 16 deletions wrappers/iam-user/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ module "wrapper" {

for_each = var.items

access_key_status = try(each.value.access_key_status, var.defaults.access_key_status, null)
create = try(each.value.create, var.defaults.create, true)
create_access_key = try(each.value.create_access_key, var.defaults.create_access_key, true)
create_login_profile = try(each.value.create_login_profile, var.defaults.create_login_profile, true)
create_ssh_key = try(each.value.create_ssh_key, var.defaults.create_ssh_key, false)
force_destroy = try(each.value.force_destroy, var.defaults.force_destroy, false)
name = try(each.value.name, var.defaults.name, "")
password_length = try(each.value.password_length, var.defaults.password_length, null)
password_reset_required = try(each.value.password_reset_required, var.defaults.password_reset_required, true)
path = try(each.value.path, var.defaults.path, null)
permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null)
pgp_key = try(each.value.pgp_key, var.defaults.pgp_key, null)
policies = try(each.value.policies, var.defaults.policies, {})
ssh_key_encoding = try(each.value.ssh_key_encoding, var.defaults.ssh_key_encoding, "SSH")
ssh_public_key = try(each.value.ssh_public_key, var.defaults.ssh_public_key, "")
tags = try(each.value.tags, var.defaults.tags, {})
access_key_status = try(each.value.access_key_status, var.defaults.access_key_status, null)
create = try(each.value.create, var.defaults.create, true)
create_access_key = try(each.value.create_access_key, var.defaults.create_access_key, true)
create_inline_policy = try(each.value.create_inline_policy, var.defaults.create_inline_policy, false)
create_login_profile = try(each.value.create_login_profile, var.defaults.create_login_profile, true)
create_ssh_key = try(each.value.create_ssh_key, var.defaults.create_ssh_key, false)
force_destroy = try(each.value.force_destroy, var.defaults.force_destroy, false)
inline_policy_permissions = try(each.value.inline_policy_permissions, var.defaults.inline_policy_permissions, null)
name = try(each.value.name, var.defaults.name, "")
override_inline_policy_documents = try(each.value.override_inline_policy_documents, var.defaults.override_inline_policy_documents, [])
password_length = try(each.value.password_length, var.defaults.password_length, null)
password_reset_required = try(each.value.password_reset_required, var.defaults.password_reset_required, true)
path = try(each.value.path, var.defaults.path, null)
permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null)
pgp_key = try(each.value.pgp_key, var.defaults.pgp_key, null)
policies = try(each.value.policies, var.defaults.policies, {})
source_inline_policy_documents = try(each.value.source_inline_policy_documents, var.defaults.source_inline_policy_documents, [])
ssh_key_encoding = try(each.value.ssh_key_encoding, var.defaults.ssh_key_encoding, "SSH")
ssh_public_key = try(each.value.ssh_public_key, var.defaults.ssh_public_key, "")
tags = try(each.value.tags, var.defaults.tags, {})
}