logo

DowanKim

Multi-Account Management System을 구축해보자

2026년 9월 12일

PLick

쿠팡, 티빙, 강남언니, 29CM까지 최근 개인정보 유출을 비롯한 보안 사고가 잇따르고 있습니다. 진행 중인 프로젝트를 인프라 관점에서 어떻게 더 안전하게 만들 수 있을지 팀원들과 고민하던 중 SW 마에스트로 멘토이신 한화비전, 무신사, 기아 현직 인프라 엔지니어분들께 직접 질문드렸습니다. 그 과정에서 Zero-Trust로 가기 위한 전제인 계정 & 권한 경계를 세우는 기술인 AWS Organizations, SCP, Control Tower, IAM Identity Center와 함께 현업에서 수십 개 계정과 수백 명의 권한을 운영하며 겪는 챌린지, 그리고 인프라 엔지니어로서의 지향점까지 깊게 배울 수 있었습니다.

기본적인 Multi-Account Management System을 팀에 제안하기 전, 이를 구축해서 실제로 보여주고 제안하기 위해, 실제로 구축해보는 과정을 기록합니다.

결과물 미리보기

image.png

image.png


핵심 개념

Organizations & OU 구성

  • 계정(Account)은 AWS의 가장 강력한 격리 단위 : 사고가 계정 밖으로 번지지 않고, 비용 & 한도 & 권한이 계정 단위로 끊긴다
  • Organizations는 계정 여러 개를 하나의 조직으로 묶는 서비스 : 조직을 만든 계정이 관리 계정, 나머지가 멤버 계정
  • OU는 계정을 담는 폴더가 아니라 정책이 적용되는 경계 : OU에 건 정책은 트리 아래로 상속된다

가드레일 & SCP

  • 가드레일 = "조직이 벗어나면 안 되는 경계" 규칙 (예 : 뭄바이 & 도쿄 리전 금지, 루트 사용자 금지, CloudTrail 끄기 금지)
  • SCP는 가드레일을 구현하는 기술 : 권한을 주는 게 아니라 최대 범위만 정한다
  • 계정에 배포되지 않고 OU에 붙여두면 API 호출 시점마다 평가 : attach 즉시 적용된다
  • 예방형(SCP, 사전 차단)과 탐지형(Config, 사후 감지)으로 나뉜다

Control Tower

  • Organizations & Identity Center & Config & CloudTrail을 검증된 표준 구성(랜딩 존)으로 자동 셋업하는 서비스
  • Organizations 위에 얹는 자동화 계층 : OU 생성에 Control Tower가 필요한 건 아니다
  • 셋업하면 Security OU와 공유 계정 2개(Log Archive, Audit)가 자동 생성된다
  • 가드레일을 Controls라는 이름으로 관리 : Preventive(SCP 기반) & Detective(Config 기반) & Proactive(CloudFormation Hook 기반) 3종

Account Factory

  • Control Tower의 계정 공장 : 발급된 계정은 베이스라인(Config, CloudTrail 연동, 기본 역할)이 장착된 상태로 나온다
  • 블루프린트(Account Factory Customization)를 더하면 우리가 만든 CloudFormation 템플릿까지 발급 시 자동 배포된다
  • 템플릿 파라미터로 계정마다 다른 값을 주입할 수 있다 (이 랩에서는 VPC 대역)

SSO & Permission Set

  • 계정마다 IAM User를 만들면 장기 자격증명(비밀번호, Access Key)이 계정 수만큼 생긴다
  • IAM Identity Center는 사람을 한 곳에서 관리하고, 로그인하면 임시 자격증명으로 각 계정에 들어가는 구조
  • Permission Set = "이 계정에서 뭘 할 수 있나"의 권한 묶음 : 실체는 각 계정 안에 배포되는 IAM Role(AWSReservedSSO_*)
  • 할당은 그룹 & 계정 & Permission Set 3축으로 조합한다

최종 구조 미리보기

image.png


1. 1단계. Organizations 활성화 + OU 구성 + dev 계정

Organizations란

  • 여러 AWS 계정을 하나의 조직(Organization)으로 묶어 중앙에서 관리하는 서비스입니다
  • 조직을 생성한 계정이 관리 계정(management account)이 됩니다 : 조직 구조와 정책을 관리하고, 전체 비용을 통합 결제합니다
  • 나머지 계정은 멤버 계정입니다 : 직접 생성하거나 기존 계정을 초대해서 편입합니다

OU(Organizational Unit)란

  • 계정을 묶는 그룹으로, Root 아래에 트리 구조로 배치됩니다
  • 폴더가 아니라 정책이 적용되는 경계입니다 : OU에 정책(SCP)을 붙이면 하위 OU와 계정 전체에 상속됩니다
  • 그래서 OU는 부서가 아니라 "같은 거버넌스를 받아야 하는 계정끼리" 묶습니다. 이 랩에서 Prod(prod & qa)와 NonProd(dev)로 나누는 이유입니다
  • 계정은 동시에 하나의 OU에만 속할 수 있습니다

조직 생성 1 : Organizations 콘솔 진입

image.png

우측 카드의 Create an organization 버튼이 시작점입니다. 버튼 아래 안내처럼 기본값은 all features enabled이고, consolidated billing(비용 통합)만 켜는 축소 모드도 있지만 우리는 SCP를 써야 하므로 기본값 그대로 진행합니다. 우측 Pricing 카드에서 Organizations 자체는 무료라는 것도 확인해 두세요.

조직 생성 2 : 생성 확인

image.png 버튼을 누르면 별도 확인 절차 없이 곧바로 조직이 만들어지고 AWS accounts 화면으로 전환됩니다. 세 가지를 확인합니다.

  • 상단의 "You successfully created an AWS organization." 배너
  • Root 트리 안에 내 계정이 management account 배지와 함께 들어가 있는 것 : 조직을 만든 순간 내 계정이 관리 계정이 됩니다
  • 좌측의 Organization ID

이메일 인증 안내는 이미 인증이 끝난 계정이면 나타나지 않습니다.

조직 생성 3 : 루트 접근 중앙화 (Enable in IAM)

화면 상단 파란 배너 "Centralize root access for member accounts"의 Enable in IAM 버튼을 누르면 IAM의 활성화 화면으로 이동합니다.

image.png

  • Root credentials management (체크 유지) : 멤버 계정의 루트 자격증명을 중앙에서 삭제/감사하고, "비밀번호 찾기"를 통한 루트 복구를 차단합니다. 이후 생성되는 멤버 계정은 루트 비밀번호가 없는 상태로 태어납니다
  • Privileged root actions in member accounts (체크 유지) : 루트만 할 수 있는 특수 작업(잘못 잠근 S3 버킷 정책 & SQS 정책 삭제 등)을 멤버 계정에 루트 없이 관리 계정에서 대신 수행할 수 있게 합니다. 루트를 없앤 대신의 탈출구입니다
  • Delegated administrator : 비워두고 그냥 Enable을 누르세요. 관리 계정 외에 루트 중앙 관리 권한을 위임할 멤버 계정을 지정하는 칸인데, 아직 멤버 계정이 없어 지정할 수 없고 활성화 후 언제든 추가할 수 있습니다

OU 만들기 1 : Workloads OU 생성

image.png

AWS accounts 화면의 Organizational structure에서 Root 체크박스를 선택하면 우측 상단 Actions 메뉴가 활성화됩니다. Actions → Organizational unit의 Create new를 누르세요. 스크린샷처럼 메뉴가 두 그룹으로 나뉘는 게 보입니다 : OU를 선택하면 Organizational unit 그룹이, 계정을 선택하면 AWS account 그룹(Move, Close 등)이 활성화됩니다. 지금은 Root를 선택했으므로 OU 생성만 가능합니다.

다음 화면에서 이름에 Workloads를 입력하고 Create organizational unit을 누릅니다. 태그는 넣지 않아도 됩니다.

OU 만들기 2 : Prod & NonProd OU 생성

같은 작업을 두 번 반복합니다. 이번에는 Root 대신 방금 만든 Workloads를 체크하고 Actions → Create new로 Prod를, 한 번 더 반복해서 NonProd를 만듭니다. 어떤 항목을 체크했느냐가 곧 새 OU의 부모가 되므로, Workloads를 체크했는지 꼭 확인하세요.

