├── examples ├── simple │ ├── variables.tf │ ├── versions.tf │ ├── outputs.tf │ ├── main.tf │ └── README.md ├── with-dependencies │ ├── variables.tf │ ├── versions.tf │ ├── outputs.tf │ ├── README.md │ └── main.tf └── complete │ ├── aws_sso_diagram.png │ ├── versions.tf │ ├── variables.tf │ ├── outputs.tf │ ├── README.md │ └── main.tf ├── .github ├── pr-labeler.yml ├── pull_request_template.md ├── workflows │ ├── pr-labeler.yml │ └── release-drafter.yml └── release-drafter.yml ├── .pre-commit-config.yaml ├── versions.tf ├── outputs.tf ├── .gitignore ├── variables.tf ├── main.tf ├── README.md └── LICENSE /examples/simple/variables.tf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/with-dependencies/variables.tf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/complete/aws_sso_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avlcloudtechnologies/terraform-aws-sso/HEAD/examples/complete/aws_sso_diagram.png -------------------------------------------------------------------------------- /.github/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | enhancement: ['feature/*', 'feat/*', 'enhancement/*'] 2 | bug: ['fix/*', 'hotfix/*', 'bugfix/*', 'patch/*', 'bug/*'] 3 | chore: ['chore/*', 'docs/*'] -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.45.0 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_docs 7 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12.23" 3 | required_providers { 4 | aws = { 5 | source = "hashicorp/aws" 6 | version = ">= 4.40" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/complete/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12.23" 3 | required_providers { 4 | aws = { 5 | source = "hashicorp/aws" 6 | version = ">= 4.30" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /examples/simple/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12.23" 3 | required_providers { 4 | aws = { 5 | source = "hashicorp/aws" 6 | version = ">= 3.27" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /examples/with-dependencies/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12.23" 3 | required_providers { 4 | aws = { 5 | source = "hashicorp/aws" 6 | version = ">= 3.27" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | * List the changes you are making. 3 | * Reference Github issue if appropriate, i.e. `closes #123` 4 | 5 | ## Breaking Changes 6 | * Any breaking changes? 7 | 8 | ## Testing 9 | * How was this tested? -------------------------------------------------------------------------------- /examples/simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_ssoadmin_permission_sets" { 2 | description = "Maps of permission sets with attributes listed in Terraform resource aws_ssoadmin_permission_set documentation." 3 | value = module.sso.aws_ssoadmin_permission_sets 4 | } 5 | -------------------------------------------------------------------------------- /examples/with-dependencies/outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_ssoadmin_permission_sets" { 2 | description = "Maps of permission sets with attributes listed in Terraform resource aws_ssoadmin_permission_set documentation." 3 | value = module.sso.aws_ssoadmin_permission_sets 4 | } 5 | -------------------------------------------------------------------------------- /.github/workflows/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | name: PR Labeler 2 | on: 3 | pull_request: 4 | types: [opened] 5 | 6 | jobs: 7 | pr-labeler: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: TimonVS/pr-labeler-action@v3 11 | env: 12 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /examples/complete/variables.tf: -------------------------------------------------------------------------------- 1 | variable "developer_readonly_accounts" { 2 | default = ["shared-services", "productA-eks-prod"] 3 | } 4 | variable "developer_workload_accounts" { 5 | default = ["productA-eks-staging", "productA-eks-dev"] 6 | } 7 | 8 | variable "security_accounts" { 9 | default = ["security"] 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@master 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_ssoadmin_permission_sets" { 2 | description = "Maps of permission sets with attributes listed in Terraform resource aws_ssoadmin_permission_set documentation." 3 | value = aws_ssoadmin_permission_set.this 4 | } 5 | output "aws_ssoadmin_account_assignments" { 6 | description = "Maps of account assignments to permission sets with keys user/group_name.permission_set_name.account_id and attributes listed in Terraform resource aws_ssoadmin_account_assignment documentation." 7 | value = aws_ssoadmin_account_assignment.this 8 | } -------------------------------------------------------------------------------- /examples/complete/outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_ssoadmin_permission_sets" { 2 | description = "Maps of permission sets with attributes listed in Terraform resource aws_ssoadmin_permission_set documentation." 3 | value = module.sso.aws_ssoadmin_permission_sets 4 | } 5 | 6 | output "aws_ssoadmin_account_assignments" { 7 | description = "Maps of account assignments to permission sets with keys user/group_name.permission_set_name.account_id and attributes listed in Terraform resource aws_ssoadmin_account_assignment documentation." 8 | value = module.sso.aws_ssoadmin_account_assignments 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_Store 3 | 4 | # Local .terraform directories 5 | **/.terraform/* 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | 11 | # Terraform lockfile 12 | .terraform.lock.hcl 13 | 14 | # Crash log files 15 | crash.log 16 | 17 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 18 | # .tfvars files are managed as part of configuration and so should be included in 19 | # version control. 20 | # 21 | # example.tfvars 22 | 23 | # Ignore override files as they are usually used to override resources locally and so 24 | # are not checked in 25 | override.tf 26 | override.tf.json 27 | *_override.tf 28 | *_override.tf.json 29 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: '❗ Breaking' 5 | labels: 6 | - 'breaking' 7 | - title: 'Features' 8 | labels: 9 | - 'feature' 10 | - 'enhancement' 11 | - title: 'Bug Fixes' 12 | labels: 13 | - 'bug' 14 | - title: 'Maintenance' 15 | label: 'chore' 16 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 17 | change-title-escapes: '\<*_&' 18 | version-resolver: 19 | major: 20 | labels: 21 | - 'breaking' 22 | minor: 23 | labels: 24 | - 'enhancement' 25 | patch: 26 | labels: 27 | - 'patch' 28 | - 'bug' 29 | - 'chore' 30 | default: minor 31 | template: | 32 | # What's Changed 33 | 34 | $CHANGES 35 | -------------------------------------------------------------------------------- /examples/simple/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | data "aws_organizations_organization" "this" {} 6 | 7 | locals { 8 | all_accounts_names = [for account in toset(data.aws_organizations_organization.this.accounts) : account.name] 9 | all_accounts_map = zipmap(local.all_accounts_names, tolist(toset(data.aws_organizations_organization.this.accounts))) 10 | non_management_accounts_names = [for account in toset(data.aws_organizations_organization.this.non_master_accounts) : account.name] 11 | non_management_accounts_map = zipmap(local.non_management_accounts_names, tolist(toset(data.aws_organizations_organization.this.non_master_accounts))) 12 | } 13 | 14 | module "sso" { 15 | source = "avlcloudtechnologies/sso/aws" 16 | 17 | permission_sets = { 18 | AdministratorAccess = { 19 | description = "Provides full access to AWS services and resources.", 20 | session_duration = "PT2H", 21 | managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] 22 | }, 23 | } 24 | account_assignments = [ 25 | { 26 | principal_name = "management" 27 | principal_type = "GROUP" 28 | permission_set = "AdministratorAccess" 29 | account_ids = [for account in local.all_accounts_map : account.id] 30 | }, 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "permission_sets" { 2 | description = "Map of maps containing Permission Set names as keys. See permission_sets description in README for information about map values." 3 | type = any 4 | default = { 5 | AdministratorAccess = { 6 | description = "Provides full access to AWS services and resources.", 7 | session_duration = "PT2H", 8 | managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] 9 | } 10 | } 11 | } 12 | 13 | variable "account_assignments" { 14 | description = "List of maps containing mapping between user/group, permission set and assigned accounts list. See account_assignments description in README for more information about map values." 15 | type = list(object({ 16 | principal_name = string, 17 | principal_type = string, 18 | permission_set = string, 19 | account_ids = list(string) 20 | })) 21 | 22 | default = [] 23 | } 24 | 25 | variable "identitystore_group_data_source_depends_on" { 26 | description = "List of parameters that identitystore group data sources depend on, for example new SSO group IDs." 27 | type = list(string) 28 | default = [] 29 | } 30 | 31 | variable "identitystore_user_data_source_depends_on" { 32 | description = "List of parameters that identitystore user data sources depend on, for example new SSO user IDs." 33 | type = list(string) 34 | default = [] 35 | } -------------------------------------------------------------------------------- /examples/simple/README.md: -------------------------------------------------------------------------------- 1 | # Simple 2 | 3 | ## Pre-requisites 4 | Before this example can be used, please ensure that the following pre-requisites are met: 5 | - Enable AWS Organizations and add AWS Accounts you want to be managed by SSO. [Documentation](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_tutorials_basic.html) 6 | - Enable AWS SSO. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/step1.html). 7 | - Create AWS SSO entities (Users and Groups). [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/addusers.html). 8 | - Ensure that Terraform is using a role with permissions required for AWS SSO management. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/iam-auth-access-using-id-policies.html#requiredpermissionsconsole). 9 | 10 | 11 | 12 | ## Requirements 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [terraform](#requirement\_terraform) | >= 0.12.23 | 17 | | [aws](#requirement\_aws) | >= 3.27 | 18 | 19 | ## Providers 20 | 21 | | Name | Version | 22 | |------|---------| 23 | | [aws](#provider\_aws) | >= 3.27 | 24 | 25 | ## Modules 26 | 27 | | Name | Source | Version | 28 | |------|--------|---------| 29 | | [sso](#module\_sso) | avlcloudtechnologies/sso/aws | n/a | 30 | 31 | ## Resources 32 | 33 | | Name | Type | 34 | |------|------| 35 | | [aws_organizations_organization.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | 36 | 37 | ## Inputs 38 | 39 | No inputs. 40 | 41 | ## Outputs 42 | 43 | | Name | Description | 44 | |------|-------------| 45 | | [aws\_ssoadmin\_permission\_sets](#output\_aws\_ssoadmin\_permission\_sets) | Maps of permission sets with attributes listed in Terraform resource aws\_ssoadmin\_permission\_set documentation. | 46 | -------------------------------------------------------------------------------- /examples/with-dependencies/README.md: -------------------------------------------------------------------------------- 1 | # Simple 2 | Example showing how to create SSO users and groups in the same state file as `terraform-aws-sso` module resources and without adding `depends_on` argument at the module level. Using null resource, it will only recreate account assignments, when new SSO group is added. It will NOT re-create all module resources, after a new AWS Account is added. 3 | 4 | ## Pre-requisites 5 | Before this example can be used, please ensure that the following pre-requisites are met: 6 | - Enable AWS Organizations and add AWS Accounts you want to be managed by SSO. [Documentation](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_tutorials_basic.html) 7 | - Enable AWS SSO. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/step1.html). 8 | - Ensure that Terraform is using a role with permissions required for AWS SSO management. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/iam-auth-access-using-id-policies.html#requiredpermissionsconsole). 9 | 10 | 11 | 12 | ## Requirements 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [terraform](#requirement\_terraform) | >= 0.12.23 | 17 | | [aws](#requirement\_aws) | >= 3.27 | 18 | 19 | ## Providers 20 | 21 | | Name | Version | 22 | |------|---------| 23 | | [aws](#provider\_aws) | >= 3.27 | 24 | 25 | ## Modules 26 | 27 | | Name | Source | Version | 28 | |------|--------|---------| 29 | | [aws\_identitystore](#module\_aws\_identitystore) | avlcloudtechnologies/identitystore/aws | 0.1.1 | 30 | | [sso](#module\_sso) | avlcloudtechnologies/sso/aws | | 31 | 32 | ## Resources 33 | 34 | | Name | Type | 35 | |------|------| 36 | | [aws_organizations_organization.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | 37 | 38 | ## Inputs 39 | 40 | No inputs. 41 | 42 | ## Outputs 43 | 44 | | Name | Description | 45 | |------|-------------| 46 | | [aws\_ssoadmin\_permission\_sets](#output\_aws\_ssoadmin\_permission\_sets) | Maps of permission sets with attributes listed in Terraform resource aws\_ssoadmin\_permission\_set documentation. | 47 | -------------------------------------------------------------------------------- /examples/with-dependencies/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | data "aws_organizations_organization" "this" {} 6 | 7 | locals { 8 | all_active_accounts_map = { for account in toset(data.aws_organizations_organization.this.accounts) : account.name => account if account.status == "ACTIVE" } 9 | non_management_active_accounts_map = { for account in toset(data.aws_organizations_organization.this.non_master_accounts) : account.name => account if account.status == "ACTIVE" } 10 | sso_groups = { 11 | management = { 12 | description = "Group with Administrator access to all accounts including Management account" 13 | }, 14 | admins = { 15 | description = "Group with Administrator access to all accounts excluding Management account" 16 | }, 17 | readonly = { 18 | description = "Group for Read only access" 19 | } 20 | } 21 | sso_users = { 22 | aurimas = { 23 | display_name = "aurimas" 24 | given_name = "Aurimas" 25 | family_name = "Mickevicius" 26 | sso_groups = ["management", "readonly"] 27 | }, 28 | john = { 29 | display_name = "john" 30 | given_name = "John" 31 | family_name = "Smith" 32 | sso_groups = ["admins", "readonly"] 33 | } 34 | } 35 | } 36 | 37 | module "sso" { 38 | source = "avlcloudtechnologies/sso/aws" 39 | 40 | permission_sets = { 41 | AdministratorAccess = { 42 | description = "Provides full access to AWS services and resources.", 43 | session_duration = "PT2H", 44 | managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] 45 | }, 46 | ViewOnlyAccess = { 47 | description = "View resources and basic metadata across all AWS services.", 48 | session_duration = "PT2H", 49 | managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] 50 | }, 51 | } 52 | account_assignments = [ 53 | { 54 | principal_name = "management" 55 | principal_type = "GROUP" 56 | permission_set = "AdministratorAccess" 57 | account_ids = [for account in local.all_active_accounts_map : account.id] 58 | }, 59 | { 60 | principal_name = "admins" 61 | principal_type = "GROUP" 62 | permission_set = "AdministratorAccess" 63 | account_ids = [for account in local.non_management_active_accounts_map : account.id] 64 | }, 65 | { 66 | principal_name = "readonly" 67 | principal_type = "GROUP" 68 | permission_set = "ViewOnlyAccess" 69 | account_ids = [for account in local.non_management_active_accounts_map : account.id] 70 | }, 71 | ] 72 | identitystore_group_data_source_depends_on = [for group in module.aws_identitystore.groups : group.group_id] 73 | } 74 | 75 | module "aws_identitystore" { 76 | source = "avlcloudtechnologies/identitystore/aws" 77 | version = "0.1.1" 78 | 79 | sso_users = var.sso_users 80 | sso_groups = var.sso_groups 81 | } -------------------------------------------------------------------------------- /examples/complete/README.md: -------------------------------------------------------------------------------- 1 | # Complete 2 | 3 | ## Pre-requisites 4 | Before this example can be used, please ensure that the following pre-requisites are met: 5 | - Enable AWS Organizations and add AWS Accounts you want to be managed by SSO. [Documentation](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_tutorials_basic.html) 6 | - Enable AWS SSO. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/step1.html). 7 | - Create AWS SSO entities (Users and Groups). [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/addusers.html). 8 | - Ensure that Terraform is using a role with permissions required for AWS SSO management. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/iam-auth-access-using-id-policies.html#requiredpermissionsconsole). 9 | - If using Customer Managed Policies in permission sets, please make sure that policy exists (pre-created) in target AWS account. 10 | 11 | ## Diagram 12 | ![Alt text](aws_sso_diagram.png?raw=true "Title") 13 | 14 | 15 | ## Requirements 16 | 17 | | Name | Version | 18 | |------|---------| 19 | | [terraform](#requirement\_terraform) | >= 0.12.23 | 20 | | [aws](#requirement\_aws) | >= 4.30 | 21 | 22 | ## Providers 23 | 24 | | Name | Version | 25 | |------|---------| 26 | | [aws](#provider\_aws) | >= 4.30 | 27 | 28 | ## Modules 29 | 30 | | Name | Source | Version | 31 | |------|--------|---------| 32 | | [sso](#module\_sso) | avlcloudtechnologies/sso/aws | n/a | 33 | 34 | ## Resources 35 | 36 | | Name | Type | 37 | |------|------| 38 | | [aws_iam_policy_document.EKSAdmin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 39 | | [aws_organizations_organization.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | 40 | 41 | ## Inputs 42 | 43 | | Name | Description | Type | Default | Required | 44 | |------|-------------|------|---------|:--------:| 45 | | [developer\_readonly\_accounts](#input\_developer\_readonly\_accounts) | n/a | `list` |
[
"shared-services",
"productA-eks-prod"
]
| no | 46 | | [developer\_workload\_accounts](#input\_developer\_workload\_accounts) | n/a | `list` |
[
"productA-eks-staging",
"productA-eks-dev"
]
| no | 47 | | [security\_accounts](#input\_security\_accounts) | n/a | `list` |
[
"security"
]
| no | 48 | 49 | ## Outputs 50 | 51 | | Name | Description | 52 | |------|-------------| 53 | | [aws\_ssoadmin\_account\_assignments](#output\_aws\_ssoadmin\_account\_assignments) | Maps of account assignments to permission sets with keys user/group\_name.permission\_set\_name.account\_id and attributes listed in Terraform resource aws\_ssoadmin\_account\_assignment documentation. | 54 | | [aws\_ssoadmin\_permission\_sets](#output\_aws\_ssoadmin\_permission\_sets) | Maps of permission sets with attributes listed in Terraform resource aws\_ssoadmin\_permission\_set documentation. | 55 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | data "aws_organizations_organization" "this" {} 6 | 7 | locals { 8 | all_active_accounts_map = { for account in toset(data.aws_organizations_organization.this.accounts) : account.name => account if account.status == "ACTIVE" } 9 | non_management_active_accounts_map = { for account in toset(data.aws_organizations_organization.this.non_master_accounts) : account.name => account if account.status == "ACTIVE" } 10 | } 11 | 12 | module "sso" { 13 | source = "avlcloudtechnologies/sso/aws" 14 | permission_sets = { 15 | AdministratorAccess = { 16 | description = "Provides full access to AWS services and resources.", 17 | session_duration = "PT2H", 18 | managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] 19 | }, 20 | ViewOnlyAccess = { 21 | description = "View resources and basic metadata across all AWS services.", 22 | session_duration = "PT2H", 23 | managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] 24 | }, 25 | PowerUserAccess = { 26 | description = "View resources and basic metadata across all AWS services.", 27 | managed_policies = ["arn:aws:iam::aws:policy/PowerUserAccess"] 28 | }, 29 | EKSAdminAccess = { 30 | description = "Allow full EKS and read only access across all AWS resources.", 31 | # Can use Managed, Customer and Inline policies in the same permission set 32 | managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] 33 | inline_policy = data.aws_iam_policy_document.EKSAdmin.json 34 | # NOTE! Customer Managed policies have to exist in all AWS accounts that this permission set will be assigned to. 35 | customer_managed_policies = ["customer-managed-policy-foo"] 36 | tags = { "foo" = "bar" }, 37 | } 38 | } 39 | account_assignments = [ 40 | { 41 | principal_name = "management" 42 | principal_type = "GROUP" 43 | permission_set = "AdministratorAccess" 44 | account_ids = [for account in local.all_active_accounts_map : account.id] 45 | }, 46 | { 47 | principal_name = "admins" 48 | principal_type = "GROUP" 49 | permission_set = "AdministratorAccess" 50 | account_ids = [for account in local.non_management_active_accounts_map : account.id] 51 | }, 52 | { 53 | principal_name = "bob" 54 | principal_type = "USER" 55 | permission_set = "PowerUserAccess" 56 | account_ids = [for account in local.non_management_active_accounts_map : account.id if contains(var.security_accounts, account.name)] 57 | }, 58 | { 59 | principal_name = "developers" 60 | principal_type = "GROUP" 61 | permission_set = "ViewOnlyAccess" 62 | account_ids = [for account in local.non_management_active_accounts_map : account.id if contains(var.developer_readonly_accounts, account.name)] 63 | }, 64 | { 65 | principal_name = "developers" 66 | principal_type = "GROUP" 67 | permission_set = "EKSAdminAccess" 68 | account_ids = [for account in local.non_management_active_accounts_map : account.id if contains(var.developer_workload_accounts, account.name)] 69 | }, 70 | ] 71 | } 72 | 73 | data "aws_iam_policy_document" "EKSAdmin" { 74 | statement { 75 | sid = "AllowEKS" 76 | actions = ["eks:*"] 77 | resources = ["*"] 78 | } 79 | statement { 80 | sid = "AllowPassRole" 81 | actions = ["iam:PassRole"] 82 | resources = ["*"] 83 | condition { 84 | test = "StringEquals" 85 | variable = "iam:PassedToService" 86 | values = ["eks.amazonaws.com"] 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | ssoadmin_instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] 3 | managed_ps = { for ps_name, ps_attrs in var.permission_sets : ps_name => ps_attrs if can(ps_attrs.managed_policies) } 4 | customer_managed_ps = { for ps_name, ps_attrs in var.permission_sets : ps_name => ps_attrs if can(ps_attrs.customer_managed_policies) } 5 | # create ps_name and managed policy maps list 6 | ps_policy_maps = flatten([ 7 | for ps_name, ps_attrs in local.managed_ps : [ 8 | for policy in ps_attrs.managed_policies : { 9 | ps_name = ps_name 10 | policy_arn = policy 11 | } if can(ps_attrs.managed_policies) 12 | ] 13 | ]) 14 | # create ps_name and customer managed policy maps list 15 | customer_ps_policy_maps = flatten([ 16 | for ps_name, ps_attrs in local.customer_managed_ps : [ 17 | for policy in ps_attrs.customer_managed_policies : { 18 | ps_name = ps_name 19 | policy_name = policy 20 | } if can(ps_attrs.customer_managed_policies) 21 | ] 22 | ]) 23 | account_assignments = flatten([ 24 | for assignment in var.account_assignments : [ 25 | for account_id in assignment.account_ids : { 26 | principal_name = assignment.principal_name 27 | principal_type = assignment.principal_type 28 | permission_set = aws_ssoadmin_permission_set.this[assignment.permission_set] 29 | account_id = account_id 30 | } 31 | ] 32 | ]) 33 | groups = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "GROUP"] 34 | users = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "USER"] 35 | } 36 | 37 | resource "null_resource" "group_dependency" { 38 | triggers = { 39 | dependency_id = join(",", var.identitystore_group_data_source_depends_on) 40 | } 41 | } 42 | 43 | resource "null_resource" "user_dependency" { 44 | triggers = { 45 | dependency_id = join(",", var.identitystore_user_data_source_depends_on) 46 | } 47 | } 48 | 49 | data "aws_ssoadmin_instances" "this" {} 50 | 51 | data "aws_identitystore_group" "this" { 52 | for_each = toset(local.groups) 53 | identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] 54 | alternate_identifier { 55 | unique_attribute { 56 | attribute_path = "DisplayName" 57 | attribute_value = each.value 58 | } 59 | } 60 | depends_on = [null_resource.group_dependency] 61 | } 62 | 63 | data "aws_identitystore_user" "this" { 64 | for_each = toset(local.users) 65 | identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0] 66 | alternate_identifier { 67 | unique_attribute { 68 | attribute_path = "UserName" 69 | attribute_value = each.value 70 | } 71 | } 72 | depends_on = [null_resource.user_dependency] 73 | } 74 | 75 | resource "aws_ssoadmin_permission_set" "this" { 76 | for_each = var.permission_sets 77 | 78 | name = each.key 79 | description = lookup(each.value, "description", null) 80 | instance_arn = local.ssoadmin_instance_arn 81 | relay_state = lookup(each.value, "relay_state", null) 82 | session_duration = lookup(each.value, "session_duration", null) 83 | tags = lookup(each.value, "tags", {}) 84 | } 85 | 86 | resource "aws_ssoadmin_permission_set_inline_policy" "this" { 87 | for_each = { for ps_name, ps_attrs in var.permission_sets : ps_name => ps_attrs if can(ps_attrs.inline_policy) } 88 | 89 | inline_policy = each.value.inline_policy 90 | instance_arn = local.ssoadmin_instance_arn 91 | permission_set_arn = aws_ssoadmin_permission_set.this[each.key].arn 92 | } 93 | 94 | resource "aws_ssoadmin_managed_policy_attachment" "this" { 95 | for_each = { for ps in local.ps_policy_maps : "${ps.ps_name}.${ps.policy_arn}" => ps } 96 | 97 | instance_arn = local.ssoadmin_instance_arn 98 | managed_policy_arn = each.value.policy_arn 99 | permission_set_arn = aws_ssoadmin_permission_set.this[each.value.ps_name].arn 100 | } 101 | 102 | resource "aws_ssoadmin_customer_managed_policy_attachment" "this" { 103 | for_each = { for ps in local.customer_ps_policy_maps : "${ps.ps_name}.${ps.policy_name}" => ps } 104 | 105 | instance_arn = local.ssoadmin_instance_arn 106 | permission_set_arn = aws_ssoadmin_permission_set.this[each.value.ps_name].arn 107 | customer_managed_policy_reference { 108 | name = each.value.policy_name 109 | path = "/" 110 | } 111 | } 112 | 113 | resource "aws_ssoadmin_account_assignment" "this" { 114 | for_each = { for assignment in local.account_assignments : "${assignment.principal_name}.${assignment.permission_set.name}.${assignment.account_id}" => assignment } 115 | 116 | instance_arn = each.value.permission_set.instance_arn 117 | permission_set_arn = each.value.permission_set.arn 118 | principal_id = each.value.principal_type == "GROUP" ? data.aws_identitystore_group.this[each.value.principal_name].id : data.aws_identitystore_user.this[each.value.principal_name].id 119 | principal_type = each.value.principal_type 120 | 121 | target_id = each.value.account_id 122 | target_type = "AWS_ACCOUNT" 123 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS SSO Terraform module 2 | ![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/avlcloudtechnologies/terraform-aws-sso) 3 | 4 | This module handles creation of AWS SSO permission sets and assignment to AWS SSO entities and AWS Accounts. 5 | 6 | ## Pre-requisites 7 | Before this module can be used, please ensure that the following pre-requisites are met: 8 | - Enable AWS Organizations and add AWS Accounts you want to be managed by SSO. [Documentation](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_tutorials_basic.html) 9 | - Enable AWS SSO. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/step1.html). 10 | - Create AWS SSO entities (Users and Groups) [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/addusers.html) or use identitystore [module](https://github.com/avlcloudtechnologies/terraform-aws-identitystore). 11 | - Ensure that Terraform is using a role with permissions required for AWS SSO management. [Documentation](https://docs.aws.amazon.com/singlesignon/latest/userguide/iam-auth-access-using-id-policies.html#requiredpermissionsconsole). 12 | 13 | ## Usage 14 | More complex examples can be found in the [examples](https://github.com/avlcloudtechnologies/terraform-aws-sso/tree/master/examples) directory. Simple use case: 15 | 16 | 17 | ```hcl 18 | module "sso" { 19 | source = "avlcloudtechnologies/sso/aws" 20 | 21 | permission_sets = { 22 | AdministratorAccess = { 23 | description = "Provides full access to AWS services and resources.", 24 | session_duration = "PT2H", 25 | managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] 26 | }, 27 | } 28 | account_assignments = [ 29 | { 30 | principal_name = "management" 31 | principal_type = "GROUP" 32 | permission_set = "AdministratorAccess" 33 | account_ids = ["123456789", "234567890"] 34 | }, 35 | ] 36 | } 37 | ``` 38 | 39 | ## `permission_sets` and `account_assignments` 40 | 41 | `permission_sets` is a map of maps. Key is used as unique value for `for_each` resources. Inner map has the following keys/value pairs. 42 | 43 | | Name | Description | Type | If unset | 44 | |------|-------------|:----:|:-----:| 45 | | description | (Optional) The description of the Permission Set. | string | Provider default behavior | 46 | | relay\_state | (Optional) The relay state URL used to redirect users within the application during the federation authentication process | string | Provider default behavior. | 47 | | session\_duration | (Optional) The length of time that the application user sessions are valid in the ISO-8601 standard | string | Provider default behavior. | 48 | | tags | (Optional) Key-value map of resource tags. | string | Provider default behavior | 49 | | managed\_policies | (Optional) List of Managed IAM policies that are attached to permission set. | list(string) | Managed Policies not set. | 50 | | customer\_managed\_policies | (Optional) List of Customer Managed IAM policies that are attached to permission set. | list(string) | Customer Managed Policies not set. | 51 | | inline\_policy | (Optional) Inline policy that is attached to permission set. | string | Inline policy not set. | 52 | 53 | `account_assignments` is a list of maps which have the following keys/value pairs. 54 | 55 | | Name | Description | Type | If unset | 56 | |------|-------------|:----:|:-----:| 57 | | principal\_name | (Required) Name of the SSO entity that you want to assign the Permission Set. | string | Required | 58 | | principal\_type | (Required) Type of the SSO entity that you want to assign the Permission Set. Valid values: USER, GROUP | string | Required | 59 | | permission\_set | (Required) Name of the Permission Set which will be granted to SSO entity on specified AWS accounts. | string | Required | 60 | | account\_ids | (Required) AWS account IDs. | list | Required | 61 | 62 | 63 | 64 | ## Requirements 65 | 66 | | Name | Version | 67 | |------|---------| 68 | | [terraform](#requirement\_terraform) | >= 0.12.23 | 69 | | [aws](#requirement\_aws) | >= 4.40 | 70 | 71 | ## Providers 72 | 73 | | Name | Version | 74 | |------|---------| 75 | | [aws](#provider\_aws) | >= 4.40 | 76 | | [null](#provider\_null) | n/a | 77 | 78 | ## Modules 79 | 80 | No modules. 81 | 82 | ## Resources 83 | 84 | | Name | Type | 85 | |------|------| 86 | | [aws_ssoadmin_account_assignment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_account_assignment) | resource | 87 | | [aws_ssoadmin_customer_managed_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_customer_managed_policy_attachment) | resource | 88 | | [aws_ssoadmin_managed_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_managed_policy_attachment) | resource | 89 | | [aws_ssoadmin_permission_set.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set) | resource | 90 | | [aws_ssoadmin_permission_set_inline_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set_inline_policy) | resource | 91 | | [null_resource.group_dependency](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | 92 | | [null_resource.user_dependency](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | 93 | | [aws_identitystore_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | 94 | | [aws_identitystore_user.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_user) | data source | 95 | | [aws_ssoadmin_instances.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssoadmin_instances) | data source | 96 | 97 | ## Inputs 98 | 99 | | Name | Description | Type | Default | Required | 100 | |------|-------------|------|---------|:--------:| 101 | | [account\_assignments](#input\_account\_assignments) | List of maps containing mapping between user/group, permission set and assigned accounts list. See account\_assignments description in README for more information about map values. |
list(object({
principal_name = string,
principal_type = string,
permission_set = string,
account_ids = list(string)
}))
| `[]` | no | 102 | | [identitystore\_group\_data\_source\_depends\_on](#input\_identitystore\_group\_data\_source\_depends\_on) | List of parameters that identitystore group data sources depend on, for example new SSO group IDs. | `list(string)` | `[]` | no | 103 | | [identitystore\_user\_data\_source\_depends\_on](#input\_identitystore\_user\_data\_source\_depends\_on) | List of parameters that identitystore user data sources depend on, for example new SSO user IDs. | `list(string)` | `[]` | no | 104 | | [permission\_sets](#input\_permission\_sets) | Map of maps containing Permission Set names as keys. See permission\_sets description in README for information about map values. | `any` |
{
"AdministratorAccess": {
"description": "Provides full access to AWS services and resources.",
"managed_policies": [
"arn:aws:iam::aws:policy/AdministratorAccess"
],
"session_duration": "PT2H"
}
}
| no | 105 | 106 | ## Outputs 107 | 108 | | Name | Description | 109 | |------|-------------| 110 | | [aws\_ssoadmin\_account\_assignments](#output\_aws\_ssoadmin\_account\_assignments) | Maps of account assignments to permission sets with keys user/group\_name.permission\_set\_name.account\_id and attributes listed in Terraform resource aws\_ssoadmin\_account\_assignment documentation. | 111 | | [aws\_ssoadmin\_permission\_sets](#output\_aws\_ssoadmin\_permission\_sets) | Maps of permission sets with attributes listed in Terraform resource aws\_ssoadmin\_permission\_set documentation. | 112 | 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------