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

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

SAML 페더레이션과 그 기능에 익숙해지기 위해 CloudFormation 템플릿을 사용해 SAML 자격 증명 공급자(IdP)와 연결된 페더레이션 IAM 역할을 설정할 거예요. 이 튜토리얼은 두 리소스를 단일 스택에서 함께 만드는 방법을 보여줘요.

출처: 문서

본문

템플릿은 AWS 리소스에 대한 페더레이션 접근에 사용할 수 있는 SAML IdP와, SAML 공급자를 신뢰하는 IAM 역할을 만들어요. 외부 IdP로 인증된 사용자는 이 역할을 맡아 AWS 리소스에 접근할 수 있어요.

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

  • IdP의 메타데이터 문서로 구성된 SAML IdP
  • SAML IdP를 신뢰하고 인증된 사용자가 맡을 수 있는 페더레이션 IAM 역할
  • 역할에 연결해 특정 권한을 부여할 수 있는 구성 가능한 관리형 정책

사전 요구 사항 (Prerequisites)

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

  • 이 튜토리얼에서 IdP의 SAML 메타데이터 XML 파일을 포맷하는 데 사용하는 Python 명령을 실행할 로컬 머신에 설치된 Python 3.6 이상
  • 외부 IdP의 SAML 메타데이터 문서를 XML 파일로 저장한 것

CloudFormation으로 SAML IdP와 역할 생성 (Create a SAML IdP and role using CloudFormation)

SAML IdP와 페더레이션 역할을 만들려면 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. 다음 스택 세부 정보를 지정해요:
    • 스택 이름을 입력해요.
    • IdentityProviderName은 비워 두면 스택 이름을 기반으로 이름이 자동 생성되거나, SAML IdP에 사용자 지정 이름을 입력할 수 있어요. 예: CompanyIdP 또는 EnterpriseSSO
    • 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>
      
    • RoleName은 비워 두면 스택 이름을 기반으로 이름이 자동 생성되거나, 페더레이션 IAM 역할에 사용자 지정 이름을 입력할 수 있어요. 예: SAML-Developer-Access 또는 SAML-ReadOnly-Role
    • 다른 매개변수는 요구 사항에 따라 기본값을 수락하거나 직접 입력해요:
      • IdentityProviderAddPrivateKey – SAML 어서션을 복호화하기 위한 선택적 개인 키
      • IdentityProviderAssertionEncryptionMode – SAML 어서션의 암호화 모드. 예시 값: Allowed, Required, 또는 암호화 없음으로 비워 두기
      • RoleSessionDuration – 최대 세션 기간(초) (3600-43200, 기본값 7200) 예: 14400 (4시간)
      • RolePermissionsBoundary – 권한 경계 정책의 선택적 ARN 예: arn:aws:iam::123456789012:policy/DeveloperBoundary
      • RolePath – IAM 역할의 경로 (기본값은 /) 예: /saml-roles/
      • RoleManagedPolicy1-5 – 연결할 최대 5개 관리형 정책의 선택적 ARN. RoleManagedPolicy1의 예: arn:aws:iam::aws:policy/ReadOnlyAccess. RoleManagedPolicy2의 예: 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).
  • IdentityProviderARN: 생성된 SAML IdP의 ARN(예: arn:aws:iam::123456789012:saml-provider/CompanyIdP).

IdP가 역할 전환용 적절한 SAML 속성을 보내도록 구성하려면 이 두 ARN이 모두 필요해요.

SAML 페더레이션 테스트 (Test the SAML federation)

SAML IdP와 페더레이션 역할을 만든 후에는 페더레이션 설정을 테스트할 수 있어요.

  1. https://console.aws.amazon.com/iam/ 에서 IAM 콘솔을 열어요.
  2. 탐색 창에서 Identity providers를 선택해요.
  3. 목록에 새로 만든 SAML IdP가 보여야 해요.
  4. IdP 이름을 선택해 세부 정보를 확인해요. IdP 상세 페이지에서 SAML 메타데이터 문서와 기타 구성 세부 정보를 볼 수 있어요.
  5. 탐색 창에서 Roles를 선택해요.
  6. 새로 만든 페더레이션 역할을 찾아 선택해요. 역할 상세 페이지에서 SAML IdP가 이 역할을 맡을 수 있게 허용하는 신뢰 정책을 볼 수 있어요.
  7. Trust relationships 탭을 선택해 신뢰 정책을 검토해요.
  8. 신뢰 정책은 SAML IdP가 신뢰받아 이 역할을 맡을 수 있고, SAML 대상(SAML:aud)이 https://signin.aws.amazon.com/saml과 일치한다는 조건이 있는지 보여줘야 해요.

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

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

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

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

리소스 (Resources)

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

  • AWS::IAM::SAMLProvider: AWS와 외부 IdP 간의 신뢰를 설정하는 SAML IdP
  • AWS::IAM::Role: SAML IdP를 통해 인증된 사용자가 맡을 수 있는 페더레이션 IAM 역할

구성 (Configuration)

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

  • IdentityProviderName – SAML IdP의 이름 (자동 생성 이름을 위해 비워 두기)
  • IdentityProviderSAMLMetadataDocument – IdP의 SAML 메타데이터 문서 (필수)
  • IdentityProviderAddPrivateKey – SAML 어서션을 복호화하기 위한 선택적 개인 키
  • IdentityProviderAssertionEncryptionMode – SAML 어서션의 암호화 모드
  • RoleName – IAM 역할의 이름 (자동 생성 이름을 위해 비워 두기)
  • RolePath – IAM 역할의 경로 (기본값 /)
  • RolePermissionsBoundary – 권한 경계 정책의 선택적 ARN
  • RoleSessionDuration – 최대 세션 기간(초) (3600-43200, 기본값 7200)
  • RoleManagedPolicy1-5 – 연결할 최대 5개 관리형 정책의 선택적 ARN

