IAM 초기 세팅 1

(id account) Multi Account 운영에 필요한 IAM 세팅

backend와 provider 설정

  • terraform/iam/dayone-id/backend.tf 파일을 수정합니다.

vim terraform/iam/dayone-id/backend.tf
terraform {
  required_version = "= 0.12.18" # Terraform Version 

  backend "s3" {
    bucket         = "dayone-id-apnortheast2-tfstate" # Set bucket name 
    key            = "dayone/terraform/iam/dayone-id/terraform.tfstate"
    region         = "ap-northeast-2"
    encrypt        = true
    dynamodb_table = "terraform-lock" # Set DynamoDB Table
  }
}
vim provider.tf
provider "aws" {
  region = "us-east-1" # IAM is global
}

공통 권한 생성

  • 모든 사용자가 공통으로 가져야 할 권한을 생성합니다.

  • 이후 각 그룹마다 권한을 매핑할 예정입니다.

terraform/iam/dayone-id/every_policy.tf 를 확인합니다. 수정하실 필요는 없습니다.

vim terraform/iam/dayone-id/every_policy.tf
#### Permission to rotate key
resource "aws_iam_policy" "RotateKeys" {
  name        = "RotateKeys"
  description = "allow users to change their aws keys, and passwords."

  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:*LoginProfile",
                "iam:*AccessKey*",
                "iam:*SSHPublicKey*"
            ],
            "Resource": "arn:aws:iam::${var.account_id}:user/$${aws:username}"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:ListAccount*",
                "iam:GetAccountSummary",
                "iam:GetAccountPasswordPolicy",
                "iam:ListUsers"
            ],
            "Resource": "*"
        }
    ]
}
EOF
}

#### Permission to self managed MFA
resource "aws_iam_policy" "SelfManageMFA" {
  name        = "SelfManageMFA"
  description = "allow a user to manage their own MFA device."

  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowUsersToCreateDeleteTheirOwnVirtualMFADevices",
            "Effect": "Allow",
            "Action": [
                "iam:*VirtualMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::${var.account_id}:mfa/$${aws:username}"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "iam:GetAccountPasswordPolicy",
            "Resource": "*"
        },
        {
            "Sid": "AllowUsersToManageTheirOwnPassword",
            "Effect": "Allow",
            "Action": [
                "iam:ChangePassword"
            ],
            "Resource": [
                "arn:aws:iam::${var.account_id}:user/$${aws:username}"
            ]
        },
        {
            "Sid": "AllowUsersToEnableSyncDisableTheirOwnMFADevices",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::${var.account_id}:user/$${aws:username}"
            ]
        },
        {
            "Sid": "AllowUsersToListVirtualMFADevices",
            "Effect": "Allow",
            "Action": [
                "iam:ListVirtualMFADevices"
            ],
            "Resource": [
                "arn:aws:iam::${var.account_id}:mfa/*"
            ]
        },
        {
            "Sid": "AllowUsersToListUsersInConsole",
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers"
            ],
            "Resource": [
                "arn:aws:iam::${var.account_id}:user/*"
            ]
        }
    ]
}
EOF
}

#### Force user to use MFA for security issue
resource "aws_iam_policy" "ForceMFA" {
  name        = "ForceMFA"
  description = "disallow a user anything unless MFA enabled"

  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DoNotAllowAnythingOtherThanAboveUnlessMFAd",
            "Effect": "Deny",
            "NotAction": [
                "iam:*",
                "sts:AssumeRole",
                "s3:*",
                "dynamodb:*",
                "*"
            ],
            "Resource": "*",
            "Condition": {
                "Null": {
                    "aws:MultiFactorAuthAge": "true"
                }
            }
        }
    ]
}
EOF
}

Module 세팅

  • Multi-account 세팅을 위해서 각 account마다 필요한 정책을 생성해야 합니다.

  • 공통 리소스는 module을 통해서 생성합니다.

    • terraform/iam/dayone-id/_module_assume_policy 폴더를 생성하여 정의

terraform/iam/dayone-id/_module_assume_policy/main.tf 를 확인합니다. 따로 수정하실 필요는 없습니다.

vim terraform/iam/dayone-id/_module_assume_policy/main.tf
resource "aws_iam_policy" "policy" {
  name        = "assume-${var.aws_account}-${var.subject}-policy"
  path        = "/"
  description = "${var.aws_account}-${var.subject}-policy"
  policy      = data.aws_iam_policy_document.policy-document.json
}

data "aws_iam_policy_document" "policy-document" {
  statement {
    effect = "Allow"

    resources = var.resources

    actions = [
      "sts:AssumeRole",
    ]
  }
}

Production 계정 접근을 위한 Policy 생성

  • Production 계정에 Assume하기 위해서는 권한이 필요합니다.

  • id 계정에서 production계정의 role을 사용하기 위한 권한 설정이므로, 추후에 production 계정에 해당 Role을 생성해야 합니다.( 본 설정만으로는 assume할 수 없습니다.)

terraform/iam/dayone-id/assume_policy_benx_prod.tf 를 수정합니다.