Root
└── Workloads
    ├── Prod         : prod, qa가 들어갈 자리 
    └── NonProd      : dev가 들어갈 자리 

SCP 활성 확인

SCP(Service Control Policy)는 조직 차원에서 계정들이 쓸 수 있는 권한의 최대 범위를 정하는 정책입니다. 권한을 부여하는 게 아니라 천장을 정하는 것이고, OU에 붙이면 하위 계정 전체에 상속됩니다. 자세한 개념과 작성은 2단계에서 다루고, 여기서는 활성 상태만 확인합니다.

image.png

예전에는 SCP를 수동으로 켜야 했지만, 현재 콘솔은 조직을 생성하면 자동으로 활성화해 줍니다. 우측 상단 버튼이 "Disable service control policies"로 보이면 이미 켜져 있는 것입니다. 목록에는 정책 2개가 자동으로 준비되어 있습니다.

  • FullAWSAccess (AWS managed) : 모든 작업을 허용하는 기본 정책. SCP의 출발점은 "전부 허용"이고, 여기서 Deny 정책을 겹쳐 깎아 들어가는 구조입니다
  • DenyLeaveAndCloseAccount (Customer managed) : 콘솔이 만들어 준 권장 가드레일. 멤버 계정이 스스로 조직을 떠나거나(LeaveOrganization) 계정을 닫는 것을 막습니다

image.png

FullAWSAccess를 클릭해 Targets 탭을 열면 Root, 방금 만든 OU 3개(Workloads, Prod, NonProd), 관리 계정까지 전부에 붙어 있는 것이 보입니다. 그래서 SCP가 켜져 있어도 지금은 아무것도 차단되지 않는 상태입니다

dev 계정 생성

AWS accounts 화면 우측 상단의 Add an AWS account 버튼 → Create an AWS account를 선택합니다. 입력값은 3가지입니다.

  • AWS account name : {{Project Name}}-dev
  • Email address : {{Gmail ID}}+awsdev@gmail.com : 계정마다 고유 이메일이 필요하다는 제약을 별칭으로 풀기
  • IAM role name : 기본값 OrganizationAccountAccessRole 그대로. 관리 계정에서 이 계정으로 들어가는 통로 역할이며, 잠시 뒤 역할 전환에서 사용합니다

Create를 누르면 1~2분 안에 생성되고, 완료되면 Root 바로 아래에 나타납니다. 1단계에서 루트 접근 중앙화를 켜뒀기 때문에 이 계정은 루트 비밀번호가 없는(rootless) 상태로 태어납니다.

image.png 생성이 끝나면 dev 계정 체크 → Actions → AWS account : Move로 Workloads/NonProd에 이동시킵니다.

관리자 IAM 사용자 만들기

잠시 뒤 할 역할 전환은 루트 사용자로는 불가능합니다. 루트는 다른 계정의 역할을 빌리는 것(AssumeRole) 자체가 차단되어 있어서, 메뉴도 보이지 않고 시도해도 실패합니다. 그래서 관리 계정에 관리자 IAM 사용자를 만들고, 이후 작업은 그 사용자로 진행합니다. "루트는 봉인하고 일상 작업은 IAM으로"라는 실무 원칙 그대로입니다.

image.png

  • User name : {{Project Name}}-iam
  • Provide user access to the AWS Management Console 체크 : 콘솔 로그인용이 핵심입니다
  • User type은 "I want to create an IAM user" 선택 : Identity Center를 권장한다는 안내가 뜨지만, 그건 4단계에서 다룹니다
  • Console password : Autogenerated(자동 생성) 그대로 두면 마지막 화면에서 확인할 수 있습니다
  • 다음 단계(Set permissions)에서 Attach policies directly → AdministratorAccess를 붙입니다

image.png 생성 완료 화면의 Console sign-in URL과 비밀번호는 이 화면에서만 보여줍니다. Download .csv 또는 복사로 반드시 기록해 두세요. 로그인 URL은 https://<관리계정ID>.signin.aws.amazon.com/console 형식입니다.

멀티 세션을 켜고 IAM 사용자로 로그인

루트 세션을 유지한 채 IAM 사용자 세션을 나란히 열기 위해 콘솔의 멀티 세션 기능을 사용합니다 (무료 편의 기능입니다).

image.png 우측 상단 계정 드롭다운에서 Turn on multi-session support를 누르면 멀티 세션 모드가 켜집니다. 이후 세션 추가 버튼으로 새 로그인 화면을 열고, 기록해 둔 URL에서 {{Project Name}}-iam과 비밀번호로 로그인합니다.

image.png 로그인이 되면 드롭다운에 현재 세션 = IAM 사용자, 기타 활성 세션 = root가 나란히 보입니다. 현재 세션의 "IAM 사용자" 항목에 방금 만든 사용자 이름이 표시되는지 확인하세요.

역할 전환으로 dev 접속

역할 전환은 dev에 "로그인"하는 게 아닙니다. 지금의 관리 계정 세션으로 dev 계정 안의 역할을 잠시 빌려 쓰는 것입니다. 두 조건이 맞물려 성립합니다 : dev 쪽에는 Organizations가 계정을 만들 때 심어둔 OrganizationAccountAccessRole이 있고 그 신뢰 정책이 관리 계정을 허용하며, 우리 IAM 사용자 쪽에는 역할을 빌릴 권한(sts:AssumeRole, AdministratorAccess에 포함)이 있습니다.

먼저 입력할 값 2가지를 준비합니다.

image.png

  • 계정 ID (12자리) : Organizations → AWS accounts에서 dev 계정을 클릭하면 상세 화면 제목과 ID 항목에 나옵니다. 복사해 두세요
  • 역할 이름 : 계정을 만들 때 기본값으로 둔 OrganizationAccountAccessRole입니다. 생성 후에는 콘솔 어디에서도 이 이름을 다시 보여주지 않으므로, 계정을 만들 때 기록해 두는 것이 원칙입니다

IAM 사용자 세션의 드롭다운에서 세션 추가 옆 화살표(▼) → 역할 전환을 누르고, 폼에 값을 입력합니다.

image.png

  • Switch from : 세션이 여러 개면 선택지가 나옵니다. 반드시 {{Project Name}}-iam 세션을 선택하세요. root 세션을 선택하면 전환이 실패합니다
  • Account ID = dev 계정 ID, IAM role name = OrganizationAccountAccessRole, Display name = {{Project Name}}-dev, 색상은 자유

image.png 전환이 되면 상단 바의 계정 표시가 dev로 바뀝니다. 드롭다운을 열어 보면 페더레이션 사용자가 OrganizationAccountAccessRole/{{Project Name}}-iam으로 표시되는데, 이것이 "관리 계정의 IAM 사용자가 dev의 역할을 빌려 쓰고 있다"는 물증입니다. 한 번 전환하면 드롭다운에 이력이 남아 다음부터는 클릭 한 번으로 오갈 수 있습니다.

통합 결제 (Consolidated Billing)

조직을 만들면 관리 계정이 하위 모든 멤버 계정의 운영비를 통합 결제합니다. AWS accounts 화면 안내문에도 "관리 계정이 조직 내 모든 계정의 청구를 책임진다"고 명시되어 있습니다. OU 위치나 트리 구조와는 무관하며, 조직의 멤버 계정이기만 하면 비용이 관리 계정으로 합산 청구됩니다.

image.png


2단계. 가드레일을 SCP로 구현하기

AWS 계정을 운영하다 보면 누군가는 실수할 수 있습니다. 그래서 조직 & 계정 차원에서 "금지하는 행위 또는 규칙"을 정의하는데 우리는 이걸 "가드레일"이라고 부릅니다. 예를 들어 "뭄바이 & 도쿄 리전 사용 금지", "루트 사용자 사용 금지" 같은 규칙입니다.

이런 규칙을 계정 단위로 적용하고 싶을 때 구현하는 기술이 SCP(Service Control Policy)입니다. SCP는 JSON 정책으로 작성해 OU에 붙이며, 계정 안에 배포되는 게 아니라 OU에 붙여두고 API 호출 시점마다 평가됩니다. 그래서 attach/detach가 즉시 반영됩니다. 하위 계정 전체에 상속되는 것도 이 구조 덕분입니다.

트리의 어느 노드에 붙이느냐가 곧 적용 범위입니다. 위에 붙일수록 넓게, 아래에 붙일수록 좁게 걸립니다.

