

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# IAM 教學課程：使用 CloudFormation 範本建立 SAML 身分提供者 (IdP) 和 SAML 聯合 IAM 角色
<a name="tutorial_saml-idp-and-federated-role"></a>

若要熟悉 SAML 聯合及其功能，您將使用 CloudFormation 範本來設定 SAML 身分提供者 (IdP) 和相關聯的聯合 IAM 角色。本教學課程說明如何在單一堆疊中同時建立這兩個資源。

範本會建立 SAML IdP，可用於聯合存取 AWS 資源，以及信任 SAML 供應商的 IAM 角色。外部 IdP 驗證的使用者可以擔任此角色來存取 AWS 資源。

部署的資源包含下列項目：
+ 使用 IdP 中繼資料文件設定的 SAML IdP。
+ 信任 SAML IdP 且可由經過身分驗證之使用者擔任的聯合身分 IAM 角色。
+ 可連接至角色以授予特定許可的可設定受管政策。

## 先決條件
<a name="tutorial_saml-idp-and-federated-role-prereqs"></a>

此教學課程假設您已備妥下列項目：
+ 在本機電腦上安裝的 Python 3.6 或更新版本，以便執行本教學課程中用於格式化 IdP SAML 中繼資料 XML 檔案的 Python 命令。
+ 外部 IdP 中儲存為 XML 檔案的 SAML 中繼資料文件。

## 使用 建立 SAML IdP 和角色 CloudFormation
<a name="tutorial_saml-idp-and-federated-role-create"></a>

若要建立 SAML IdP 和聯合身分角色，需要建立 CloudFormation 範本，並使用它來建立包含這兩個資源的堆疊。

### 建立範本
<a name="tutorial_saml-idp-and-federated-role-file"></a>

首先，建立 CloudFormation 範本。

1. 在 [範本](#tutorial_saml-idp-and-federated-role-template) 區段中，按一下 **JSON** 或 **YAML** 索引標籤上的複製圖示，以複製範本內容。

1. 在新檔案中貼上範本內容。

1. 在本機儲存檔案。

### 建立 堆疊。
<a name="tutorial_saml-idp-and-federated-role-stack"></a>

接下來，使用剛儲存的範本來佈建 CloudFormation 堆疊。

1. 在 https：//[https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/) 開啟 CloudFormation 主控台。

1. 在**堆疊**頁面上，從**建立堆疊**功能表中選擇**使用新資源 (標準)**。

1. 指定範本：

   1. 在**必要條件**下，選擇**選擇現有範本**。

   1. 在**指定範本**下，選擇**上傳範本檔案**。

   1. 選擇**選擇檔案**，導覽至範本檔案並選取它。

   1. 選擇**下一步**。

1. 指定下列堆疊詳細資訊：

   1. 輸入堆疊名稱。

   1. 對於 **IdentityProviderName**，您可以將其留空，以根據堆疊名稱自動產生名稱，或輸入 SAML IdP 的自訂名稱。

      範例：`CompanyIdP` 或 `EnterpriseSSO`

   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. 對於 **RoleName**，可以將其留空，以根據堆疊名稱自動產生名稱，或輸入聯合身分 IAM 角色的自訂名稱。

      範例：`SAML-Developer-Access` 或 `SAML-ReadOnly-Role`

   1. 對於其他參數，請接受預設值或根據需求輸入自己的值：
      + **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`

   1. 選擇**下一步**。

1. 設定堆疊選項：

   1. 在**堆疊失敗選項**下，選擇**刪除所有新建立的資源**。
**注意**  
選擇此選項可避免堆疊建立失敗時，因資源刪除政策指定為保留而可能產生的資源費用。

   1. 接受所有其他預設值。

   1. 在**功能**下，勾選方塊以確認 CloudFormation 可能會在帳戶中建立 IAM 資源。

   1. 選擇**下一步**。

1. 檢閱堆疊詳細資訊，然後選擇**提交**。

CloudFormation 會建立堆疊。堆疊建立完成後，堆疊資源即可使用。您可以使用堆疊詳細資訊頁面上的**資源**索引標籤，來檢視帳戶中佈建的資源。

堆疊將輸出下列值，您可以在**輸出**索引標籤上檢視：
+ **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 聯合身分
<a name="tutorial_saml-idp-and-federated-role-using"></a>

SAML IdP 和聯合身分角色建立後，您可以測試聯合身分設定。

1. 在以下網址開啟 IAM 主控台：[https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)。

1. 在導覽窗格中，請選擇 **Identity providers** (身分提供者)。

   您應該會在清單中看到新建立的 SAML IdP。

1. 選擇 IdP 名稱以檢視其詳細資訊。

   在 IdP 詳細資訊頁面上，可以查看 SAML 中繼資料文件及其他組態詳細資訊。

1. 在導覽窗格中，選擇**角色**。

1. 找到並選擇新建立的聯合身分角色。

   在角色詳細資訊頁面上，可以查看允許 SAML IdP 擔任此角色的信任政策。

1. 選擇**信任關係**索引標籤以檢閱信任政策。

   信任政策應該會顯示 SAML IdP 受信任可擔任此角色，條件是 SAML 對象 (`SAML:aud`) 與 `https://signin.aws.amazon.com/saml` 相符。

## 清理：刪除資源
<a name="tutorial_saml-idp-and-federated-role-delete"></a>

最後一個步驟是，刪除堆疊及其包含的資源。

1. 開啟 CloudFormation 主控台。

1. 在**堆疊**頁面上，選擇從範本建立的堆疊，然後選擇**刪除**，接著確認**刪除**。

   CloudFormation 會啟動刪除堆疊及其包含的所有資源。

## CloudFormation 範本詳細資訊
<a name="tutorial_saml-idp-and-federated-role-template-details"></a>

### Resources
<a name="tutorial_saml-idp-and-federated-role-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。
+ [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html)：可由透過 SAML IdP 進行身分驗證之使用者擔任的聯合身分 IAM 角色。

### Configuration
<a name="tutorial_saml-idp-and-federated-role-configuration"></a>

範本包含下列可設定的參數：
+ **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 範本
<a name="tutorial_saml-idp-and-federated-role-template"></a>

將下列 JSON 或 YAML 程式碼儲存為個別檔案，以用作本教學課程的 CloudFormation 範本。

------
#### [ 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'
```

------