CloudFormation 템플릿으로 SAML 페더레이션 역할 생성하기

CloudFormation 템플릿으로 SAML 페더레이션 역할 생성하기 (튜토리얼)

AWS 계정에 기존 SAML 자격 증명 공급자(IdP)가 구성되어 있다면 해당 IdP를 신뢰하는 페더레이션 IAM 역할을 만들 수 있어요. 이 튜토리얼은 CloudFormation 템플릿을 사용해 외부 IdP를 통해 인증된 사용자가 맡을 수 있는 SAML 페더레이션 IAM 역할을 만드는 방법을 보여줘요.

출처: 문서

본문

템플릿은 SAML IdP가 역할을 맡을 수 있게 허용하는 신뢰 정책이 있는 페더레이션 IAM 역할을 만들어요. 외부 IdP로 인증된 사용자는 역할의 권한에 따라 AWS 리소스에 접근하기 위해 이 역할을 맡을 수 있어요.

배포된 리소스는 다음으로 이루어져 있어요.

  • 기존 SAML IdP를 신뢰하는 페더레이션 IAM 역할
  • 역할에 연결해 특정 권한을 부여할 수 있는 구성 가능한 관리형 정책
  • 선택적 권한 경계(permissions boundary)와 세션 기간 설정

사전 요구 사항 (Prerequisites)

이 튜토리얼은 다음이 이미 준비되어 있다고 가정해요.

  • AWS 계정에 구성된 기존 SAML IdP. 없다면 IAM 튜토리얼 "CloudFormation 템플릿으로 SAML 자격 증명 공급자(IdP) 생성"을 사용해 만들 수 있어요.
  • 스택을 만들 때 매개변수로 지정해야 하는 SAML IdP의 ARN
  • 이 튜토리얼에서 IdP의 SAML 메타데이터 XML 파일을 포맷하는 데 사용하는 Python 명령을 실행할 로컬 머신에 설치된 Python 3.6 이상

CloudFormation으로 SAML 페더레이션 역할 생성 (Create a SAML federated role using CloudFormation)

SAML 페더레이션 역할을 만들려면 CloudFormation 템플릿을 만들고 이를 사용해 역할을 포함하는 스택을 만들어요.

템플릿 생성 (Create the template)

먼저 CloudFormation 템플릿을 만들어요.

  1. Template 섹션에서 JSON 또는 YAML 탭의 복사 아이콘을 클릭해 템플릿 내용을 복사해요.
  2. 템플릿 내용을 새 파일에 붙여넣어요.
  3. 파일을 로컬에 저장해요.

스택 생성 (Create the stack)

다음으로 저장한 템플릿을 사용해 CloudFormation 스택을 프로비저닝해요.

  1. https://console.aws.amazon.com/cloudformation 에서 CloudFormation 콘솔을 열어요.
  2. Stacks 페이지에서 Create stack 메뉴에서 **with new resources (standard)**를 선택해요.
  3. 템플릿을 지정해요:
    • Prerequisite 아래에서 Choose an existing template을 선택해요.
    • Specify template 아래에서 Upload a template file을 선택해요.
    • Choose file을 선택하고 템플릿 파일로 이동한 뒤 선택해요.
    • Next를 선택해요.
  4. 다음 스택 세부 정보를 지정해요:
    • 스택 이름을 입력해요.
    • SAMLProviderARN에 기존 SAML IdP의 ARN을 입력해요. 형식은 arn:aws:iam::123456789012:saml-provider/YourProviderName이어야 해요. 예: arn:aws:iam::123456789012:saml-provider/CompanyIdP

      참고 – IAM 튜토리얼 "CloudFormation 템플릿으로 SAML 자격 증명 공급자(IdP) 생성"으로 SAML IdP를 만들었다면 해당 CloudFormation 스택의 Outputs 탭에서 공급자 ARN을 찾을 수 있어요.

    • RoleName은 비워 두면 스택 이름을 기반으로 이름이 자동 생성되거나, IAM 역할에 사용자 지정 이름을 입력할 수 있어요. 예: SAML-Developer-Access 또는 SAML-ReadOnly-Role
    • 다른 매개변수는 요구 사항에 따라 기본값을 수락하거나 직접 입력해요:
      • RoleSessionDuration – 최대 세션 기간(초) (3600-43200, 기본값 7200) 예: 14400 (4시간)
      • RolePermissionsBoundary – 권한 경계 정책의 선택적 ARN 예: arn:aws:iam::123456789012:policy/DeveloperBoundary
      • RolePath – IAM 역할의 경로 (기본값은 /) 예: /saml-roles/
      • ManagedPolicy1-5 – 연결할 최대 5개 관리형 정책의 선택적 ARN. ManagedPolicy1의 예: arn:aws:iam::aws:policy/ReadOnlyAccess. ManagedPolicy2의 예: arn:aws:iam::123456789012:policy/CustomPolicy
    • Next를 선택해요.
  5. 스택 옵션을 구성해요:
    • Stack failure options 아래에서 Delete all newly created resources를 선택해요.

      참고 – 이 옵션을 선택하면 스택 생성이 실패해도 삭제 정책이 리소스를 보관하도록 지정된 리소스에 대해 요금이 부과되는 것을 방지할 수 있어요.

    • 다른 모든 기본값을 수락해요.
    • Capabilities 아래에서 CloudFormation이 계정에 IAM 리소스를 만들 수 있음을 인정하는 확인란을 선택해요.
    • Next를 선택해요.
  6. 스택 세부 정보를 검토하고 Submit을 선택해요.
  7. CloudFormation이 스택을 만들어요. 스택 생성이 완료되면 스택 리소스를 사용할 준비가 돼요. 스택 상세 페이지의 Resources 탭을 사용해 계정에 프로비저닝된 리소스를 볼 수 있어요.