Root            ← 여기 붙이면 조직 전체 적용
└── Workloads   ← 여기 붙이면 Prod & NonProd & 그 안의 모든 계정 적용
    ├── Prod    ← 여기 붙이면 prod, qa만 적용
    └── NonProd ← 여기 붙이면 dev만 적용

가드레일 : "뭄바이 & 도쿄 리전 사용 금지" 이 가드레일을 SCP로 구현하는 방법은 두 가지입니다.

방식 1 : 차단 목록 : 금지할 리전을 나열합니다.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyBlockedRegions",
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*",
    "Condition": {
      "StringEquals": {
        "aws:RequestedRegion": ["ap-south-1", "ap-northeast-1"]
      }
    }
  }]
}

직관적이지만 구멍이 있습니다 : AWS가 새 리전을 열 때마다 목록에 추가해야 하고, 빠뜨리면 그 리전은 열려 있습니다.

방식 2 : 허용 목록 (실무는 이렇게 적용하는 방안을 지향합니다) : 허용할 리전만 나열하고 나머지는 전부 거부합니다.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyOutsideAllowedRegions",
    "Effect": "Deny",
    "NotAction": ["iam:*", "sts:*", "organizations:*", "support:*", "budgets:*"],
    "Resource": "*",
    "Condition": {
      "StringNotEquals": { "aws:RequestedRegion": ["ap-northeast-2", "us-east-1"] }
    }
  }]
}

뭄바이, 도쿄는 물론 처음 보는 신규 리전까지 자동으로 막힙니다. "금지할 것을 나열하지 말고 허용할 것만 나열하라"가 가드레일 설계의 기본 원칙입니다. NotAction에 IAM/STS 등은 제외해야 합니다. 이것들은 글로벌 서비스라 us-east-1을 경유하는데 막으면 로그인이 안 됩니다. SCP 초기 운영에서 발생하는 흔한 실수입니다.

SCP 만들기 1 : 정책 생성

SCP는 Organizations의 여러 정책 유형 중 하나입니다. Policies 화면을 보면 SCP만 Enabled이고 RCP & Tag & S3 정책 등은 Disabled 상태인 것을 볼 수 있습니다 (조직 생성 시 SCP만 자동 활성화되기 때문).

image.png

Service control policies 화면에서 우측 상단 Create policy를 누르고 아래처럼 입력합니다.

  • Policy name : deny-outside-allowed-regions
  • Description (선택) : 서울, 버지니아 외 리전 사용 차단
  • JSON 편집기의 기본 틀을 지우고 위 방식 2 JSON을 붙여넣기 → Create policy

image.png

목록에 deny-outside-allowed-regions가 Customer managed policy로 추가됩니다. 아직 아무 데도 붙지 않아 효과는 없습니다.

SCP 만들기 2 : Workloads OU에 부착

목록에서 방금 만든 정책을 체크 → Actions → Attach policy를 선택합니다. (정책 이름을 클릭해 상세로 들어간 뒤 Targets 탭 → Attach로 해도 됩니다.)

image.png

대상 선택 트리에서 Workloads를 선택하고 Attach합니다. Root나 개별 계정이 아니라 Workloads OU여야 하위의 Prod & NonProd 계정 전체(현재는 dev)에 상속됩니다. 붙는 즉시 적용됩니다.

부착 결과는 Workloads OU 상세의 Policies 탭에서 확인합니다. Applied policies에 방금 붙인 정책이 Attached directly로, Root에 걸린 정책들은 Attached to Root(상속)로 구분되어 나타납니다.

image.png

SCP 만들기 3 : 차단 확인

차단 테스트는 반드시 우측 상단 계정 표시가 {{Project Name}}-dev (멤버 계정 ID)인 상태에서 하세요. 관리 계정({{Project Name}}-iam으로 로그인된 상태)에서는 SCP가 적용되지 않아 도쿄에서도 그냥 성공합니다. 멀티 세션은 탭마다 신원이 다를 수 있으니, 드롭다운에서 현재 세션이 dev로 역할 전환된 것을 확인하고 진행합니다.

{{Project Name}}-dev 세션에서 우상단 리전을 도쿄(아시아 태평양)로 바꾸고 EC2 콘솔을 엽니다. EC2 대시보드의 위젯들이 액세스 거부로 도배되고, 에러 문구에 explicit deny in a service control policy: ...p-0wlt0tik가 보입니다. 방금 만든 SCP의 정책 ID가 그대로 찍혀 있어, 정확히 이 SCP가 막았다는 물증이 됩니다. 리소스 생성을 시도해도 같은 이유로 거부됩니다. 서울 리전으로 돌아오면 모든 것이 정상 동작합니다.

image.png

3단계. Control Tower + Account Factory

Control Tower와 랜딩 존

  • Control Tower는 멀티 어카운트 환경을 하나하나 직접 만들 필요 없이 AWS 모범사례대로 설정된 환경을(계정을) 자동으로 세팅해 주는 서비스입니다
  • 이 "모범사례가 적용된 어카운트 기본 환경"을 랜딩 존(Landing Zone)이라고 부릅니다. 예를 들어 계정을 발급할 때 VPC & IGW & Subnet & RT & NAT 등 기초 세팅이 갖춰진 채로 만들어집니다
  • 랜딩 존을 켜면 아래 다섯 가지가 세트로 자동 구성됩니다. 개념을 먼저 훑고 3-1부터 실제 셋업에 들어갑니다

1. 로그 & 감사 전용 계정 (추가 생성)

랜딩 존은 Security OU와 그 안에 공유 계정 2개를 자동으로 추가 생성합니다. CloudTrail 기록은 Log Archive 계정의 S3에 모이고, Config 기록은 Audit 계정의 S3와 애그리게이터에 모입니다. Audit 계정은 이 기록을 읽어 분석하며 조직 전체 계정을 감사하는 보안 계정입니다.

  • Log Archive 계정 : 조직 전체의 CloudTrail 로그(누가 언제 무슨 API를 호출했나)가 모이는 저장소입니다. 쓰기만 되고 사람이 로그인할 일이 없는 증거 보관소입니다. App이나 서버 로그를 저장하는 것이 아닙니다. 조직 감사에 필요한 로그만 수집합니다
  • Audit 계정 : 각 계정의 Config 기록(리소스 구성 변경 이력)이 모이는 곳입니다. 모인 기록을 읽어 분석하고 조직 전체 계정을 감사하는 보안 계정이기도 합니다. 각 멤버 계정을 직접 점검하는 감사 권한도 가지며, GuardDuty & Security Hub 같은 보안 서비스를 조직 차원에서 중앙 관리하는 위임 관리자 자리로도 쓰입니다
Root  (관리 계정)
├── Security OU          Control Tower가 자동 생성
│   ├── Log Archive      전 계정의 CloudTrail/Config 로그 집중 저장
│   └── Audit            전 계정을 들여다보는 보안 감사용
└── Workloads OU         1단계에서 직접 생성, 여기서 등록(enroll)
    ├── Prod OU          prod & qa : Account Factory로 발급
    └── NonProd OU       dev : 1단계에서 수동 생성

2. CloudTrail & Config 자동 적용

  • CloudTrail : 누가 언제 무슨 API를 호출했는지 감사 로그로 기록. 관리 계정의 조직 트레일 하나가 전 계정을 커버하며 Log Archive 계정에 집중 저장
  • AWS Config : 각 계정의 리소스 상태를 계속 기록하고 규칙 위반을 감지 (Detective control의 실체). 편입되는 계정마다 레코더 설치, 기록은 Audit 계정에 집중 저장

조직 트레일은 편입 여부와 무관하게 전 계정을 기록합니다. Config 레코더는 편입된 멤버 계정에 의무 설치되며 골라 끌 수 없습니다. 관리 계정은 거버넌스 대상이 아니라 Config 레코더가 깔리지 않습니다. Config 때문에 이 단계부터 월 $2~5의 최소 비용이 발생합니다.

3. 가드레일은 Controls로 관리

Control Tower는 가드레일을 Controls라는 이름으로 관리합니다. 2단계에서 우리가 SCP를 직접 JSON으로 짜서 붙였다면, Control Tower는 검증된 가드레일들을 콘솔에서 켜고 끄는 항목으로 제공합니다. 동작 방식에 따라 3종류입니다.

  • Preventive : 위반 행동을 사전 차단 (내부적으로 SCP로 구현)
  • Detective : 위반을 사후 감지해 표시 (내부적으로 Config로 구현)
  • Proactive : 리소스 생성 시점에 검사 (CloudFormation Hook으로 구현)

