DEV Community

Kelly Lima
Kelly Lima

Posted on

IAM Identity Center com Terraform

Recentemente tive um projeto no trabalho envolvendo o Identity Center da AWS (sucessor do Single Sign-On), quis deixar aqui de uma forma mais simples e prática coisas que fui aprendendo.


Para ficar um pouco melhor a visualização do que será feito, elaborei a imagem abaixo:

Imagem do fluxo que será feito

Falando das etapas:

  • Criação de um grupo;
  • Adicionar membro ao grupo;
  • Criação da Permission Set;
  • Adicionar policies na Permission Set;
  • Criar o que é chamado Account Assignment;

Algumas explicações básicas:

  • Permission Set é a forma que o Identity Center trabalha com permissões, por trás nada mais é que uma role que é atribuída à um grupo/usuário;
  • Esse processo que chamamos de Account Assignment nada mais é que criar o vínculo da Permission Set com a conta que você quer (por exemplo, conta de produção, desenvolvimento, etc). Caso você não conheça muito sobre a estrutura do Identity Center, de forma resumida, ele é centralizado, ou seja, todas as minhas permissões para todas as minhas contas irão ficar centralizada em uma conta só dentro do Identity Center, por isso precisamos criar esse vínculo para que ele saiba em qual conta essas permissões se aplicam.

Código

Tentei trazer um exemplo de código que possa ser reutilizado, para ficar um pouco mais elaborado e vocês conseguirem visualizar algo a mais e não ser um exemplo tão simples.

OBS 01: Estou considerando que você já tenha conhecimento de AWS e Terraform, logo suas credenciais/região já devem estar configuradas.
OBS 02: O código só funciona com a versão do provider AWS >= 5.14. Explico melhor sobre no final.

Nós teremos um arquivo de locals chamado local.tf que terá:


data "aws_ssoadmin_instances" "my_sso" {}

output "arns" {
  value = tolist(data.aws_ssoadmin_instances.my_sso.arns)[0]
}

output "identity_store_id" {
  value = tolist(data.aws_ssoadmin_instances.my_sso.identity_store_ids)[0]
}

# sempre vamos acabar precisando utilizar esse id e esse arn em outros lugares
locals {
  identity_store_id  = tolist(data.aws_ssoadmin_instances.my_sso.identity_store_ids)[0]
  identity_store_arn = tolist(data.aws_ssoadmin_instances.my_sso.arns)[0]
}

Enter fullscreen mode Exit fullscreen mode

Nós teremos um outro arquivo chamado user-group.tf, nesse arquivo nós iremos colocar um exemplo de estrutura de usuários que queremos adicionar:


variable "user_group" {
  description = "Create users in Identity Center"
  default = {
    "kelly.lima" = {
      email      = "kelly.lima@email.com"
      group      = ["group-a"]
      first_name = "Kelly"
      last_name  = "Lima"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Um arquivo chamado policies-managed.tf onde colocaremos policies gerenciadas pela AWS a forma de adicioná-las é diferente das policies personalizadas. Nas gerenciadas nós teremos:


variable "policies_managed" {
  description = "Policies Managed (AWS)"
  type        = list(string)
  default = [
    "arn:aws:iam::aws:policy/ReadOnlyAccess",
    "arn:aws:iam::aws:policy/AWSBillingReadOnlyAccess"
  ]
}

Enter fullscreen mode Exit fullscreen mode

Esses valores de arn serão padrão. Você pode pegar esses valores dentro do próprio IAM.

Arquivo de policies personalizadas se chamará policies-custom.tf e terá:


variable "policy_group_a" {
  description = "Policies Custom"
  type        = list(string)
  default = [
    "policies-group-a",
    "policies-default"
  ]
}

Enter fullscreen mode Exit fullscreen mode

No exemplo acima, podemos ver que no Identity Center as policies customizadas nós devemos passar o nome, pois o resource aws_ssoadmin_customer_managed_policy_attachment (que iremos utilizar em breve) ele só aceita nome da policy e não o arn. Nesse caso eu estou considerando que essa policy já foi criada utilizando o resource aws_iam_policy.

Teremos um arquivo que chamaremos de accounts.tf onde terá:


variable "accounts_aws" {
  description = "Accounts AWS"
  default = {
    "prod" = {
      number = "123456789345"
    },
    "dev" = {
      number = "458456789345"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Este é um exemplo de como poderia ficar várias contas, mas nós iremos considerar apenas a conta "prod" para facilitar.

Agora nós teremos o nosso último arquivo onde iremos construir todo esse processo que temos na imagem do começo, ficaria:


# Criação do Group A
resource "aws_identitystore_group" "group_a" {
  identity_store_id = local.identity_store_id
  display_name      = "group-a"
  description       = "sso group a"
}

# Adciona os membros baseado no group em var.user_group
resource "aws_identitystore_group_membership" "membership_group_a" {
  for_each          = { for user in local.group_members : user => aws_identitystore_user.user_sso[user].user_id }
  depends_on        = [aws_identitystore_user.user_sso]
  identity_store_id = local.identity_store_id
  group_id          = aws_identitystore_group.group_a.group_id
  member_id         = each.value
}

# Criação da Permission Set
resource "aws_ssoadmin_permission_set" "permission_set_group_a" {
  name         = "PermissionGroupA"
  instance_arn = local.identity_store_arn
  description  = "Permission Set Custom"
}

# Adiciona as policies managed AWS na Permission Set
resource "aws_ssoadmin_managed_policy_attachment" "policy_managed_group_a" {
  instance_arn       = local.identity_store_arn
  count              = length(var.policies_managed)
  managed_policy_arn = var.policies_managed[count.index]
  permission_set_arn = aws_ssoadmin_permission_set.permission_set_group_a.arn
}

# Adiciona as policies customizadas na Permission Set
resource "aws_ssoadmin_customer_managed_policy_attachment" "policy_custom" {
  for_each = {
    for policy_key, policy_value in var.policy_group_a :
    policy_key => var.policy_group_a[policy_key]
  }
  instance_arn       = local.identity_store_arn
  permission_set_arn = aws_ssoadmin_permission_set.permission_set_group_a.arn
  customer_managed_policy_reference {
    name = each.value
    path = "/"
  }
}

# Adiciona a Permission Set com o Group A à conta "prod"
resource "aws_ssoadmin_account_assignment" "prod_env" {
  for_each = {
    for key, key_value in local.permission_set_arns :
    value_key => local.permission_set_arns[value_key]
  }
  instance_arn       = local.identity_store_arn
  permission_set_arn = each.value

  principal_id   = aws_identitystore_group.group_a.group_id
  principal_type = "GROUP"

  target_id   = var.accounts_aws["prod"].number
  target_type = "AWS_ACCOUNT"
}

locals {
# Aqui você pode colocar outros arns de Permission Sets
  permission_set_arns = [aws_ssoadmin_permission_set.permission_set_group_a.arn]

# Deixei esse locals no mesmo arquivo e não no arquivo local.tf apenas para questão didática, ficar mais prático de visualizar
  group_members = [
    for user, details in var.user_group :
    user if contains(details.group, "group-a")
  ]
}

Enter fullscreen mode Exit fullscreen mode

Alguns pontos:

Como podem ver é utilizado o depends_on no resource aws_identitystore_group_membership foi colocado pois o código estava dando um bug na hora do apply, o Terraform reclamava de adicionar um membro que não existia ainda. Há outros problemas similares quando rodamos o destroy envolvendo Permission Sets, em alguns casos ele reclama do estado que está recebendo (espera estado A e recebe B), ao rodar o comando novamente ele consegue finalizar a exclusão do resource, é realmente um bug nos states do Terraform. Tentei utilizar o depends_on nesse caso também, mas não obtive sucesso, você pode ver melhor sobre nesses links:

Sobre o aviso que deixo acima da versão ser >= 5.14 é por conta desses bugs, antes da versão 5.14 eles ocorriam com maior frequência, houve várias correções referentes a Permission Set.

Conclusão

Bom, eu tentei ser o mais sucinta possível, espero que tenha ajudado, qualquer coisa só perguntar. Valeu! ✌️

Top comments (0)