스택은 다음 값을 출력하며 Outputs 탭에서 볼 수 있어요.

  • RoleARN: 생성된 IAM 역할의 ARN(예: arn:aws:iam::123456789012:role/SAML-Developer-Access, 자동 생성 이름을 사용하면 arn:aws:iam::123456789012:role/stack-name-a1b2c3d4).

이 역할 ARN은 IdP가 역할 전환용 적절한 SAML 속성을 보내도록 구성할 때 필요해요.

SAML 페더레이션 역할 테스트 (Test the SAML federated role)

SAML 페더레이션 역할을 만든 후에는 구성을 확인하고 페더레이션 설정을 테스트할 수 있어요.

  1. https://console.aws.amazon.com/iam/ 에서 IAM 콘솔을 열어요.
  2. 탐색 창에서 Roles를 선택해요.
  3. 새로 만든 페더레이션 역할을 찾아 선택해요. 사용자 지정 역할 이름을 제공했다면 그 이름을 찾아요. RoleName 매개변수를 비워 두었다면 역할에는 스택 이름과 고유 식별자를 기반으로 한 자동 생성 이름이 있어요.
  4. Trust relationships 탭을 선택해 신뢰 정책을 검토해요.
  5. 신뢰 정책은 SAML IdP가 신뢰받아 이 역할을 맡을 수 있고, SAML 대상(SAML:aud)이 https://signin.aws.amazon.com/saml과 일치한다는 조건이 있는지 보여줘야 해요.
  6. Permissions 탭을 선택해 연결된 정책을 검토해요. 생성 중 역할에 연결된 모든 관리형 정책을 볼 수 있어요.
  7. 역할 요약 페이지에 표시된 Role ARN을 기록해요. 외부 IdP가 사용자가 이 역할을 맡을 수 있게 구성하려면 이 ARN이 필요해요.

이제 SAML 페더레이션 역할을 사용할 준비가 됐어요. 외부 IdP가 SAML 어서션에 이 역할의 ARN을 포함하도록 구성하면 인증된 사용자가 이 역할을 맡아 AWS 리소스에 접근할 수 있게 돼요.

정리: 리소스 삭제 (Clean up: delete resources)

마지막 단계로 스택과 그 안에 포함된 리소스를 삭제해요.

  1. CloudFormation 콘솔을 열어요.
  2. Stacks 페이지에서 템플릿으로 만든 스택을 선택하고 Delete를 선택한 뒤 Delete를 확인해요.
  3. CloudFormation이 스택과 여기에 포함된 모든 리소스의 삭제를 시작해요.

CloudFormation 템플릿 세부 정보 (CloudFormation template details)

리소스 (Resources)

이 튜토리얼의 CloudFormation 템플릿은 계정에 다음 리소스를 만들어요.

  • AWS::IAM::Role: SAML IdP를 통해 인증된 사용자가 맡을 수 있는 페더레이션 IAM 역할

구성 (Configuration)

템플릿은 다음 구성 가능한 매개변수를 포함해요.

  • RoleName – IAM 역할의 이름 (자동 생성 이름을 위해 비워 두기)
  • SAMLProviderARN – SAML IdP의 ARN (필수)
  • RoleSessionDuration – 최대 세션 기간(초) (3600-43200, 기본값 7200)
  • RolePermissionsBoundary – 권한 경계 정책의 선택적 ARN
  • RolePath – IAM 역할의 경로 (기본값 /)
  • ManagedPolicy1-5 – 연결할 최대 5개 관리형 정책의 선택적 ARN