커스텀 SCP(2단계)와의 관계 : Control Tower를 켜도 우리가 만든 SCP는 사라지지 않습니다. Controls가 그 위에 추가로 얹힐 뿐 대체하지 않으며, 같은 OU에서 둘 다 함께 평가됩니다.

image.png

실무에서의 분담 : 표준 규칙(리전 제한, CloudTrail 끄기 방지, S3 퍼블릭 차단 등)은 Control Tower Controls로 켜서 관리하고, Control 목록에 없는 조직 고유 규칙(특정 인스턴스 타입만 허용, 태그 강제 등)만 커스텀 SCP로 직접 만듭니다.

4. 새 계정을 찍어내는 Account Factory

  • 표준 베이스라인(랜딩 존 설정 + CloudTrail & Config 연동)이 자동 적용된 계정을 발급하는 "계정 공장"입니다
  • 여기에 우리가 만든 VPC 블루프린트를 얹으면, 계정 생성과 동시에 네트워크까지 배포됩니다

5. IAM Identity Center

랜딩 존은 사람의 로그인을 한곳에서 관리하는 IAM Identity Center도 함께 활성화합니다. IAM User 없이 SSO로 각 계정에 접속하는 구조이며, 4단계에서 상세히 다룹니다.

3-1. 랜딩 존 셋업 (30~60분 대기)

반드시 관리 계정에 관리자 권한으로 로그인한 상태에서, 리전을 서울(ap-northeast-2)로 맞추고 진행합니다.

진입 : Control Tower 콘솔의 랜딩 페이지에서 우측 Enable AWS Control Tower 버튼을 누르면 셋업 위저드가 시작됩니다. Pricing 카드처럼 Control Tower 자체는 무료이고, 켜지는 서비스(Config 등)만 과금됩니다.

image.png

Step 1 : Choose setup preferences : Setup preference는 I want to set up a full environment를 선택합니다. Governed Regions의 Home Region은 서울로 고정되어 있습니다.

image.png

Additional governed Regions를 펼쳐 US East (N. Virginia) us-east-1만 추가합니다. 리전을 더 넣을수록 Config 비용이 늘고, 2단계에서 만든 리전 제한 SCP(방식 2 : 서울 & 버지니아만 허용)와도 맞아야 하므로 이 두 개만 둡니다. Automatic account enrollment는 체크된 기본값 그대로 둡니다 (등록된 OU로 계정을 옮기면 자동 편입).

image.png

Step 2 : Create organizational units : Control Tower가 Security OU(Foundational)를 만들고 그 안에 로그/감사 공유 계정을 넣습니다. Sandbox OU도 기본 제안되는데 이번 랩에서 쓰진 않지만 만들어져도 무방합니다. "Your organization and OUs were created successfully"가 뜨면 다음으로 넘어갑니다.

image.png

Step 3 : Configure Service integrations : 여기서 Log Archive & Audit 계정을 새로 만듭니다. CloudTrail 중앙 로깅 섹션에서 Create new를 눌러 로그 계정 이메일을 입력합니다.

두 이메일은 아직 어떤 AWS 계정도 쓰지 않은 새 별칭이어야 합니다. IAM Identity Center 접근 방식은 기본값(Control Tower가 Identity Center로 접근 관리)을 그대로 둡니다.

image.png

Step 4 : Review and enable : AWS Config와 CloudTrail이 Enabled로 구성되는 것을 확인하고 Enable AWS Control Tower를 누릅니다. 이때부터 30~60분 걸리며, 브라우저를 닫아도 백그라운드로 진행됩니다.

image.png

3-2. Account Factory 준비

본격 발급에 앞서 두 가지를 준비합니다 : 발급을 실행할 사용자의 포트폴리오 접근 설정과, 계정에 얹을 VPC 블루프린트 등록입니다. 역할 하나가 더 필요하지만, 그건 3-4에서 실패를 직접 겪은 뒤에 만듭니다. 먼저 Account Factory가 무엇 위에서 동작하는지 봅니다.

Control Tower는 새 계정 발급과 기존 계정 편입(enroll)을 같은 엔진으로 처리하는데, 그 엔진이 Account Factory이고 내부적으로는 Service Catalog의 "Account Factory 포트폴리오"라는 제품으로 구현되어 있습니다.

image.png

누구로 작업하나 : root 불가, IAM 사용자 필요

Account Factory 발급/편입은 root로는 할 수 없습니다. 이유는 두 가지입니다.

  • 기술적 제약 : Service Catalog 포트폴리오의 접근 주체는 IAM 사용자 & 그룹 & 역할만 될 수 있고, root는 이 목록에 등록하는 것 자체가 불가능합니다. root로 발급하면 "포트폴리오 접근 불가" 오류가 납니다
  • 원칙 : root는 최후의 작업에만 쓰고 운영은 IAM & SSO로 한다는 보안 기본을 Control Tower가 강제하는 것입니다

그래서 IAM 신원으로 진행합니다. 랜딩 존이 관리 계정 root 이메일로 SSO 관리자 사용자를 이미 만들어 두긴 했지만(4단계에서 다룹니다), 이 랩은 1단계에서 만든 {{Project Name}}-iam으로 이어서 진행합니다.

admin인데도 포트폴리오 연결이 필요한 이유

{{Project Name}}-iam은 AdministratorAccess를 갖고 있지만, 그것만으로는 Account Factory가 안 됩니다. 두 권한의 종류가 다르기 때문입니다.

image.png

AdministratorAccess는 "건물 마스터키"에 해당합니다. 그런데 Account Factory 포트폴리오는 마스터키가 있어도 입주자 명단에 이름이 있어야 들어가는 방입니다. Service Catalog가 "누가 이 표준 제품을 셀프서비스로 쓸 수 있는지"를 IAM 권한과 별도로 통제하도록 설계했기 때문입니다. 그래서 admin이어도 포트폴리오 명단에 {{Project Name}}-iam을 올리는 연결 작업이 필요합니다. 참고로 SSO 관리자로 발급할 때는 이 연결이 필요 없습니다. Control Tower가 랜딩 존을 만들 때 관리 계정의 SSO 역할 2개를 명단에 미리 올려 두기 때문입니다.

포트폴리오에 IAM 사용자 연결하기

Service Catalog → Portfolios → AWS Control Tower Account Factory Portfolio를 클릭한 뒤 Access(액세스) 탭을 엽니다. 연결 전에는 Control Tower가 만든 SSO 역할 2개만 보입니다.

image.png

우측 Grant access(액세스 권한 부여)를 누르고, 액세스 유형은 IAM 보안 주체를 선택합니다. 아래 탭에서 사용자(User)를 고르고 {{Project Name}}-iam을 체크한 뒤 Grant access를 누릅니다.

image.png

"user에 대한 액세스가 추가됨" 성공 배너가 뜨고, Access 목록이 3개(SSO 역할 2 + {{Project Name}}-iam)로 늘어나면 완료입니다. 이제 이 사용자로 Account Factory 발급/편입이 가능합니다.

image.png

블루프린트 등록

블루프린트는 계정 발급 시 자동 배포되는 CloudFormation 템플릿입니다. 구조는 고정이고 대역만 파라미터로 받습니다.

AWSTemplateFormatVersion: "2010-09-09"
Description: >
  Baseline VPC for Account Factory blueprint.
  Two AZs, Public 2 + Private 2 subnets, Internet Gateway, no NAT.
  CIDR range is injected per account via VpcCidr parameter.

Parameters:
  VpcCidr:
    Type: String
    Description: "VPC CIDR, /16 only. qa: 10.10.0.0/16, prod: 10.20.0.0/16"
    Default: 10.10.0.0/16
    AllowedPattern: ^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/16$
    ConstraintDescription: must be an IPv4 CIDR block with /16 prefix
  EnvName:
    Type: String
    Description: Environment name used in resource Name tags
    Default: qa
    AllowedValues: [dev, qa, prod]