CloudFormation 템플릿 (CloudFormation template)

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

JSON

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "[AWSDocs] IAM: tutorial_saml-idp-and-federated-role",
  "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"]
    },
    "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 +=,.@-"
    },
    "RolePath": {
      "Type": "String",
      "Description": "Path for the IAM Role",
      "AllowedPattern": "(^\\/$)|(^\\/.*\\/$)",
      "Default": "/"
    },
    "RolePermissionsBoundary": {
      "Type": "String",
      "Description": "Optional ARN of the permissions boundary policy (leave empty for none)",
      "Default": ""
    },
    "RoleSessionDuration": {
      "Description": "The maximum session duration (in seconds) that you want to set for the specified role (3600-43200)",
      "Type": "Number",
      "MinValue": 3600,
      "MaxValue": 43200,
      "Default": 7200
    },
    "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": {
    "HasCustomProviderName": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderName"}, ""]}]},
    "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"}, ""]}]},
    "HasPrivateKey": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAddPrivateKey"}, ""]}]},
    "HasAssertionEncryptionMode": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAssertionEncryptionMode"}, ""]}]}
  },
  "Resources": {
    "SAMLProvider": {
      "Type": "AWS::IAM::SAMLProvider",
      "Properties": {
        "Name": {"Fn::If": ["HasCustomProviderName", {"Ref": "IdentityProviderName"}, {"Ref": "AWS::NoValue"}]},
        "SamlMetadataDocument": {"Ref": "IdentityProviderSAMLMetadataDocument"},
        "AddPrivateKey": {"Fn::If": ["HasPrivateKey", {"Ref": "IdentityProviderAddPrivateKey"}, {"Ref": "AWS::NoValue"}]},
        "AssertionEncryptionMode": {"Fn::If": ["HasAssertionEncryptionMode", {"Ref": "IdentityProviderAssertionEncryptionMode"}, {"Ref": "AWS::NoValue"}]}
      }
    },
    "SAMLFederatedRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "RoleName": {"Fn::If": ["HasCustomRoleName", {"Ref": "RoleName"}, {"Ref": "AWS::NoValue"}]},
        "Path": {"Ref": "RolePath"},
        "Description": "SAML federated IAM role for SSO access with specified permissions",
        "MaxSessionDuration": {"Ref": "RoleSessionDuration"},
        "PermissionsBoundary": {"Fn::If": ["HasPermissionsBoundary", {"Ref": "RolePermissionsBoundary"}, {"Ref": "AWS::NoValue"}]},
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Federated": {"Ref": "SAMLProvider"}
              },
              "Action": [
                "sts:AssumeRole",
                "sts:SetSourceIdentity",
                "sts:TagSession"
              ],
              "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"}
      }
    },
    "IdentityProviderARN": {
      "Description": "ARN of the created SAML Identity Provider",
      "Value": {"Ref": "SAMLProvider"},
      "Export": {
        "Name": {"Fn::Sub": "${AWS::StackName}-IdentityProviderARN"}
      }
    }
  }
}

YAML

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

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"

  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 +=,.@-"

  RolePath:
    Type: String
    Description: Path for the IAM Role
    AllowedPattern: (^\/$)|(^\/.*\/$)
    Default: "/"

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

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

  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:
  HasCustomProviderName: !Not [!Equals [!Ref IdentityProviderName, ""]]
  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, ""]]
  HasPrivateKey: !Not [!Equals [!Ref IdentityProviderAddPrivateKey, ""]]
  HasAssertionEncryptionMode: !Not [!Equals [!Ref IdentityProviderAssertionEncryptionMode, ""]]

Resources:
  SAMLProvider:
    Type: AWS::IAM::SAMLProvider
    Properties:
      Name: !If
        - HasCustomProviderName
        - !Ref IdentityProviderName
        - !Ref AWS::NoValue
      SamlMetadataDocument: !Ref IdentityProviderSAMLMetadataDocument
      AddPrivateKey: !If
        - HasPrivateKey
        - !Ref IdentityProviderAddPrivateKey
        - !Ref AWS::NoValue
      AssertionEncryptionMode: !If
        - HasAssertionEncryptionMode
        - !Ref IdentityProviderAssertionEncryptionMode
        - !Ref AWS::NoValue

  SAMLFederatedRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !If
        - HasCustomRoleName
        - !Ref RoleName
        - !Ref AWS::NoValue
      Path: !Ref RolePath
      Description: "SAML federated IAM role for SSO access with specified permissions"
      MaxSessionDuration: !Ref RoleSessionDuration
      PermissionsBoundary: !If
        - HasPermissionsBoundary
        - !Ref RolePermissionsBoundary
        - !Ref AWS::NoValue
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Federated: !Ref SAMLProvider
            Action:
              - 'sts:AssumeRole'
              - 'sts:SetSourceIdentity'
              - 'sts:TagSession'
            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'

  IdentityProviderARN:
    Description: ARN of the created SAML Identity Provider
    Value: !Ref SAMLProvider
    Export:
      Name: !Sub '${AWS::StackName}-IdentityProviderARN'

더 알아보기 (Learn more)