CloudFormation 템플릿 (CloudFormation template)

이 튜토리얼의 CloudFormation 템플릿으로 사용할 다음 JSON 또는 YAML 코드를 별도 파일로 저장해요.

JSON

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "[AWSDocs] IAM: tutorial_saml-federated-role",
  "Parameters": {
    "RoleName": {
      "Type": "String",
      "Description": "Name of the IAM Role (leave empty for auto-generated name like '{StackName}-{UniqueId}')",
      "Default": "",
      "AllowedPattern": "^$|^[\\w+=,.@-]{1,64}$",
      "ConstraintDescription": "Must be empty or 1-64 characters and can contain alphanumeric characters and +=,.@-"
    },
    "SAMLProviderARN": {
      "Type": "String",
      "Description": "ARN of the SAML Identity Provider",
      "AllowedPattern": "^arn:aws:iam::\\d{12}:saml-provider/[a-zA-Z0-9._-]+$",
      "ConstraintDescription": "Must be a valid SAML provider ARN"
    },
    "RoleSessionDuration": {
      "Type": "Number",
      "Description": "The maximum session duration (in seconds) that you want to set for the specified role (3600-43200)",
      "MinValue": 3600,
      "MaxValue": 43200,
      "Default": 7200
    },
    "RolePermissionsBoundary": {
      "Type": "String",
      "Description": "Optional ARN of the permissions boundary policy (leave empty for none)",
      "Default": ""
    },
    "RolePath": {
      "Type": "String",
      "Description": "Path for the IAM role (must start and end with /)",
      "Default": "/",
      "AllowedPattern": "^\\/.*\\/$|^\\/$",
      "ConstraintDescription": "Role path must start and end with forward slash (/)"
    },
    "RoleManagedPolicy1": {
      "Type": "String",
      "Description": "Optional managed policy ARN 1",
      "Default": ""
    },
    "RoleManagedPolicy2": {
      "Type": "String",
      "Description": "Optional managed policy ARN 2",
      "Default": ""
    },
    "RoleManagedPolicy3": {
      "Type": "String",
      "Description": "Optional managed policy ARN 3",
      "Default": ""
    },
    "RoleManagedPolicy4": {
      "Type": "String",
      "Description": "Optional managed policy ARN 4",
      "Default": ""
    },
    "RoleManagedPolicy5": {
      "Type": "String",
      "Description": "Optional managed policy ARN 5",
      "Default": ""
    }
  },
  "Conditions": {
    "HasCustomRoleName": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleName"}, ""]}]},
    "HasPermissionsBoundary": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RolePermissionsBoundary"}, ""]}]},
    "HasPolicy1": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy1"}, ""]}]},
    "HasPolicy2": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy2"}, ""]}]},
    "HasPolicy3": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy3"}, ""]}]},
    "HasPolicy4": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy4"}, ""]}]},
    "HasPolicy5": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy5"}, ""]}]}
  },
  "Resources": {
    "SAMLFederatedRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "RoleName": {"Fn::If": ["HasCustomRoleName", {"Ref": "RoleName"}, {"Ref": "AWS::NoValue"}]},
        "Description": "IAM role with SAML provider trust",
        "MaxSessionDuration": {"Ref": "RoleSessionDuration"},
        "PermissionsBoundary": {"Fn::If": ["HasPermissionsBoundary", {"Ref": "RolePermissionsBoundary"}, {"Ref": "AWS::NoValue"}]},
        "Path": {"Ref": "RolePath"},
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Federated": {"Ref": "SAMLProviderARN"}
              },
              "Action": "sts:AssumeRoleWithSAML",
              "Condition": {
                "StringEquals": {
                  "SAML:aud": "https://signin.aws.amazon.com/saml"
                }
              }
            }
          ]
        },
        "ManagedPolicyArns": {
          "Fn::Split": [
            ",",
            {
              "Fn::Join": [
                ",",
                [
                  {"Fn::If": ["HasPolicy1", {"Ref": "RoleManagedPolicy1"}, {"Ref": "AWS::NoValue"}]},
                  {"Fn::If": ["HasPolicy2", {"Ref": "RoleManagedPolicy2"}, {"Ref": "AWS::NoValue"}]},
                  {"Fn::If": ["HasPolicy3", {"Ref": "RoleManagedPolicy3"}, {"Ref": "AWS::NoValue"}]},
                  {"Fn::If": ["HasPolicy4", {"Ref": "RoleManagedPolicy4"}, {"Ref": "AWS::NoValue"}]},
                  {"Fn::If": ["HasPolicy5", {"Ref": "RoleManagedPolicy5"}, {"Ref": "AWS::NoValue"}]}
                ]
              ]
            }
          ]
        }
      }
    }
  },
  "Outputs": {
    "RoleARN": {
      "Description": "ARN of the created IAM Role",
      "Value": {"Fn::GetAtt": ["SAMLFederatedRole", "Arn"]},
      "Export": {
        "Name": {"Fn::Sub": "${AWS::StackName}-RoleARN"}
      }
    }
  }
}