Resources:
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-vpc" }
        - { Key: Environment, Value: !Ref EnvName }

  Igw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-igw" }

  IgwAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref Vpc
      InternetGatewayId: !Ref Igw

  # Subnet CIDRs are carved from VpcCidr automatically:
  # index 0/1 -> public a/c, index 2/3 -> private a/c (all /24)
  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 4, 8]]
      MapPublicIpOnLaunch: true
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-public-a" }

  PublicSubnetC:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      AvailabilityZone: !Select [2, !GetAZs ""]
      CidrBlock: !Select [1, !Cidr [!Ref VpcCidr, 4, 8]]
      MapPublicIpOnLaunch: true
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-public-c" }

  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Select [2, !Cidr [!Ref VpcCidr, 4, 8]]
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-private-a" }

  PrivateSubnetC:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      AvailabilityZone: !Select [2, !GetAZs ""]
      CidrBlock: !Select [3, !Cidr [!Ref VpcCidr, 4, 8]]
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-private-c" }

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-public-rt" }

  PublicDefaultRoute:
    Type: AWS::EC2::Route
    DependsOn: IgwAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref Igw

  PublicSubnetARouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetCRouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetC
      RouteTableId: !Ref PublicRouteTable

  # No NAT by design: private subnets route locally only
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
        - { Key: Name, Value: !Sub "${EnvName}-private-rt" }

  PrivateSubnetARouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetCRouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetC
      RouteTableId: !Ref PrivateRouteTable

Outputs:
  VpcId:
    Value: !Ref Vpc
  PublicSubnetIds:
    Value: !Join [",", [!Ref PublicSubnetA, !Ref PublicSubnetC]]
  PrivateSubnetIds:
    Value: !Join [",", [!Ref PrivateSubnetA, !Ref PrivateSubnetC]]

image.png

생성되는 리소스 : VPC, Public 서브넷 2개(AZ-a/c, 첫 두 /24), Private 서브넷 2개(AZ-a/c, 다음 두 /24), IGW, 라우팅 테이블 2개. NAT는 없습니다.

블루프린트는 어디에 쓰이나

Service Catalog에 등록한 이 블루프린트는 Account Factory로 계정을 발급할 때 선택됩니다 (3-4 & 3-5). 계정이 만들어지는 순간 Control Tower가 이 CloudFormation 템플릿을 새 계정 안에서 실행해, 발급과 동시에 표준 네트워크를 배포합니다. 발급 화면의 "Account factory customization" 섹션에서 이 블루프린트를 고르고 VpcCidr & EnvName 파라미터만 계정별로 다르게 입력하면 됩니다. 즉 같은 템플릿 하나로 qa(10.10)와 prod(10.20)가 같은 구조 & 다른 대역으로 찍혀 나옵니다. 한 번 등록해 두면 이후 모든 계정 발급에 재사용됩니다.

Service Catalog → 제품 목록 → 우측 제품 생성 → 제품 생성을 누릅니다.

image.png 제품 세부 정보에 이름 blueprint를 입력하고, 제품 유형은 CloudFormation 템플릿, 템플릿은 앞서 내려받은 vpc-baseline-blueprint.yaml 파일을 업로드합니다. 생성하면 "제품을 생성함" 배너와 함께 제품 목록에 blueprint(CLOUD_FORMATION_TEMPLATE)가 추가됩니다. image.png

3-3. Workloads OU 등록

랜딩 존이 자동 생성한 Security OU는 이미 관리되지만, 1단계에서 우리가 만든 Workloads OU는 아직 Control Tower 관리 밖입니다. 등록해야 그 아래에 Account Factory로 계정을 발급할 수 있습니다.

OU를 등록하면 baseline(Config & CloudTrail & Controls)은 OU 자체와, 이후 Control Tower가 그 OU에서 발급하거나 편입하는 계정에 적용됩니다. Control Tower 도입 전에 수동으로 만들어 둔 기존 계정(dev)도 그 OU에 직접 들어 있으면 OU 등록과 함께 편입됩니다. 3-6에서 확인합니다.

Control Tower → Organization 화면을 보면 Workloads/Prod/NonProd와 dev의 baseline 상태가 모두 Not enabled인 반면 Security OU와 로그/감사 계정은 이미 Enabled인 것이 대비됩니다.

image.png

Workloads를 선택 → Actions → Register organizational unit을 누릅니다. 상위 OU를 등록해도 중첩 OU는 자동 등록되지 않습니다. Workloads 등록이 끝나면 하위 Prod와 NonProd를 각각 선택해 같은 방법으로 한 번씩 더 Register합니다. 발급 대상 OU(Prod)가 등록돼 있지 않으면 3-4 발급 폼에서 Prod를 골라도 "선택한 OU에 baseline이 활성화돼 있어야 한다"는 안내와 함께 진행이 막히므로, Organization 화면에서 세 OU가 모두 Enabled인지 확인하고 다음으로 넘어갑니다. 이미 그 안에 직접 들어 있던 기존 계정 dev는 NonProd 등록과 함께 편입됩니다. 3-6에서 확인합니다.

개별 계정 화면(Enroll account)에서 진행하면 "OU가 등록되지 않았다"는 사전 검사 경고와 Service Catalog 접근 오류가 날 수 있습니다. 계정 단위가 아니라 OU 단위로 Register하는 것이 정답입니다.

등록을 시작하면 "Registering organizational unit: Workloads ... in progress" 배너가 뜨고, OU 안의 계정 수에 따라 몇 분~십여 분 걸립니다. Control status가 In progress로 바뀌며 완료되면 알림이 옵니다.

image.png

발급 전 알아둘 제약 : 블루프린트는 계정당 1개만 연결할 수 있습니다. 발급은 병렬이 안 되므로 qa 발급이 완전히 끝난 뒤 prod를 시작합니다. 발급 중에 다른 발급이나 enroll을 시도하면 "another operation in progress" 오류가 납니다.

3-4. qa 발급 (20~30분 대기)

Control Tower → Account factory 화면 우측 상단의 Create account(계정 생성)를 누릅니다. 이 화면의 "네트워크 구성" 카드는 Control Tower가 발급하는 모든 계정에 기본으로 얹는 VPC(172.31.0.0/16) 설정입니다. 이 랩은 그대로 두고 진행합니다. 그러면 블루프린트 VPC와 별개로 이 기본 VPC가 거버넌스 리전마다 하나씩 더 생깁니다. 원치 않으면 발급 전에 카드를 편집해 VPC 생성 리전을 모두 해제합니다.

image.png

계정 정보 입력 : 발급 폼 위쪽부터 차례로 입력합니다.

image.png

Account factory customization : 허브 계정 Validate와 첫 실패

폼 아래쪽 Account factory customization (optional) 섹션을 펼칩니다. 첫 칸이 "Account that contains your AWS Service Catalog products"입니다. 블루프린트(Service Catalog 제품)가 들어 있는 계정, 즉 허브 계정의 번호를 넣으라는 뜻입니다. 우리는 관리 계정에 블루프린트를 만들었으니 273349345035를 넣고 Validate account를 누릅니다.

그러면 빨간 오류가 뜹니다.

image.png

User: arn:aws:iam::273349345035:user/{{Project Name}}-iam is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::273349345035:role/AWSControlTowerBlueprintAccess

admin인데 왜 실패했을까

{{Project Name}}-iam은 AdministratorAccess를 가진 관리자입니다. 3-2에서 포트폴리오 명단에도 올렸습니다. 그런데도 실패한 이유는 오류 문구 안에 있습니다. {{Project Name}}-iam이 무언가를 직접 하려다 막힌 것이 아니라, AWSControlTowerBlueprintAccess라는 역할을 맡으려다(AssumeRole) 막힌 것입니다.

역할(Role)은 사용자처럼 권한을 갖는 IAM 신원이지만 비밀번호가 없습니다. 대신 다른 사용자나 서비스가 그 역할을 잠시 맡아서(AssumeRole) 쓰는 용도입니다.

Account factory customization은 아래처럼 동작합니다.

image.png

  • Validate account를 누르면 콘솔은 발급자({{Project Name}}-iam) 자격으로 허브 계정의 AWSControlTowerBlueprintAccess 역할을 AssumeRole해서 "블루프린트가 있나"를 확인합니다
  • 발급이 시작되면 Control Tower 서비스 역할(AWSControlTowerAdmin)이 같은 역할을 AssumeRole해서 블루프린트를 읽고 새 계정 안에 배포합니다
  • 역할 이름은 AWSControlTowerBlueprintAccess로 고정입니다. 허브 계정이 관리 계정 자신이어도 반드시 있어야 합니다