vim terraform/iam/dayone-id/assume_policy_benx_prod.tf
### Create policies for allowing user to assume the role in the production account
### You can copy this file and change `prod` to other environment if you have any other account

# Admin Access policy 
# If this policy is applied, then you will be able to assume role in the production account with admin permission
module "dayone_prod_admin" {
  source      = "./_module_assume_policy/"
  aws_account = "dayone-prod"
  subject     = "admin"
  resources   = ["arn:aws:iam::${var.prod_account_id}:role/assume-dayone-prod-admin"]
}

output "assume_dayone_prod_admin_policy_arn" {
  value = module.dayone_prod_admin.assume_policy_arn
}

# Poweruser Access policy 
# If this policy is applied, then you will be able to assume role in the production account with poweruser permission
module "dayone_prod_poweruser" {
  source      = "./_module_assume_policy/"
  aws_account = "dayone-prod"
  subject     = "poweruser"
  resources   = ["arn:aws:iam::${var.prod_account_id}:role/assume-dayone-prod-poweruser"]
}

output "assume_dayone_prod_poweruser_policy_arn" {
  value = module.dayone_prod_poweruser.assume_policy_arn
}


# ReadOnly Access policy 
# If this policy is applied, then you will be able to assume role in the production account with readonly permission
module "dayone_prod_readonly" {
  source      = "./_module_assume_policy/"
  aws_account = "dayone-prod"
  subject     = "readonly"
  resources   = ["arn:aws:iam::${var.prod_account_id}:role/assume-dayone-prod-readonly"]
}

output "assume_dayone_prod_readonly_policy_arn" {
  value = module.dayone_prod_readonly.assume_policy_arn
}

팀별로 그룹과 사용자 생성

  • 편의상 아래와 같이 세팅했습니다.

    • group_dayone_devops_black

      • 모든 계정에서 admin 권한을 사용할 수 있는 사용자의 그룹

      • 예제 사용자 : admin@dayone.com

    • group_dayone_devops_white

      • 모든 계정에서 readonly 권한을 사용할 수 있는 사용자의 그룹

      • 예제 사용자 : admin@dayone.com

  • 위 세팅은 회사 상황에 맞게 수정하시기 바랍니다.

  • 참고로 vim을 사용하시면 :%s/dayone/<company_name>/g 명령을 통해 손쉽게 수정하실 수 있습니다.

terraform/iam/dayone-id/group_dayone_devops_black.tf 을 수정합니다.

  • (white는 생략합니다.)

vim terraform/iam/dayone-id/group_dayone_devops_black.tf
############## Dayone DevOps Group ##################
resource "aws_iam_group" "dayone_devops_black" {
  name = "dayone_devops_black"
}

resource "aws_iam_group_membership" "dayone_devops_black" {
  name = aws_iam_group.dayone_devops_black.name

  users = [
    aws_iam_user.admin_dayone.name
  ]

  group = aws_iam_group.dayone_devops_black.name
}

#######################################################

########### Dayone DevOps users #####################

resource "aws_iam_user" "admin_dayone" {  
  name = "admin@dayone.com"       # Edit this value to the username you want to use 
}

#######################################################

############### DevOps Basic Policy ##################
######################################################

########### DevOps Assume Policies ####################
resource "aws_iam_group_policy_attachment" "dayone_devops_black" {
  count      = length(var.userassume_policy_dayone_devops_black)
  group      = aws_iam_group.dayone_devops_black.name
  policy_arn = var.userassume_policy_dayone_devops_black[count.index]
}

variable "userassume_policy_dayone_devops_black" {
  description = "IAM Policy to be attached to user"
  type        = list(string)

  default = [
    # Please change <account_id> to the real account id number of id account 
    "arn:aws:iam::<account_id>:policy/assume-dayone-prod-admin-policy", # Add admin policy to black group user 
  ]
}

#######################################################


############### MFA Manager ###########################
resource "aws_iam_group_policy_attachment" "dayone_devops_black_rotatekeys" {
  group      = aws_iam_group.dayone_devops_black.name
  policy_arn = aws_iam_policy.RotateKeys.arn
}

resource "aws_iam_group_policy_attachment" "dayone_devops_black_selfmanagemfa" {
  group      = aws_iam_group.dayone_devops_black.name
  policy_arn = aws_iam_policy.SelfManageMFA.arn
}

resource "aws_iam_group_policy_attachment" "dayone_devops_black_forcemfa" {
  group      = aws_iam_group.dayone_devops_black.name
  policy_arn = aws_iam_policy.ForceMFA.arn
}

#######################################################

리소스 생성

수정을 마쳤으면 terraform 초기화 작업을 진행합니다.

$ terraform init
Initializing modules...
- dayone_prod_admin in _module_assume_policy
- dayone_prod_poweruser in _module_assume_policy
- dayone_prod_readonly in _module_assume_policy

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (hashicorp/aws) 2.56.0...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 2.56"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

리소스 생성을 확인합니다.

$ terraform plan -parallelism=30
Acquiring state lock. This may take a few moments...
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

