

# IAM 자습서: CloudFormation 템플릿을 사용하여 SAML ID 제공업체(idP) 생성
<a name="tutorial_saml-idp"></a>

AWS 계정에 대한 SAML 페더레이션을 설정하려면 SAML ID 제공업체(idP)를 생성해야 합니다. 이 자습서에서는 CloudFormation 템플릿을 사용하여 AWS와 외부 IdP 간에 신뢰를 설정하는 SAML IdP를 생성하는 방법을 보여줍니다.

이 템플릿은 IdP의 메타데이터 문서로 구성된 SAML IdP를 생성합니다. 그러면 페더레이션 IAM 역할이 이 IdP를 참조하여 외부 IdP의 인증된 사용자가 AWS 리소스에 액세스할 수 있도록 허용합니다.

배포된 리소스는 IdP의 메타데이터 문서와 선택적 암호화 설정으로 구성된 SAML IdP로 이루어져 있습니다.

## 사전 조건
<a name="tutorial_saml-idp-prereqs"></a>

이 자습서에서는 다음을 이미 완료했다고 가정합니다.
+ 이 자습서에서 IdP의 SAML 메타데이터 XML 파일의 형식을 지정하는 데 사용되는 Python 명령을 실행할 수 있도록 로컬 시스템에 설치된 Python 3.6 이상 버전.
+ XML 파일로 저장된 외부 IdP의 SAML 메타데이터 문서.

## CloudFormation을 사용하여 SAML IdP 생성
<a name="tutorial_saml-idp-create"></a>

SAML IdP를 생성하려면 CloudFormation 템플릿을 생성하고 이를 사용하여 IdP 리소스가 포함된 스택을 생성합니다.

### 템플릿 생성
<a name="tutorial_saml-idp-file"></a>

우선 CloudFormation 템플릿을 만듭니다.