여기서 핵심은 AssumeRole이 한쪽 권한만으로는 성립하지 않는다는 점입니다. 양쪽이 모두 허락해야 합니다.

image.png

3-2의 비유로 돌아가면 AdministratorAccess는 건물 마스터키였고 포트폴리오는 입주자 명단이 있어야 들어가는 방이었습니다. AWSControlTowerBlueprintAccess 역할은 또 다른 방입니다. 이 방의 입주자 명단이 신뢰 정책입니다. 그런데 지금은 이 방 자체가 아직 지어지지 않은 상태입니다. 마스터키가 있어도 방이 없으면 들어갈 수 없습니다. 그래서 해결책은 {{Project Name}}-iam에 권한을 더 주는 것이 아닙니다. 역할 하나를 새로 만들고 그 역할의 신뢰 정책에 {{Project Name}}-iam과 Control Tower를 올리는 것입니다. IAM 사용자 화면은 열 필요가 없습니다.

해결 : AWSControlTowerBlueprintAccess 역할 만들기

바로가기 : IAM : Roles (관리 계정, {{Project Name}}-iam 그대로. IAM은 글로벌이라 리전 무관)

IAM → Roles → 우측 상단 Create role을 누릅니다. 목록에 Control Tower가 만들어 둔 AWSControlTowerAdmin 등의 역할이 보입니다. 우리가 신뢰 정책에 적을 바로 그 역할입니다.

image.png

Step 1 : Select trusted entity에서 Custom trust policy를 고릅니다. AWS service나 AWS account가 아닙니다. 아래 편집기에는 기본 JSON이 들어 있습니다. Principal이 {}로 비어 있으므로 전부 지우고 아래 JSON으로 바꾼 뒤 Next를 누릅니다.

image.png

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": [
      "arn:aws:iam::273349345035:role/service-role/AWSControlTowerAdmin",
      "arn:aws:iam::273349345035:user/{{Project Name}}-iam"
    ]},
    "Action": "sts:AssumeRole"
  }]
}

Step 2 : Add permissions에서 AWSServiceCatalogAdminFullAccess를 검색해 그 항목 하나만 체크합니다. Step 3 : Name, review, and create에서 Role name에 정확히 AWSControlTowerBlueprintAccess를 입력합니다. 대소문자까지 그대로여야 합니다. 검토 화면에서 신뢰 주체 2개와 권한 정책 1개를 확인하고 Create role을 누릅니다.

Validate 재시도와 블루프린트 연결

발급 화면으로 돌아가 새로고침합니다. 이전 오류 상태가 폼에 남아 있기 때문입니다. 계정 정보를 다시 입력하고 허브 계정 273349345035를 넣은 뒤 Validate account를 누르면 이번에는 통과됩니다. 그 아래 제품 드롭다운이 열리면서 blueprint가 보입니다. "AWS Control Tower Account Factory"라는 제품도 같이 보이지만 Control Tower 자체의 제품이므로 고르지 않습니다.

image.png Product version은 3-2에서 버전 이름을 따로 붙이지 않았기 때문에 pa-로 시작하는 ID로 보입니다. 하나뿐이니 그대로 둡니다. Blueprint parameters에서 EnvName은 드롭다운에서 qa, VpcCidr은 10.10.0.0/16을 넣습니다. Blueprint policy는 기본값(AdministratorAccess)으로 두고, Deployment Regions는 Home Region(서울)을 고릅니다. image.png

맨 아래 Create account를 누릅니다. Control Tower → Organization 화면에 {{Project Name}}-qa가 Prod OU 아래 새 계정 ID와 함께 나타나고, baseline status가 In progress로 보입니다. 20~30분 뒤 Enabled로 바뀌면 발급 완료입니다. root 이메일로 인증 메일이 오지만 발급 진행에는 필요하지 않습니다.

image.png

3-5. prod 발급 (20~30분 대기)

qa가 Enrolled로 바뀐 것을 확인한 뒤 같은 폼으로 prod를 발급합니다. 달라지는 값은 이메일, 표시 이름, 파라미터 두 개뿐이고 OU는 qa와 같은 Workloads/Prod입니다.

3-6. 기존 계정(dev) 편입 확인

qa & prod는 Account Factory로 발급되면서 baseline이 자동 적용됩니다. 1단계에서 미리 만든 dev는 도입 전부터 있던 기존 계정이라 사정이 다릅니다. OU를 등록하면 그 OU에 직접 들어 있는 기존 계정도 함께 편입됩니다. 그래서 3-3에서 NonProd를 등록할 때 dev도 같이 편입됐습니다. NonProd 등록이 Prod보다 오래 걸린 이유가 이것입니다.

Control Tower → Organization에서 NonProd를 펼쳐 dev의 baseline 상태가 Enabled인지 확인합니다. Identity Center에는 dev 이메일로 "Admin User"라는 SSO 사용자도 이때 함께 만들어져 있습니다.

Not enabled로 남아 있으면 : OU 등록 때 계정 편입이 실패한 경우입니다. dev 계정 선택 → Actions → Enroll account로 따로 편입합니다. 사전 검사에서 AWSControlTowerExecution 역할이 없다는 오류가 나면 dev 계정에 역할 전환으로 들어가 그 이름의 IAM 역할을 만들고 다시 시도합니다. 신뢰 주체는 관리 계정 ID(273349345035), 권한은 AdministratorAccess입니다.

발급 vs 편입 : 같은 Account Factory를 쓰지만 : 발급은 새 계정을 만들며 baseline을 적용하고, 편입(enroll)은 이미 있는 계정을 baseline 관리 아래로 넣습니다. dev는 후자입니다. OU 등록이 편입까지 대신해 준 경우입니다.

3-7. 최종 정리 : 무엇이 무엇과 어떻게 연결되는가

3단계에서 만든 것은 결국 아래 화면 한 장에 다 들어 있습니다. 여기서는 설정 절차가 아니라 이 화면에 보이는 것들이 서로 무슨 관계이고 어떻게 돌아가는지를 정리합니다.

image.png

image.png

1. 큰 그림 : Organizations 위에 얹힌 Control Tower

Organizations는 뼈대입니다. 계정을 OU 트리에 배치하고, OU에 SCP를 붙이고, 비용을 관리 계정으로 합산합니다. 1단계와 2단계는 이 뼈대만으로 진행했습니다. Control Tower는 그 뼈대 위에서 동작하는 자동화 레이어입니다. 직접 하면 계정마다 반복해야 할 일을 네 가지 도구로 대신합니다.

image.png

랜딩 존은 이 네 도구가 동작하는 데 필요한 것을 한꺼번에 깔아 둔 초기 세팅입니다. Security OU와 공유 계정 2개, 조직 트레일, Config 중앙 버킷, Identity Center 활성화가 그 내용입니다.

2. OU & SCP & Controls : 위치가 규칙의 범위, Controls가 규칙의 상위 개념

OU는 계정을 담는 폴더입니다. SCP는 OU에 붙는 "할 수 없는 것" 목록입니다. 계정이 트리의 어디에 있느냐가 어떤 SCP를 받느냐를 정합니다. 2단계에서 Workloads에 붙인 리전 제한 SCP는 Prod와 NonProd를 거쳐 qa, prod, dev까지 내려갑니다.

  • SCP는 계정 안에 설치되는 것이 아니라 OU에 붙어 있고 API 호출마다 평가됩니다. 그래서 붙이고 떼는 즉시 반영됩니다
  • Control Tower의 Control(가드레일)은 SCP보다 상위 개념입니다. "이 규칙을 지켜라"가 Control이고, 그 규칙을 실제로 집행하는 수단이 아래 셋입니다. SCP는 그중 하나입니다

image.png

그래서 등록된 Workloads OU를 Organizations에서 보면 2단계에서 직접 만든 deny-outside-allowed-regions와 Control Tower가 붙인 aws-guardrails-xxxx가 나란히 걸려 있습니다. 하나는 우리가 손으로 쓴 SCP이고 다른 하나는 Preventive control이 SCP로 내려온 것입니다. 대체가 아니라 둘 다 함께 평가됩니다.

3. Control Tower에 OU 편입

