diff --git a/.header.md b/.header.md index 42d1ce6..df32cf4 100644 --- a/.header.md +++ b/.header.md @@ -11,6 +11,7 @@ - Dynamic Reference of Existing Groups - AWS Managed Policy Support - Customer Managed Policy Support +- Dynamic Application Creation (with Portal Options, Users and Groups assignments and Assignments Access Scopes configuration) ## Important @@ -37,6 +38,32 @@ }, } + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } + ``` The object/principal names are referenced throughout the module. Failure to follow this guidance may lead to unintentional errors such as the following: @@ -167,6 +194,36 @@ module "aws-iam-identity-center" { } ``` +## Basic Usage - Create Applications and assign to Users and Groups + +``` + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } +``` + ## Contributing See the `CONTRIBUTING.md` file for information on how to contribute. diff --git a/README.md b/README.md index 6cf8d50..6217b68 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ - Dynamic Reference of Existing Groups - AWS Managed Policy Support - Customer Managed Policy Support +- Dynamic Application Creation (with Portal Options, Users and Groups assignments and Assignments Access Scopes configuration) ## Important @@ -38,6 +39,32 @@ }, } + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } + ``` The object/principal names are referenced throughout the module. Failure to follow this guidance may lead to unintentional errors such as the following: @@ -168,6 +195,36 @@ module "aws-iam-identity-center" { } ``` +## Basic Usage - Create Applications and assign to Users and Groups + +``` + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } +``` + ## Contributing See the `CONTRIBUTING.md` file for information on how to contribute. @@ -199,6 +256,11 @@ No modules. | [aws_identitystore_group_membership.sso_group_membership_existing_google_sso_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/identitystore_group_membership) | resource | | [aws_identitystore_user.sso_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/identitystore_user) | resource | | [aws_ssoadmin_account_assignment.account_assignment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_account_assignment) | resource | +| [aws_ssoadmin_application.sso_apps](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_application) | resource | +| [aws_ssoadmin_application_access_scope.sso_apps_assignments_access_scope](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_application_access_scope) | resource | +| [aws_ssoadmin_application_assignment.sso_apps_groups_assignments](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_application_assignment) | resource | +| [aws_ssoadmin_application_assignment.sso_apps_users_assignments](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_application_assignment) | resource | +| [aws_ssoadmin_application_assignment_configuration.sso_apps_assignments_configs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_application_assignment_configuration) | resource | | [aws_ssoadmin_customer_managed_policy_attachment.pset_customer_managed_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_customer_managed_policy_attachment) | resource | | [aws_ssoadmin_managed_policy_attachment.pset_aws_managed_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_managed_policy_attachment) | resource | | [aws_ssoadmin_permission_set.pset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set) | resource | @@ -222,6 +284,7 @@ No modules. | [existing\_sso\_groups](#input\_existing\_sso\_groups) | Names of the existing groups that you wish to reference from IAM Identity Center. |
map(object({| `{}` | no | | [existing\_sso\_users](#input\_existing\_sso\_users) | Names of the existing users that you wish to reference from IAM Identity Center. |
group_name = string
}))
map(object({| `{}` | no | | [permission\_sets](#input\_permission\_sets) | Permission Sets that you wish to create in IAM Identity Center. This variable is a map of maps containing Permission Set names as keys. See permission\_sets description in README for information about map values. | `any` | `{}` | no | +| [sso\_applications](#input\_sso\_applications) | List of applications to be created in IAM Identity Center |
user_name = string
group_membership = optional(list(string), null) // only used if your IdP only syncs users, and you wish to manage which groups they should go in
}))
map(object({| `{}` | no | | [sso\_groups](#input\_sso\_groups) | Names of the groups you wish to create in IAM Identity Center. |
name = string
application_provider_arn = string
description = optional(string)
portal_options = optional(object({
sign_in_options = optional(object({
application_url = optional(string)
origin = string
}))
visibility = optional(string)
}))
status = string # acceptable values are "ENABLED" or "DISABLED"
client_token = optional(string)
tags = optional(map(string))
assignment_required = bool # Resource: aws_ssoadmin_application_assignment_configuration
assignments_access_scope = optional(
list(object({
authorized_targets = optional(list(string)) # List of application names
scope = string
}))
) # Resource: aws_ssoadmin_application_access_scope
group_assignments = optional(list(string)) # Resource aws_ssoadmin_application_assignment, keeping it separated for groups
user_assignments = optional(list(string)) # Resource aws_ssoadmin_application_assignment, keeping it separated for users
}))
map(object({| `{}` | no | | [sso\_users](#input\_sso\_users) | Names of the users you wish to create in IAM Identity Center. |
group_name = string
group_description = optional(string, null)
}))
map(object({| `{}` | no | @@ -231,5 +294,8 @@ No modules. |------|-------------| | [account\_assignment\_data](#output\_account\_assignment\_data) | Tuple containing account assignment data | | [principals\_and\_assignments](#output\_principals\_and\_assignments) | Map containing account assignment data | +| [sso\_applications\_arns](#output\_sso\_applications\_arns) | A map of SSO Applications ARNs created by this module | +| [sso\_applications\_group\_assignments](#output\_sso\_applications\_group\_assignments) | A map of SSO Applications assignments with groups created by this module | +| [sso\_applications\_user\_assignments](#output\_sso\_applications\_user\_assignments) | A map of SSO Applications assignments with users created by this module | | [sso\_groups\_ids](#output\_sso\_groups\_ids) | A map of SSO groups ids created by this module | \ No newline at end of file diff --git a/VERSION b/VERSION index 0ec25f7..95cf410 100644 --- a/VERSION +++ b/VERSION @@ -1 +1,3 @@ -v1.0.0 +v1.0.1 + + diff --git a/data.tf b/data.tf index 48976ab..0ae661a 100644 --- a/data.tf +++ b/data.tf @@ -98,4 +98,3 @@ data "aws_ssoadmin_permission_set" "existing_permission_sets" { # permission_sets = "ViewOnlyAccess" # account_ids = "111111111111" # } - diff --git a/examples/create-apps-and-assignments/.header.md b/examples/create-apps-and-assignments/.header.md new file mode 100644 index 0000000..140d38b --- /dev/null +++ b/examples/create-apps-and-assignments/.header.md @@ -0,0 +1,66 @@ +This directory contains examples of using the module to **create** applications, application assignments configurations, users, groups and application assignments to both users and groups + +**IMPORTANT:** Ensure that the name of your object matches the name of your principal (e.g. user name or group name). See the following example with object/principal names 'Admin' and 'nuzumaki': + +```hcl + sso_groups = { + Admin : { + group_name = "Admin" + group_description = "Admin IAM Identity Center Group" + }, + } + + // Create desired USERS in IAM Identity Center + sso_users = { + nuzumaki : { + group_membership = ["Admin",] + user_name = "nuzumaki" + given_name = "Naruto" + family_name = "Uzumaki" + email = "nuzumaki@hiddenleaf.village" + }, + } + + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } + +``` + +These names are referenced throughout the module. Failure to do this may lead to unintentional errors such as the following: + +``` +Error: Invalid index +│ +│ on ../../main.tf line 141, in resource "aws_identitystore_group_membership" "sso_group_membership": +│ 141: member_id = (contains(local.this_users, each.value.user_name) ? aws_identitystore_user.sso_users[each.value.user_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.user_name].id) +│ ├──────────────── +│ │ aws_identitystore_user.sso_users is object with 2 attributes +│ │ each.value.user_name is "nuzumaki" +│ +│ The given key does not identify an element in this collection value. +``` + +To resolve this, ensure your object and principal names are the same and re-run `terraform plan` and `terraform apply`. diff --git a/examples/create-apps-and-assignments/README.md b/examples/create-apps-and-assignments/README.md new file mode 100644 index 0000000..ce9d3fd --- /dev/null +++ b/examples/create-apps-and-assignments/README.md @@ -0,0 +1,98 @@ + +This directory contains examples of using the module to **create** applications, application assignments configurations, users, groups and application assignments to both users and groups + +**IMPORTANT:** Ensure that the name of your object matches the name of your principal (e.g. user name or group name). See the following example with object/principal names 'Admin' and 'nuzumaki': + +```hcl + sso_groups = { + Admin : { + group_name = "Admin" + group_description = "Admin IAM Identity Center Group" + }, + } + + // Create desired USERS in IAM Identity Center + sso_users = { + nuzumaki : { + group_membership = ["Admin",] + user_name = "nuzumaki" + given_name = "Naruto" + family_name = "Uzumaki" + email = "nuzumaki@hiddenleaf.village" + }, + } + + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } + +``` + +These names are referenced throughout the module. Failure to do this may lead to unintentional errors such as the following: + +``` +Error: Invalid index +│ +│ on ../../main.tf line 141, in resource "aws_identitystore_group_membership" "sso_group_membership": +│ 141: member_id = (contains(local.this_users, each.value.user_name) ? aws_identitystore_user.sso_users[each.value.user_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.user_name].id) +│ ├──────────────── +│ │ aws_identitystore_user.sso_users is object with 2 attributes +│ │ each.value.user_name is "nuzumaki" +│ +│ The given key does not identify an element in this collection value. +``` + +To resolve this, ensure your object and principal names are the same and re-run `terraform plan` and `terraform apply`. + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [aws-iam-identity-center](#module\_aws-iam-identity-center) | ../.. | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_ssm_parameter.account1_account_id](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | + +## Inputs + +No inputs. + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/examples/create-apps-and-assignments/locals.tf b/examples/create-apps-and-assignments/locals.tf new file mode 100644 index 0000000..922f5e9 --- /dev/null +++ b/examples/create-apps-and-assignments/locals.tf @@ -0,0 +1,14 @@ +# Fetch Account Id from SSM Parameter Store +data "aws_ssm_parameter" "account1_account_id" { + name = "tf-aws-iam-idc-module-testing-account1-account-id" // replace with your SSM Parameter Key +} + +locals { + # Account IDs + account1_account_id = nonsensitive(data.aws_ssm_parameter.account1_account_id.value) + # account1_account_id = "111111111111" + # account2_account_id = "222222222222" + # account3_account_id = "333333333333" + # account4_account_id = "444444444444" + +} diff --git a/examples/create-apps-and-assignments/main.tf b/examples/create-apps-and-assignments/main.tf new file mode 100644 index 0000000..ad0e8ed --- /dev/null +++ b/examples/create-apps-and-assignments/main.tf @@ -0,0 +1,128 @@ +module "aws-iam-identity-center" { + source = "../.." // local example + # source = "aws-ia/iam-identity-center/aws" // remote example + + // Create desired GROUPS in IAM Identity Center + sso_groups = { + Admin : { + group_name = "Admin" + group_description = "Admin IAM Identity Center Group" + }, + Dev : { + group_name = "Dev" + group_description = "Dev IAM Identity Center Group" + } + } + + // Create desired USERS in IAM Identity Center + sso_users = { + nuzumaki : { + group_membership = ["Admin"] + user_name = "nuzumaki" + given_name = "Naruto" + family_name = "Uzumaki" + email = "nuzumaki@hiddenleaf.village" + }, + suchiha : { + group_membership = ["Dev"] + user_name = "suchiha" + given_name = "Sasuke" + family_name = "Uchiha" + email = "suchiha@hiddenleaf.village" + }, + } + + // Create permissions sets backed by AWS managed policies + permission_sets = { + AdministratorAccess = { + description = "Provides AWS full access permissions.", + session_duration = "PT4H", // how long until session expires - this means 4 hours. max is 12 hours + aws_managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] + tags = { ManagedBy = "Terraform" } + }, + ViewOnlyAccess = { + description = "Provides AWS view only permissions.", + session_duration = "PT3H", // how long until session expires - this means 3 hours. max is 12 hours + aws_managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] + tags = { ManagedBy = "Terraform" } + }, + } + + // Assign users/groups access to accounts with the specified permissions + account_assignments = { + Admin : { + principal_name = "Admin" # name of the user or group you wish to have access to the account(s) + principal_type = "GROUP" # entity type (user or group) you wish to have access to the account(s). Valid values are "USER" or "GROUP" + principal_idp = "INTERNAL" # type of Identity Provider you are using. Valid values are "INTERNAL" (using Identity Store) or "EXTERNAL" (using external IdP such as EntraID, Okta, Google, etc.) + permission_sets = ["AdministratorAccess", "ViewOnlyAccess"] // permissions the user/group will have in the account(s) + account_ids = [ // account(s) the group will have access to. Permissions they will have in account are above line + local.account1_account_id, + # local.account2_account_id, + # local.account3_account_id, // these are defined in a locals.tf file, example is in this directory + # local.account4_account_id, + ] + }, + Dev : { + principal_name = "Dev" + principal_type = "GROUP" + principal_idp = "INTERNAL" # type of Identity Provider you are using. Valid values are "INTERNAL" (using Identity Store) or "EXTERNAL" (using external IdP such as EntraID, Okta, Google, etc.) + permission_sets = ["ViewOnlyAccess"] + account_ids = [ + local.account1_account_id, + # local.account2_account_id, + # local.account3_account_id, + # local.account4_account_id, + ] + }, + } + + // Applications + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + tags = { ManagedBy = "Terraform" } + }, + SecondApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the Second Application" + name = "SecondApplication" + portal_options = { + sign_in_options = { + application_url = "http://example2.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication", "SecondApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Admin"] + user_assignments = ["suchiha"] + } + } + +} diff --git a/examples/existing-users-groups-create-apps/.header.md b/examples/existing-users-groups-create-apps/.header.md new file mode 100644 index 0000000..992cd80 --- /dev/null +++ b/examples/existing-users-groups-create-apps/.header.md @@ -0,0 +1,66 @@ +This directory contains examples of using the module to **reference existing** users and groups and assign permissions with **AWS Managed Policies** and assign SSO Applications to users and groups + +**IMPORTANT:** Ensure that the name of your object matches the name of your principal (e.g. user name or group name). See the following example with object/principal names 'Admin' and 'nuzumaki': + +```hcl + sso_groups = { + Admin : { + group_name = "Admin" + group_description = "Admin IAM Identity Center Group" + }, + } + + // Create desired USERS in IAM Identity Center + sso_users = { + nuzumaki : { + group_membership = ["Admin",] + user_name = "nuzumaki" + given_name = "Naruto" + family_name = "Uzumaki" + email = "nuzumaki@hiddenleaf.village" + }, + } + + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } + +``` + +These names are referenced throughout the module. Failure to do this may lead to unintentional errors such as the following: + +``` +Error: Invalid index +│ +│ on ../../main.tf line 141, in resource "aws_identitystore_group_membership" "sso_group_membership": +│ 141: member_id = (contains(local.this_users, each.value.user_name) ? aws_identitystore_user.sso_users[each.value.user_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.user_name].id) +│ ├──────────────── +│ │ aws_identitystore_user.sso_users is object with 2 attributes +│ │ each.value.user_name is "nuzumaki" +│ +│ The given key does not identify an element in this collection value. +``` + +To resolve this, ensure your object and principal names are the same and re-run `terraform plan` and `terraform apply`. diff --git a/examples/existing-users-groups-create-apps/README.md b/examples/existing-users-groups-create-apps/README.md new file mode 100644 index 0000000..5027311 --- /dev/null +++ b/examples/existing-users-groups-create-apps/README.md @@ -0,0 +1,98 @@ + +This directory contains examples of using the module to **reference existing** users and groups and assign permissions with **AWS Managed Policies** and assign SSO Applications to users and groups + +**IMPORTANT:** Ensure that the name of your object matches the name of your principal (e.g. user name or group name). See the following example with object/principal names 'Admin' and 'nuzumaki': + +```hcl + sso_groups = { + Admin : { + group_name = "Admin" + group_description = "Admin IAM Identity Center Group" + }, + } + + // Create desired USERS in IAM Identity Center + sso_users = { + nuzumaki : { + group_membership = ["Admin",] + user_name = "nuzumaki" + given_name = "Naruto" + family_name = "Uzumaki" + email = "nuzumaki@hiddenleaf.village" + }, + } + + // Create desired Applications in IAM Identity Center + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["Dev"] + user_assignments = ["nuzumaki"] + } + } + +``` + +These names are referenced throughout the module. Failure to do this may lead to unintentional errors such as the following: + +``` +Error: Invalid index +│ +│ on ../../main.tf line 141, in resource "aws_identitystore_group_membership" "sso_group_membership": +│ 141: member_id = (contains(local.this_users, each.value.user_name) ? aws_identitystore_user.sso_users[each.value.user_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.user_name].id) +│ ├──────────────── +│ │ aws_identitystore_user.sso_users is object with 2 attributes +│ │ each.value.user_name is "nuzumaki" +│ +│ The given key does not identify an element in this collection value. +``` + +To resolve this, ensure your object and principal names are the same and re-run `terraform plan` and `terraform apply`. + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [aws-iam-identity-center](#module\_aws-iam-identity-center) | ../.. | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_ssm_parameter.account1_account_id](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | + +## Inputs + +No inputs. + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/examples/existing-users-groups-create-apps/locals.tf b/examples/existing-users-groups-create-apps/locals.tf new file mode 100644 index 0000000..922f5e9 --- /dev/null +++ b/examples/existing-users-groups-create-apps/locals.tf @@ -0,0 +1,14 @@ +# Fetch Account Id from SSM Parameter Store +data "aws_ssm_parameter" "account1_account_id" { + name = "tf-aws-iam-idc-module-testing-account1-account-id" // replace with your SSM Parameter Key +} + +locals { + # Account IDs + account1_account_id = nonsensitive(data.aws_ssm_parameter.account1_account_id.value) + # account1_account_id = "111111111111" + # account2_account_id = "222222222222" + # account3_account_id = "333333333333" + # account4_account_id = "444444444444" + +} diff --git a/examples/existing-users-groups-create-apps/main.tf b/examples/existing-users-groups-create-apps/main.tf new file mode 100644 index 0000000..db2e324 --- /dev/null +++ b/examples/existing-users-groups-create-apps/main.tf @@ -0,0 +1,113 @@ +module "aws-iam-identity-center" { + source = "../.." // local example + # source = "aws-ia/iam-identity-center/aws" // remote example + + # Ensure these User/Groups already exist in your AWS account + existing_sso_groups = { + testgroup : { + group_name = "testgroup" # this must be the name of a group that already exists in your AWS account + }, + } + existing_sso_users = { + testuser : { + user_name = "testuser" # this must be the name of a user that already exists in your AWS account + }, + } + + + # Create permissions sets backed by AWS managed policies + permission_sets = { + AdministratorAccess = { + description = "Provides AWS full access permissions.", + session_duration = "PT4H", // how long until session expires - this means 4 hours. max is 12 hours + aws_managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] + tags = { ManagedBy = "Terraform" } + }, + ViewOnlyAccess = { + description = "Provides AWS view only permissions.", + session_duration = "PT3H", // how long until session expires - this means 3 hours. max is 12 hours + aws_managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] + tags = { ManagedBy = "Terraform" } + }, + } + + + # Assign users/groups access to accounts with the specified permissions + # Ensure these User/Groups already exist in your AWS account + account_assignments = { + testgroup : { + principal_name = "testgroup" + principal_type = "GROUP" + principal_idp = "EXTERNAL" + permission_sets = ["AdministratorAccess", "ViewOnlyAccess", ] + account_ids = [ // account(s) the user will have access to. Permissions they will have in account are above line + local.account1_account_id, // locals are used to allow for global changes to multiple account assignments + # local.account2_account_id, // if hard coding the account ids, you would need to change them in every place you want to change + # local.account3_account_id, // these are defined in a locals.tf file, example is in this directory + # local.account4_account_id, + ] + }, + testuser : { + principal_name = "testuser" + principal_type = "USER" + principal_idp = "EXTERNAL" + permission_sets = ["ViewOnlyAccess"] + account_ids = [ // account(s) the user will have access to. Permissions they will have in account are above line + local.account1_account_id, // locals are used to allow for global changes to multiple account assignments + # local.account2_account_id, // if hard coding the account ids, you would need to change them in every place you want to change + # local.account3_account_id, // these are defined in a locals.tf file, example is in this directory + # local.account4_account_id, + ] + }, + } + + // Applications + sso_applications = { + FirstApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the First Application" + name = "FirstApplication" + portal_options = { + sign_in_options = { + application_url = "http://example.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication"] + scope = "sso:account:access" + } + ] + group_assignments = ["testgroup"] + user_assignments = [] + tags = { ManagedBy = "Terraform" } + }, + SecondApplication : { + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + description = "I am the Second Application" + name = "SecondApplication" + portal_options = { + sign_in_options = { + application_url = "http://example2.com" + origin = "APPLICATION" + } + visibility = "ENABLED" + } + status = "ENABLED" + assignment_required = true + assignments_access_scope = [ + { + authorized_targets = ["FirstApplication", "SecondApplication"] + scope = "sso:account:access" + } + ] + group_assignments = [] + user_assignments = ["testuser"] + } + } + +} diff --git a/locals.tf b/locals.tf index b041a53..d996c3d 100644 --- a/locals.tf +++ b/locals.tf @@ -170,3 +170,51 @@ locals { # ] } + +locals { + + # Creating a local variable by flattening the complex type related to Applications to extract a simple structure representing + # group-application assignments + apps_groups_assignments = flatten([ + for app in var.sso_applications : [ + for group in app.group_assignments : { + app_name = app.name + group_name = group + principal_type = "GROUP" + } + ] + ]) + + # Creating a local variable by flattening the complex type related to Applications to extract a simple structure representing + # user-application assignments + apps_users_assignments = flatten([ + for app in var.sso_applications : [ + for user in app.user_assignments : { + app_name = app.name + user_name = user + principal_type = "USER" + } + ] + ]) + + # Creating a local variable by flattening the complex type related to Applications to extract a simple structure representing + # apps assignments configurations + apps_assignments_configs = flatten([ + for app in var.sso_applications : { + app_name = app.name + assignment_required = app.assignment_required + } + ]) + + # Creating a local variable by flattening the complex type related to Applications to extract a simple structure representing + # app assignments access scopes + apps_assignments_access_scopes = flatten([ + for app in var.sso_applications : [ + for ass_acc_scope in app.assignments_access_scope : { + app_name = app.name + authorized_targets = ass_acc_scope.authorized_targets + scope = ass_acc_scope.scope + } + ] + ]) +} diff --git a/main.tf b/main.tf index f61050a..ad2641c 100644 --- a/main.tf +++ b/main.tf @@ -247,4 +247,74 @@ resource "aws_ssoadmin_account_assignment" "account_assignment" { target_type = "AWS_ACCOUNT" } +resource "aws_ssoadmin_application" "sso_apps" { + for_each = var.sso_applications == null ? {} : var.sso_applications + name = each.value.name + instance_arn = local.ssoadmin_instance_arn + application_provider_arn = each.value.application_provider_arn + client_token = each.value.client_token + description = each.value.description + + dynamic "portal_options" { + for_each = each.value.portal_options != null ? [each.value.portal_options] : [] + content { + visibility = portal_options.value.visibility + dynamic "sign_in_options" { + for_each = each.value.portal_options.sign_in_options != null ? [each.value.portal_options.sign_in_options] : [] + content { + application_url = portal_options.value.sign_in_options.application_url + origin = portal_options.value.sign_in_options.origin + } + } + } + } + tags = each.value.tags +} + +# SSO - Applications Assigments Configuration +resource "aws_ssoadmin_application_assignment_configuration" "sso_apps_assignments_configs" { + for_each = { + for idx, assignment_config in local.apps_assignments_configs : + "${assignment_config.app_name}-assignment-config" => assignment_config + } + application_arn = aws_ssoadmin_application.sso_apps[each.value.app_name].application_arn + assignment_required = each.value.assignment_required +} + +# SSO - Application Assignments access scope +resource "aws_ssoadmin_application_access_scope" "sso_apps_assignments_access_scope" { + for_each = { + for idx, app_access_scope in local.apps_assignments_access_scopes : + "${app_access_scope.app_name}-${app_access_scope.scope}" => app_access_scope + } + application_arn = aws_ssoadmin_application.sso_apps[each.value.app_name].application_arn + authorized_targets = [ + for target in each.value.authorized_targets : aws_ssoadmin_application.sso_apps[target].application_arn + ] + #authorized_targets = each.value.authorized_targets + scope = each.value.scope +} + +# SSO - Applications Assignments +# Groups assignments +resource "aws_ssoadmin_application_assignment" "sso_apps_groups_assignments" { + for_each = { + for idx, assignment in local.apps_groups_assignments : + "${assignment.app_name}-${assignment.group_name}" => assignment + } + application_arn = aws_ssoadmin_application.sso_apps[each.value.app_name].application_arn + principal_id = (contains(local.this_groups, each.value.group_name) ? aws_identitystore_group.sso_groups[each.value.group_name].group_id : data.aws_identitystore_group.existing_sso_groups[each.value.group_name].group_id) + principal_type = each.value.principal_type +} + +# Users assignments +resource "aws_ssoadmin_application_assignment" "sso_apps_users_assignments" { + for_each = { + for idx, assignment in local.apps_users_assignments : + "${assignment.app_name}-${assignment.user_name}" => assignment + } + application_arn = aws_ssoadmin_application.sso_apps[each.value.app_name].application_arn + principal_id = (contains(local.this_users, each.value.user_name) ? aws_identitystore_user.sso_users[each.value.user_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.user_name].user_id) + principal_type = each.value.principal_type +} diff --git a/outputs.tf b/outputs.tf index 234989c..91496b1 100644 --- a/outputs.tf +++ b/outputs.tf @@ -14,3 +14,18 @@ output "sso_groups_ids" { value = { for k, v in aws_identitystore_group.sso_groups : k => v.group_id } description = "A map of SSO groups ids created by this module" } + +output "sso_applications_arns" { + value = { for k, v in aws_ssoadmin_application.sso_apps : k => v.application_arn } + description = "A map of SSO Applications ARNs created by this module" +} + +output "sso_applications_group_assignments" { + value = { for k, v in aws_ssoadmin_application_assignment.sso_apps_groups_assignments : k => v.principal_id } + description = "A map of SSO Applications assignments with groups created by this module" +} + +output "sso_applications_user_assignments" { + value = { for k, v in aws_ssoadmin_application_assignment.sso_apps_users_assignments : k => v.principal_id } + description = "A map of SSO Applications assignments with users created by this module" +} diff --git a/tests/05_create_apps_and_assignments.tftest.hcl b/tests/05_create_apps_and_assignments.tftest.hcl new file mode 100644 index 0000000..d53f66e --- /dev/null +++ b/tests/05_create_apps_and_assignments.tftest.hcl @@ -0,0 +1,13 @@ +run "unit_test" { + command = plan + module { + source = "./examples/create-apps-and-assignments" + } +} + +run "e2e_test" { + command = apply + module { + source = "./examples/create-apps-and-assignments" + } +} diff --git a/tests/06_existing_user_groups_and_apps.tftest.hcl b/tests/06_existing_user_groups_and_apps.tftest.hcl new file mode 100644 index 0000000..1c73e0e --- /dev/null +++ b/tests/06_existing_user_groups_and_apps.tftest.hcl @@ -0,0 +1,13 @@ +run "unit_test" { + command = plan + module { + source = "./examples/existing-users-groups-create-apps" + } +} + +run "e2e_test" { + command = apply + module { + source = "./examples/existing-users-groups-create-apps" + } +} diff --git a/variables.tf b/variables.tf index b06421b..395a424 100644 --- a/variables.tf +++ b/variables.tf @@ -107,3 +107,41 @@ variable "account_assignments" { default = {} } + +# Applications +variable "sso_applications" { + description = "List of applications to be created in IAM Identity Center" + type = map(object({ + name = string + application_provider_arn = string + description = optional(string) + portal_options = optional(object({ + sign_in_options = optional(object({ + application_url = optional(string) + origin = string + })) + visibility = optional(string) + })) + status = string # acceptable values are "ENABLED" or "DISABLED" + client_token = optional(string) + tags = optional(map(string)) + assignment_required = bool # Resource: aws_ssoadmin_application_assignment_configuration + assignments_access_scope = optional( + list(object({ + authorized_targets = optional(list(string)) # List of application names + scope = string + })) + ) # Resource: aws_ssoadmin_application_access_scope + group_assignments = optional(list(string)) # Resource aws_ssoadmin_application_assignment, keeping it separated for groups + user_assignments = optional(list(string)) # Resource aws_ssoadmin_application_assignment, keeping it separated for users + })) + default = {} + validation { + condition = alltrue([ + for app in values(var.sso_applications) : + app.application_provider_arn != null && + app.application_provider_arn != "" + ]) + error_message = "The application_provider_arn field is mandatory for all applications." + } +}
display_name = optional(string)
user_name = string
group_membership = list(string)
# Name
given_name = string
middle_name = optional(string, null)
family_name = string
name_formatted = optional(string)
honorific_prefix = optional(string, null)
honorific_suffix = optional(string, null)
email = string
email_type = optional(string, null)
is_primary_email = optional(bool, true)
# Phone Number
phone_number = optional(string, null)
phone_number_type = optional(string, null)
is_primary_phone_number = optional(bool, true)
# Address
country = optional(string, " ")
locality = optional(string, " ")
address_formatted = optional(string)
postal_code = optional(string, " ")
is_primary_address = optional(bool, true)
region = optional(string, " ")
street_address = optional(string, " ")
address_type = optional(string, null)
# Additional
user_type = optional(string, null)
title = optional(string, null)
locale = optional(string, null)
nickname = optional(string, null)
preferred_language = optional(string, null)
profile_url = optional(string, null)
timezone = optional(string, null)
}))