1. [템플릿](#tutorial_saml-idp-template) 섹션에서 **JSON** 또는 **YAML** 탭의 복사 아이콘을 클릭하여 템플릿 콘텐츠를 복사합니다.

1. 템플릿 콘텐츠를 새 파일에 붙여 넣습니다.

1. 파일을 로컬에 저장합니다.

### 스택 생성
<a name="tutorial_saml-idp-stack"></a>

다음으로, 저장한 템플릿을 사용하여 CloudFormation 스택을 프로비저닝합니다.

1. CloudFormation 콘솔([https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/))을 엽니다.

1. **스택** 페이지의 **스택 생성**에서 **새 리소스 사용(표준)**을 선택합니다.

1. 템플릿을 지정합니다.

   1. **사전 조건**에서 **기존 템플릿 선택**을 선택합니다.

   1. **템플릿 지정**에서 **템플릿 파일 업로드**를 선택합니다.

   1. **파일 선택**을 선택하고 템플릿 파일로 이동한 후 해당 파일을 선택합니다.

   1. **다음**을 선택합니다.

1. 다음과 같은 스택 세부 정보를 지정합니다.

   1. 스택 이름을 입력합니다.

   1. **IdentityProviderName**의 경우 이 값을 비워 두어 스택 이름을 기반으로 이름을 자동 생성하거나, SAML IdP의 사용자 지정 이름을 입력할 수 있습니다. 사용자 지정 이름에는 영숫자, 마침표, 밑줄, 하이픈만 포함할 수 있습니다.

   1. **IdentityProviderSAMLMetadataDocument**의 경우 이 필드에 붙여 넣기 전에 SAML 메타데이터 XML 파일의 형식을 한 줄로 지정해야 합니다. 이렇게 해야 하는 이유는 CloudFormation 콘솔에서 콘솔 파라미터를 전달할 때 XML 콘텐츠의 형식을 한 줄로 지정해야 하기 때문입니다.

      아래의 Python 명령을 사용하여 XML 파일의 형식을 다시 지정합니다.

      ```
      python3 -c "import sys, re; content=open(sys.argv[1]).read(); print(re.sub(r'>\s+<', '><', content.replace('\n', '').replace('\r', '').strip()))" saml-metadata.xml
      ```
**참고**  
IdP의 SAML 메타데이터 문서는 콘솔 파라미터 입력을 위해 한 줄로 형식을 지정해야 합니다. Python 명령은 줄 바꿈과 추가 공백을 제거하여 모든 원본 콘텐츠와 구조를 그대로 유지하면서 필요한 형식을 생성합니다.

      Python 명령에서 출력을 복사한 후 이를 **IdentityProviderSAMLMetadataDocument** 필드에 붙여 넣습니다.

      형식이 지정된 SAML 메타데이터 문서의 예시(간략화함):

      ```
      <?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://portal.sso.example.com/saml/assertion/CompanyIdP"><md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiIMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV...</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://portal.sso.example.com/saml/logout/CompanyIdP"/><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://portal.sso.example.com/saml/assertion/CompanyIdP"/></md:IDPSSODescriptor></md:EntityDescriptor>
      ```

   1. 다른 파라미터의 경우 기본값을 적용하거나, 요구 사항에 따라 직접 입력합니다.
      + **IdentityProviderAddPrivateKey** - SAML 어설션의 암호 해독을 위한 선택적 프라이빗 키
      + **IdentityProviderAssertionEncryptionMode** - 선택 사항, SAML 어설션에 대한 암호화 모드 설정(허용됨, 필수 또는 비어 있음)

   1. **다음**을 선택합니다.

1. 스택 옵션을 구성합니다.

   1. **스택 실패 옵션**에서 **새로 생성된 모든 리소스 삭제**를 선택합니다.
**참고**  
이 옵션을 선택하면 스택 생성에 실패하더라도 삭제 정책에서 보존하도록 지정하는 리소스에 대해서는 요금이 청구되지 않습니다.

   1. 모든 기타 기본값을 적용합니다.

   1. **기능**에서 CloudFormation이 계정에 IAM 리소스를 생성할 수 있음을 승인하는 확인란을 선택합니다.

   1. **다음**을 선택합니다.

1. 스택 세부 정보를 검토하고 **제출**을 선택합니다.

CloudFormation이 스택을 생성합니다. 스택 생성이 완료되면 스택 리소스를 사용할 준비가 된 것입니다. 스택 세부 정보 페이지의 **리소스** 탭을 사용하여 계정에 프로비저닝된 리소스를 볼 수 있습니다.

스택은 **출력** 탭에서 볼 수 있는 다음과 같은 값을 출력합니다.
+ **ProviderARN**: 생성된 SAML IdP의 ARN(예: `arn:aws:iam::123456789012:saml-provider/CompanyIdP`). 이 제공업체를 신뢰하는 역할을 생성할 경우 이 ARN이 필요합니다.
+ **ProviderName**: 생성된 SAML IdP의 이름(예: 사용자 지정 이름을 지정한 경우 `CompanyIdP`, 또는 기본 이름을 사용한 경우 `my-saml-stack-saml-provider`).

이러한 출력도 내보내므로, `Fn::ImportValue` 함수를 사용하여 이를 다른 CloudFormation 스택에서 가져올 수 있습니다.

## SAML IdP 확인
<a name="tutorial_saml-idp-using"></a>

SAML IdP가 생성되면 구성을 확인하고 페더레이션 역할과 함께 사용할 ARN을 기록할 수 있습니다.

1. IAM 콘솔([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/))을 엽니다.

1. 탐색 창에서 [**자격 증명 공급자(Identity providers)**]를 선택합니다.

   새로 생성된 SAML IdP가 목록에 표시됩니다.

1. 세부 정보를 보려면 IdP 이름을 선택합니다.

   IdP 세부 정보 페이지에서 SAML 메타데이터 문서 및 기타 구성 세부 정보를 볼 수 있습니다.

1. 세부 정보 페이지에 표시된 **공급자 ARN**을 기록해 둡니다.

   이 IdP를 신뢰하는 페더레이션 IAM 역할을 생성할 경우 이 ARN이 필요합니다.

1. 메타데이터 문서를 검토하여 외부 IdP에서 제공한 것과 일치하는지 확인합니다.

이제 SAML IdP를 페더레이션 IAM 역할에서 사용할 준비가 되었습니다. 이 IdP를 신뢰하는 역할을 생성하면 외부 IdP의 인증된 사용자가 이러한 역할을 수임하고 AWS 리소스에 액세스할 수 있습니다.

## 정리: 리소스 삭제
<a name="tutorial_saml-idp-delete"></a>

마지막 단계로, 스택을 삭제하고 해당 스택에 포함된 리소스를 삭제합니다.

1. CloudFormation 콘솔을 엽니다.

1. **스택** 페이지에서 템플릿을 통해 생성한 스택을 선택하고 **삭제**를 선택한 다음, **삭제**를 확인합니다.

   CloudFormation은 스택 및 해당 스택에 포함된 모든 리소스의 삭제를 시작합니다.

## CloudFormation 템플릿 세부 정보
<a name="tutorial_saml-idp-template-details"></a>

### 리소스
<a name="tutorial_saml-idp-template-resources"></a>

이 자습서에서 CloudFormation 템플릿은 계정에 다음과 같은 리소스를 생성합니다.

[https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-samlprovider.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-samlprovider.html): AWS와 외부 IdP 간에 신뢰를 설정하는 SAML IdP입니다.

### 구성
<a name="tutorial_saml-idp-template-config"></a>

템플릿에는 다음과 같은 구성 가능한 파라미터가 포함되어 있습니다.
+ **IdentityProviderName** - SAML IdP의 이름(자동 생성된 이름의 경우 비워둠)

  예: `CompanyIdP` 또는 `EnterpriseSSO`
+ **IdentityProviderSAMLMetadataDocument** - 외부 IdP의 SAML 메타데이터 문서(한 줄 형식)
+ **IdentityProviderAddPrivateKey** - SAML 어설션의 암호 해독을 위한 선택적 프라이빗 키
+ **IdentityProviderAssertionEncryptionMode** - 선택 사항, SAML 어설션에 대한 암호화 모드 설정

## CloudFormation 템플릿
<a name="tutorial_saml-idp-template"></a>

아래의 JSON 또는 YAML 코드를 별도의 파일로 저장하여 이 자습서의 CloudFormation 템플릿으로 사용하세요.

------
#### [ JSON ]

```
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "[AWSDocs] IAM: tutorial_saml-idp",
  "Parameters": {
    "IdentityProviderName": {
      "Type": "String",
      "Description": "Name of the SAML Identity Provider (leave empty for auto-generated name like '{StackName}-{UniqueId}')",
      "Default": "",
      "AllowedPattern": "^$|^[a-zA-Z0-9._-]+$",
      "ConstraintDescription": "Must be empty or contain only alphanumeric characters, periods, underscores, and hyphens"
    },
    "IdentityProviderSAMLMetadataDocument": {
      "Type": "String",
      "Description": "SAML metadata document from identity provider"
    },
    "IdentityProviderAddPrivateKey": {
      "Type": "String",
      "Description": "Optional private key for decrypting SAML assertions. The private key must be a .pem file that uses AES-GCM or AES-CBC encryption algorithm to decrypt SAML assertions.",
      "Default": ""
    },
    "IdentityProviderAssertionEncryptionMode": {
      "Type": "String",
      "Description": "Optional, sets encryption mode for SAML assertions",
      "Default": "",
      "AllowedValues": ["", "Allowed", "Required"]
    }
  },
  "Conditions": {
    "HasPrivateKey": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAddPrivateKey"}, ""]}]},
    "HasEncryptionMode": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAssertionEncryptionMode"}, ""]}]},
    "HasCustomName": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderName"}, ""]}]}
  },
  "Resources": {
    "SAMLProvider": {
      "Type": "AWS::IAM::SAMLProvider",
      "Properties": {
        "Name": {"Fn::If": ["HasCustomName", {"Ref": "IdentityProviderName"}, {"Ref": "AWS::NoValue"}]},
        "SamlMetadataDocument": {"Ref": "IdentityProviderSAMLMetadataDocument"},
        "Tags": [
          {
            "Key": "Name",
            "Value": {"Fn::If": ["HasCustomName", {"Ref": "IdentityProviderName"}, {"Fn::Sub": "${AWS::StackName}-saml-provider"}]}
          }
        ],
        "AddPrivateKey": {"Fn::If": ["HasPrivateKey", {"Ref": "IdentityProviderAddPrivateKey"}, {"Ref": "AWS::NoValue"}]},
        "AssertionEncryptionMode": {"Fn::If": ["HasEncryptionMode", {"Ref": "IdentityProviderAssertionEncryptionMode"}, {"Ref": "AWS::NoValue"}]}
      }
    }
  },
  "Outputs": {
    "ProviderARN": {
      "Description": "ARN of the created SAML Identity Provider",
      "Value": {"Ref": "SAMLProvider"},
      "Export": {
        "Name": {"Fn::Sub": "${AWS::StackName}-ProviderARN"}
      }
    },
    "ProviderName": {
      "Description": "Name of the SAML Identity Provider",
      "Value": {"Fn::If": ["HasCustomName", {"Ref": "IdentityProviderName"}, {"Fn::Sub": "${AWS::StackName}-saml-provider"}]},
      "Export": {
        "Name": {"Fn::Sub": "${AWS::StackName}-ProviderName"}
      }
    }
  }
}
```

------
#### [ YAML ]

```
AWSTemplateFormatVersion: '2010-09-09'
Description: '[AWSDocs] IAM: tutorial_saml-idp'

Parameters:
  IdentityProviderName:
    Type: String
    Description: Name of the SAML Identity Provider (leave empty for auto-generated name like '{StackName}-{UniqueId}')
    Default: ""
    AllowedPattern: '^$|^[a-zA-Z0-9._-]+$'
    ConstraintDescription: 'Must be empty or contain only alphanumeric characters, periods, underscores, and hyphens'

  IdentityProviderSAMLMetadataDocument:
    Type: String
    Description: SAML metadata document from identity provider

  IdentityProviderAddPrivateKey:
    Type: String
    Description: Optional private key for decrypting SAML assertions. The private key must be a .pem file that uses AES-GCM or AES-CBC encryption algorithm to decrypt SAML assertions.
    Default: ""

  IdentityProviderAssertionEncryptionMode:
    Type: String
    Description: Optional, sets encryption mode for SAML assertions
    Default: ""
    AllowedValues:
      - ""
      - "Allowed"
      - "Required"

Conditions:
  HasPrivateKey: !Not [!Equals [!Ref IdentityProviderAddPrivateKey, ""]]
  HasEncryptionMode: !Not [!Equals [!Ref IdentityProviderAssertionEncryptionMode, ""]]
  HasCustomName: !Not [!Equals [!Ref IdentityProviderName, ""]]

Resources:
  SAMLProvider:
    Type: 'AWS::IAM::SAMLProvider'
    Properties:
      Name: !If
        - HasCustomName
        - !Ref IdentityProviderName
        - !Ref AWS::NoValue
      SamlMetadataDocument: !Ref IdentityProviderSAMLMetadataDocument
      Tags:
        - Key: Name
          Value: !If
            - HasCustomName
            - !Ref IdentityProviderName
            - !Sub '${AWS::StackName}-saml-provider'
      AddPrivateKey: !If
        - HasPrivateKey
        - !Ref IdentityProviderAddPrivateKey
        - !Ref AWS::NoValue
      AssertionEncryptionMode: !If
        - HasEncryptionMode
        - !Ref IdentityProviderAssertionEncryptionMode
        - !Ref AWS::NoValue

Outputs:
  ProviderARN:
    Description: 'ARN of the created SAML Identity Provider'
    Value: !Ref SAMLProvider
    Export:
      Name: !Sub '${AWS::StackName}-ProviderARN'
  
  ProviderName:
    Description: 'Name of the SAML Identity Provider'
    Value: !If
      - HasCustomName
      - !Ref IdentityProviderName
      - !Sub '${AWS::StackName}-saml-provider'
    Export:
      Name: !Sub '${AWS::StackName}-ProviderName'
```

------