Control Tower에서 OU를 Register하는 것은 API로 보면 AWSControlTowerBaseline을 그 OU 하나를 대상으로 켜는 작업입니다. 대상이 OU 하나라서 중첩 OU로 퍼지지 않습니다. 그래서 Workloads, Prod, NonProd를 각각 등록했습니다. 등록하면 아래가 순서대로 일어납니다. 이 절에서 감시 장비라고 부르는 것은 세 번째 항목의 스택 4종입니다.

  • OU에 필수 Control 활성화 : Workloads에 9개가 걸렸습니다. Config 변경 금지, IAM 역할 변경 금지처럼 Control Tower 자신을 보호하는 규칙이며 실체는 SCP입니다
  • OU에 직접 들어 있는 기존 계정 편입 : NonProd 등록 때 dev가 함께 편입됐습니다. 등록이 9분 걸린 이유입니다
  • 편입되는 계정마다 스택 4종 배포 : 관리 계정의 StackSet이 각 계정의 AWSControlTowerExecution 역할을 통해 IAM 역할, Config 레코더와 전달 채널, CloudWatch 이벤트 규칙과 SNS, 서비스 연결 역할을 만듭니다
  • 계정 접속용 SSO 사용자 생성 : dev 이메일로 만들어진 "Admin User"가 이것입니다

편입되지 않은 계정도 OU 안에 있으면 SCP는 받습니다. 규칙은 걸리지만 감시 장비가 없는 상태입니다. 2단계 시점의 dev가 그 상태였습니다. Workloads의 리전 제한 SCP는 받았지만 Config 레코더는 없었습니다. Sandbox OU는 그보다 앞 단계입니다. 미등록이라 Control status가 "-"이고 그 안에 계정을 넣어도 Config 레코더는 깔리지 않습니다.

4. Account Factory : 발급은 생성 & 편입 & 커스터마이즈의 묶음

Account Factory는 "계정을 만든다"가 아니라 "계정을 만들고, 등록된 OU에 넣고, 편입하고, 네트워크까지 얹는다"를 한 번에 하는 도구입니다. qa 발급 때 실제로 일어난 순서입니다. 전체가 20~30분 걸립니다.

그래서 qa에는 VPC가 두 개 생깁니다. 블루프린트가 만든 10.10.0.0/16은 우리가 설계한 것입니다. 172.31.0.0/16은 Control Tower가 기본으로 얹는 것입니다. 후자는 프라이빗 서브넷만 있는 구성이라 NAT가 없고 비용이 거의 들지 않습니다. 원치 않으면 발급 전에 "네트워크 구성" 카드에서 VPC 생성 리전을 모두 해제합니다.

5. CloudTrail & Config : 행위 기록과 상태 기록

image.png

image.png

image.png Organization 화면의 "AWS Config baseline status"가 Not enabled인 것과 Config가 깔린 것은 모순이 아닙니다. 위 표에서 본 대로 그 열은 축소판 baseline을 썼는지 묻는 열입니다. 한 가지 더, Config 기록이 Audit 계정 버킷으로 가는 것은 랜딩 존 4.0의 구성입니다. 구버전 랜딩 존을 설명하는 자료에는 Log Archive로 간다고 돼 있으니 버전을 확인하고 읽어야 합니다.

6. Log Archive & Audit : 쓰는 곳과 읽는 곳

둘 다 랜딩 존이 만드는 공유 계정이고 Security OU에 있습니다. 하나로 합칠 수도 있는 것을 굳이 둘로 나눈 이유는 증거의 불변성과 최소 권한입니다.

  • Log Archive : 로그가 쌓이기만 하는 곳입니다. CloudTrail 버킷과 그 버킷의 접근 로그 버킷이 있고, 사람이 로그인할 일이 없습니다. 감사 증거를 지우거나 고칠 수 있는 사람이 없어야 증거로서 가치가 있습니다
  • Audit : 읽고 판단하는 곳입니다. Config 버킷과 애그리게이터가 여기 있어 조직 전체 리소스 상태를 한 화면에서 봅니다. 보안 알림 SNS 토픽, 각 멤버 계정을 들여다보는 읽기 역할도 여기 있습니다. GuardDuty나 Security Hub 같은 보안 서비스의 조직 위임 관리자로 지정하는 자리이기도 합니다
  • 관리 계정에 두지 않는 이유 : 관리 계정은 SCP를 받지 않는 예외 계정이라 보안 관점에서 가장 위험한 곳입니다. 로그와 감사 도구를 거기 두면 관리 계정이 침해됐을 때 증거도 같이 사라집니다
  • App이나 서버 로그와 무관 : 두 계정은 조직 거버넌스용 로그만 다룹니다. 애플리케이션 로그는 각 워크로드 계정에서 따로 설계합니다

다음 4단계는 이 구조에 사람을 붙이는 단계입니다. Control Tower가 Identity Center를 이미 켜 두었고, qa 발급 때 입력한 이메일로 SSO 사용자도 만들어져 있습니다. 4단계에서 만드는 개발자 Lead 사용자로 qa에 들어가 블루프린트 VPC를 직접 확인합니다.


4단계. Identity Center + Permission Set

3단계까지는 계정과 규칙을 만들었습니다. 4단계는 그 구조에 사람을 붙이는 단계입니다. Control Tower가 켜 둔 IAM Identity Center에 사용자 3명 & 그룹 3개 & Permission Set 2개를 만듭니다. 계정마다 다르게 할당한 뒤 포털과 CLI로 로그인을 확인합니다. 이 랩은 3-5 prod 발급을 건너뛴 상태를 기준으로 씁니다.

4-1. 개념 : 사용자 & 그룹 & Permission Set & 계정

1단계에서 만든 {{Project Name}}-iam은 관리 계정 안에 있는 IAM 사용자입니다. 다른 계정에 가려면 역할 전환이 필요했습니다. 계정이 늘수록 전환할 역할도 늘어납니다. Identity Center의 사용자는 계정 밖에 있습니다. 한 번 로그인하면 자기에게 허용된 계정 목록이 타일로 뜨고, 타일을 누르면 그 계정 안의 역할로 들어갑니다. 이 구조를 만드는 구성 요소가 넷입니다.

image.png

image.png

  • 할당의 구성 : 누가(그룹 또는 사용자) + 어느 계정에 + 어떤 Permission Set으로. 같은 그룹이 계정마다 다른 Permission Set을 받을 수 있습니다. 같은 Permission Set을 여러 계정에 재사용할 수도 있습니다
  • Permission Set 단독으로는 아무것도 생기지 않음 : 계정에 할당되는 순간 Identity Center가 그 계정 안에 AWSReservedSSO_<이름>_<해시> 역할을 만듭니다. 할당을 지우면 역할도 사라집니다
  • 할당 대상은 그룹 : 사람이 바뀌면 그룹 소속만 바꿉니다. 계정이 늘면 그룹 할당 한 줄만 추가합니다. 사용자에게 직접 할당하는 것은 예외 처리로만 씁니다
  • 세션 시간은 Permission Set에 붙음 : 기본값은 1시간입니다. 이 랩에서는 관리자용을 4시간으로 바꿉니다

Control Tower가 미리 만들어 둔 것 : 랜딩 존 셋업과 계정 발급 때 자동으로 생긴 것들입니다. 그대로 두고 우리 것은 새로 만듭니다.

image.png

이 랩의 설계 : 아래 표 하나로 정리됩니다. 개발자는 dev에만 들어갑니다. 개발자 Lead는 워크로드 계정 둘에 관리자로 들어가고 Security OU의 Log Archive & Audit은 읽기만 합니다. DevOps는 관리 계정까지 전부 관리자로 들어갑니다.

image.png

포털에 로그인하면 developer는 타일 1개, dev-lead는 타일 4개, devops는 타일 5개를 보게 됩니다. prod를 발급했다면 qa와 같은 값으로 열을 하나 더 추가합니다.

4-2. Identity Center 둘러보기 & 포털 URL 정하기

Control Tower가 Identity Center를 서울 리전에 켜 두었으므로 다른 리전에서 열면 인스턴스가 보이지 않습니다. 대시보드에서 볼 것은 세 가지입니다.

  • Identity source : Identity Center directory(내장 디렉터리)
  • Primary Region : 서울
  • Organization ID : 1단계에서 확인한 값과 같음

"Confirm identity source" 버튼은 초기 설정 안내일 뿐이라 누를 필요가 없습니다.

image.png