module.dayone_prod_readonly.data.aws_iam_policy_document.policy-document: Refreshing state...
module.dayone_prod_admin.data.aws_iam_policy_document.policy-document: Refreshing state...
module.dayone_prod_poweruser.data.aws_iam_policy_document.policy-document: Refreshing state...

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_iam_group.dayone_devops_black will be created
  + resource "aws_iam_group" "dayone_devops_black" {
      + arn       = (known after apply)
      + id        = (known after apply)
      + name      = "dayone_devops_black"
      + path      = "/"
      + unique_id = (known after apply)
    }

  # aws_iam_group.dayone_devops_white will be created
  + resource "aws_iam_group" "dayone_devops_white" {
      + arn       = (known after apply)
      + id        = (known after apply)
      + name      = "dayone_devops_white"
      + path      = "/"
      + unique_id = (known after apply)
    }

  ( ... 중략 ...)

  # module.dayone_prod_admin.aws_iam_policy.policy will be created
  + resource "aws_iam_policy" "policy" {
      + arn         = (known after apply)
      + description = "dayone-prod-admin-policy"
      + id          = (known after apply)
      + name        = "assume-dayone-prod-admin-policy"
      + path        = "/"
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action   = "sts:AssumeRole"
                      + Effect   = "Allow"
                      + Resource = "arn:aws:iam::xxxxxxxx:role/assume-dayone-prod-admin"
                      + Sid      = ""
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
    }

  # module.dayone_prod_poweruser.aws_iam_policy.policy will be created
  + resource "aws_iam_policy" "policy" {
      + arn         = (known after apply)
      + description = "dayone-prod-poweruser-policy"
      + id          = (known after apply)
      + name        = "assume-dayone-prod-poweruser-policy"
      + path        = "/"
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action   = "sts:AssumeRole"
                      + Effect   = "Allow"
                      + Resource = "arn:aws:iam::xxxxxxxx:role/assume-dayone-prod-poweruser"
                      + Sid      = ""
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
    }

  # module.dayone_prod_readonly.aws_iam_policy.policy will be created
  + resource "aws_iam_policy" "policy" {
      + arn         = (known after apply)
      + description = "dayone-prod-readonly-policy"
      + id          = (known after apply)
      + name        = "assume-dayone-prod-readonly-policy"
      + path        = "/"
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action   = "sts:AssumeRole"
                      + Effect   = "Allow"
                      + Resource = "arn:aws:iam::xxxxxxxx:role/assume-dayone-prod-readonly"
                      + Sid      = ""
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
    }

Plan: 20 to add, 0 to change, 0 to destroy.

Plan: 20 to add, 0 to change, 0 to destroy. 가 나오면 정상입니다. 추가로 사용자나 그룹을 생성하신 경우에는 값이 다를 수 있습니다.

이제, 리소스를 생성합니다.

$ terraform apply -parallelism=30
module.dayone_prod_admin.data.aws_iam_policy_document.policy-document: Refreshing state...
module.dayone_prod_readonly.data.aws_iam_policy_document.policy-document: Refreshing state...
module.dayone_prod_poweruser.data.aws_iam_policy_document.policy-document: Refreshing state...

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:
(... 중략 ...)


Plan: 20 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_iam_group.dayone_devops_white: Creating...
aws_iam_group.dayone_devops_black: Creating...
aws_iam_user.admin_dayone: Creating...
module.dayone_prod_poweruser.aws_iam_policy.policy: Creating...
module.dayone_prod_admin.aws_iam_policy.policy: Creating...
aws_iam_user.readonly_dayone: Creating...
module.dayone_prod_readonly.aws_iam_policy.policy: Creating...
aws_iam_policy.RotateKeys: Creating...
aws_iam_policy.ForceMFA: Creating...
aws_iam_policy.SelfManageMFA: Creating...
(... 중략 ...)

Apply complete! Resources: 20 added, 0 changed, 0 destroyed.

Outputs:

assume_dayone_prod_admin_policy_arn = arn:aws:iam::xxxxxxxxxxx:policy/assume-dayone-prod-admin-policy
assume_dayone_prod_poweruser_policy_arn = arn:aws:iam::xxxxxxxxxxx:policy/assume-dayone-prod-poweruser-policy
assume_dayone_prod_readonly_policy_arn = arn:aws:iam::xxxxxxxxxxx:policy/assume-dayone-prod-readonly-policy

Apply complete! Resources: 20 added, 0 changed, 0 destroyed. 결과가 plan과 같은지 확인합니다.

사용자 비밀번호 초기화

  • 테라폼으로 생성한 사용자에게 초기 비밀번호를 세팅해줍니다.

  • 세팅한 후에 해당 사용자에게 id와 비밀번호를 알려주고 패스워드 변경과 MFA 설정을 요청하시면 됩니다.

IAM > Users > 사용자 선택 > Security credentials 로 이동합니다.

Console password > Manage 버튼을 클릭하여 아래와 같이 세팅합니다.

Apply 를 진행한 후에, Console Password를 복사하여 계정 아이디(예제에서는 admin@dayone.com)과 함께 사용자에게 전달합니다.

Last updated