YAML

AWSTemplateFormatVersion: '2010-09-09'
Description: '[AWSDocs] IAM: tutorial_saml-federated-role'

Parameters:
  RoleName:
    Type: String
    Description: 'Name of the IAM Role (leave empty for auto-generated name like ''{StackName}-{UniqueId}'')'
    Default: ""
    AllowedPattern: '^$|^[\w+=,.@-]{1,64}$'
    ConstraintDescription: 'Must be empty or 1-64 characters and can contain alphanumeric characters and +=,.@-'

  SAMLProviderARN:
    Type: String
    Description: 'ARN of the SAML Identity Provider'
    AllowedPattern: '^arn:aws:iam::\d{12}:saml-provider/[a-zA-Z0-9._-]+$'
    ConstraintDescription: 'Must be a valid SAML provider ARN'

  RoleSessionDuration:
    Type: Number
    Description: 'The maximum session duration (in seconds) that you want to set for the specified role (3600-43200)'
    MinValue: 3600
    MaxValue: 43200
    Default: 7200

  RolePermissionsBoundary:
    Type: String
    Description: Optional ARN of the permissions boundary policy (leave empty for none)
    Default: ""

  RolePath:
    Type: String
    Description: 'Path for the IAM role (must start and end with /)'
    Default: "/"
    AllowedPattern: '^\/.*\/$|^\/$'
    ConstraintDescription: 'Role path must start and end with forward slash (/)'

  RoleManagedPolicy1:
    Type: String
    Description: Optional managed policy ARN 1
    Default: ""
  RoleManagedPolicy2:
    Type: String
    Description: Optional managed policy ARN 2
    Default: ""
  RoleManagedPolicy3:
    Type: String
    Description: Optional managed policy ARN 3
    Default: ""
  RoleManagedPolicy4:
    Type: String
    Description: Optional managed policy ARN 4
    Default: ""
  RoleManagedPolicy5:
    Type: String
    Description: Optional managed policy ARN 5
    Default: ""

Conditions:
  HasCustomRoleName: !Not [!Equals [!Ref RoleName, ""]]
  HasPermissionsBoundary: !Not [!Equals [!Ref RolePermissionsBoundary, ""]]
  HasPolicy1: !Not [!Equals [!Ref RoleManagedPolicy1, ""]]
  HasPolicy2: !Not [!Equals [!Ref RoleManagedPolicy2, ""]]
  HasPolicy3: !Not [!Equals [!Ref RoleManagedPolicy3, ""]]
  HasPolicy4: !Not [!Equals [!Ref RoleManagedPolicy4, ""]]
  HasPolicy5: !Not [!Equals [!Ref RoleManagedPolicy5, ""]]

Resources:
  SAMLFederatedRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !If
        - HasCustomRoleName
        - !Ref RoleName
        - !Ref AWS::NoValue
      Description: 'IAM role with SAML provider trust'
      MaxSessionDuration: !Ref RoleSessionDuration
      PermissionsBoundary: !If
        - HasPermissionsBoundary
        - !Ref RolePermissionsBoundary
        - !Ref AWS::NoValue
      Path: !Ref RolePath
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Federated: !Ref SAMLProviderARN
            Action: 'sts:AssumeRoleWithSAML'
            Condition:
              StringEquals:
                'SAML:aud': 'https://signin.aws.amazon.com/saml'
      ManagedPolicyArns:
        !Split
          - ','
          - !Join
            - ','
            - - !If [HasPolicy1, !Ref RoleManagedPolicy1, !Ref 'AWS::NoValue']
              - !If [HasPolicy2, !Ref RoleManagedPolicy2, !Ref 'AWS::NoValue']
              - !If [HasPolicy3, !Ref RoleManagedPolicy3, !Ref 'AWS::NoValue']
              - !If [HasPolicy4, !Ref RoleManagedPolicy4, !Ref 'AWS::NoValue']
              - !If [HasPolicy5, !Ref RoleManagedPolicy5, !Ref 'AWS::NoValue']

Outputs:
  RoleARN:
    Description: 'ARN of the created IAM Role'
    Value: !GetAtt SAMLFederatedRole.Arn
    Export:
      Name: !Sub '${AWS::StackName}-RoleARN'

더 알아보기 (Learn more)