오른쪽 아래 AWS access portal URLs 항목이 사용자 로그인 주소입니다. 기본값은 d-xxxxxxxxxx.awsapps.com/start처럼 디렉터리 ID가 들어간 형태라 외우기 어렵습니다. Settings → Identity source 탭 → Actions → Customize AWS access portal URL에서 앞부분을 바꿉니다.

image.png

한 번만 바꿀 수 있음 : 포털 URL의 앞부분은 한 번 바꾸면 다시 바꿀 수 없습니다. AWS 전체에서 유일해야 합니다. 다른 조직이 이미 쓰는 이름이면 거부됩니다. 이 랩은 {{Project Name}}으로 바꿔 https://{{Project Name}}.awsapps.com/start를 씁니다.

image.png

Multi-account permissions → AWS accounts를 열면 Organizations 트리가 그대로 보입니다. 계정마다 어떤 Permission Set이 할당돼 있는지는 오른쪽에 나옵니다. 관리 계정에는 Control Tower가 할당한 것 5개가 이미 있습니다. 4-6에서 할당을 추가할 화면이 여기입니다.

image.png

Permission sets 메뉴에는 Control Tower가 만든 6개가 Provisioned 상태로 있습니다. 4-5에서 여기에 2개를 추가합니다.

image.png

4-3. 사용자 3명 만들기

Users → Add user를 세 번 반복합니다. 이메일은 Identity Center 안에서 유일해야 하므로 Control Tower가 이미 쓴 이메일(4-1 표의 사용자 3명)은 쓸 수 없습니다.

image.png

Next를 누르면 그룹에 넣는 화면이 나옵니다. 아직 그룹이 없으므로 건너뛰고 Add user까지 갑니다. 만들자마자 초대 메일이 발송됩니다. 메일은 4-7에서 처리하고 지금은 계속 진행합니다.

4-4. 그룹 3개 만들고 사용자 넣기

Groups → Create group에서 Group name에 Developers를 넣고, 아래 "Add users to group"에서 developer를 체크한 뒤 Create group을 누릅니다. 같은 방법으로 DevLeads에 dev-lead를, DevOps에 devops를 넣어 만듭니다. Control Tower가 만든 그룹 8개가 같은 목록에 보이지만 손대지 않습니다.

4-5. Permission Set 2개 만들기

Permission sets → Create permission set을 누릅니다. Permission set type은 Predefined permission set을 고르고, 아래 목록에서 AdministratorAccess를 선택합니다. AWS 관리형 정책을 그대로 쓰는 방식이라 JSON을 쓸 일이 없습니다.

다음 화면에서 이름은 기본값 AdministratorAccess를 그대로 둡니다. Control Tower 것은 AWSAdministratorAccess라 이름이 겹치지 않습니다. Session duration을 기본 1 hour에서 4 hours로 바꿉니다. 이 값은 이 Permission Set으로 들어간 세션의 유지 시간입니다. Permission Set마다 다르게 둘 수 있습니다. Next → Create로 마칩니다.

같은 방법으로 ReadOnlyAccess를 하나 더 만듭니다. 이번에는 세션 시간을 기본 1 hour로 둡니다.

할당 전에는 역할 없음 : 방금 만든 Permission Set 2개는 아직 어느 계정에도 역할을 만들지 않았습니다. 그래서 목록의 Provisioning status가 Control Tower 것들과 달리 Provisioned로 표시되지 않습니다. 4-6에서 계정에 할당하는 순간 각 계정 안에 역할이 생기고 Provisioned로 바뀝니다.

4-6. 계정에 할당하기

AWS accounts 화면에서 계정을 체크하고 우측 상단 Assign users or groups를 누르면 위저드가 시작됩니다. 순서는 Groups 탭에서 그룹 선택 → Permission Set 선택 → Review → Submit입니다. 한 번에 계정 여러 개와 그룹 여러 개를 고를 수 있지만, 고른 그룹 전부에 고른 Permission Set 전부가 붙는 방식이라 권한이 다른 조합은 따로 돌려야 합니다. 4-1의 설계 표대로 다섯 번 반복합니다.

image.png Submit을 누르면 "Configuring your AWS account" 진행 표시가 잠깐 뜹니다. 이때 Identity Center가 대상 계정 안에 AWSReservedSSO_<Permission Set 이름>_xxxx 역할을 만드는 중입니다. 끝나면 AWS accounts 화면의 해당 계정 오른쪽에 Permission Set 이름이 추가됩니다. Permission sets 목록에서는 두 개의 상태가 Provisioned로 바뀝니다.

4-7. 포털 로그인 확인

4-3에서 발송된 초대 메일을 엽니다. 세 사용자의 메일이 같은 Gmail 받은편지함에 옵니다. Accept invitation을 누르면 비밀번호 설정 화면이 나옵니다. 이어서 MFA 등록 화면이 나옵니다. Identity Center 기본 설정이 첫 로그인 때 MFA 등록을 요구하므로 인증 앱이나 패스키를 등록하고 넘어갑니다.

설정이 끝나면 https://{{Project Name}}.awsapps.com/start로 이동합니다. 로그인한 사용자에 따라 보이는 것이 다릅니다.

image.png 타일에서 역할 이름을 누르면 그 계정 콘솔이 새 탭으로 열립니다. 우측 상단 계정 표시가 AWSReservedSSO_AdministratorAccess_xxxx/dev-lead 형태로 보입니다. Permission Set이 역할로 실체화된 것을 여기서 확인할 수 있습니다.

dev-lead로 qa에 들어가 3단계에서 미뤄 둔 검증을 여기서 합니다. VPC 콘솔(서울)에 블루프린트가 만든 10.10.0.0/16과 Control Tower 기본 VPC 172.31.0.0/16 두 개가 보여야 합니다. 서브넷 목록에는 qa-public-a, qa-public-c, qa-private-a, qa-private-c가 있어야 합니다. 리전을 버지니아로 바꾸면 172.31만 있고 10.10은 없어야 정상입니다. 도쿄로 바꾸면 2단계 SCP가 상속돼 EC2 대시보드가 액세스 거부로 채워집니다.

같은 포털에서 Log Archive 타일의 ReadOnlyAccess로 들어가 S3 콘솔에서 Create bucket을 눌러 봅니다. 액세스 거부가 뜨면 정상입니다. 같은 dev-lead라도 계정에 따라 권한이 다른 것을 확인하는 절차입니다. devops로 다시 들어가면 같은 화면에서 생성이 허용됩니다.

4-8. CLI 연동

포털 로그인이 되면 CLI도 같은 신원으로 쓸 수 있습니다. IAM 사용자의 액세스 키를 발급받을 필요가 없습니다.

aws configure sso
# SSO session name : {{Project Name}}
# SSO start URL    : https://{{Project Name}}.awsapps.com/start
# SSO region       : ap-northeast-2
# 브라우저가 열리면 dev-lead로 인증 → 계정 목록에서 qa 선택 → AdministratorAccess 선택
# CLI default client Region : ap-northeast-2
# Profile name     : lead-qa

aws sso login --profile lead-qa
aws sts get-caller-identity --profile lead-qa
aws ec2 describe-vpcs --profile lead-qa --query 'Vpcs[].CidrBlock'

get-caller-identity 결과의 Arn에 assumed-role/AWSReservedSSO_AdministratorAccess_xxxx/dev-lead가 보입니다. 마지막 명령은 4-7에서 콘솔로 본 VPC 2개를 CLI로 다시 확인하는 것입니다.


정리(해체)와 유지 전략

랩이 끝나면 계정은 총 6개가 됩니다 : 관리, dev, qa, prod, Log Archive, Audit.

image.png

해체할 때 알아야 할 것

  • Control Tower 해제는 콘솔의 "랜딩 존 해제(decommission)" 기능을 써야 하며 꽤 번거롭습니다. 수동으로 리소스를 지우면 drift만 발생합니다.
  • 멤버 계정 폐쇄는 계정별 "계정 폐쇄" → 90일 유예 후 완전 삭제. 30일 내 폐쇄 가능한 계정 수 제한도 있습니다.
  • 계정을 지워도 루트 이메일은 90일간 재사용 불가이니, 재실습 땐 새 별칭(+awsdev2)을 쓰면 됩니다.
  • 조직 자체를 없애려면 : 멤버 계정 전부 제거/폐쇄 → 관리 계정에서 "조직 삭제".