

# IAM 教程：使用 CloudFormation 模板创建 SAML 联合 IAM 角色
<a name="tutorial_saml-federated-role"></a>

当 AWS 账户中配置了现有 SAML 身份提供者 (IdP) 时，可创建信任该 IdP 的联合 IAM 角色。本教程展示如何使用 CloudFormation 模板，创建通过外部 IdP 进行身份验证的用户可代入的 SAML 联合 IAM 角色。

该模板使用允许 SAML IdP 代入该角色的信任策略创建联合 IAM 角色。经外部 IdP 进行身份验证的用户可以代入此角色，根据附加至该角色的权限访问 AWS 资源。

所部署的资源包括以下部分：
+ 信任现有 SAML IdP 的联合 IAM 角色。
+ 可配置的托管策略，可以附加到该角色以授予特定权限。
+ 可选的权限边界和会话持续时间设置。

## 先决条件
<a name="tutorial_saml-federated-role-prereqs"></a>

本教程假定您已准备好以下各项：
+ AWS 账户中配置的现有 SAML IdP。如果还没有，可以使用 [IAM 教程：使用 CloudFormation 模板创建 SAML 身份提供者 (IdP)](tutorial_saml-idp.md) 教程创建一个。
+ SAML IdP 的 ARN，需要在创建堆栈时将其指定为参数。
+ 本地计算机上安装了 Python 3.6 或更高版本，可运行本教程中用于对 IdP 的 SAML 元数据 XML 文件进行格式化的 Python 命令。

## 使用 CloudFormation 创建 SAML 联合角色
<a name="tutorial_saml-federated-role-create"></a>

要创建 SAML 联合角色，您需要创建 CloudFormation 模板，并使用此模板创建包含该角色的堆栈。

### 创建模板
<a name="tutorial_saml-federated-role-file"></a>

首先，创建 CloudFormation 模板。

1. 在 [模板](#tutorial_saml-federated-role-template) 部分中，单击 **JSON** 或 **YAML** 选项卡上的复制图标，以复制模板内容。

1. 将模板内容粘贴到新文件中。

1. 将该文件保存在本地。

### 创建堆栈
<a name="tutorial_saml-federated-role-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. 对于 **SAMLProviderARN**，请输入现有 SAML IdP 的 ARN。其格式应为 `arn:aws:iam::123456789012:saml-provider/YourProviderName`。

      示例：`arn:aws:iam::123456789012:saml-provider/CompanyIdP`
**注意**  
如果使用 [IAM 教程：使用 CloudFormation 模板创建 SAML 身份提供者 (IdP)](tutorial_saml-idp.md) 教程创建了 SAML IdP，则可以在该 CloudFormation 堆栈的“输出”选项卡中找到提供者 ARN。

   1. 对于 **RoleName**，可将其留空以根据堆栈名称自动生成名称，也可以为 IAM 角色输入自定义名称。

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

   1. 对于其他参数，请接受默认值或根据需要输入自己的值：
      + **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`

   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`，如果使用自动生成的名称）。

在配置 IdP 以发送相应 SAML 属性进行角色代入时，需要此角色 ARN。

## 测试 SAML 联合角色
<a name="tutorial_saml-federated-role-using"></a>

一旦创建 SAML 联合角色后，可验证其配置并测试联合身份验证设置。

1. 通过以下网址打开 IAM 控制台：[https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)。

1. 在导航窗格中，选择**角色**。

1. 找到并选择新创建的联合角色。

   如果提供了自定义角色名称，请查找该名称。如果将 RoleName 参数留空，则该角色将拥有一个基于堆栈名称和唯一标识符的自动生成名称。

1. 选择**信任关系**选项卡，查看信任策略。

   信任策略应表明在 SAML 受众 (`SAML:aud`) 匹配 `https://signin.aws.amazon.com/saml` 的条件下，信任您的 SAML IdP 代入此角色。

1. 选择**权限**选项卡，查看附加的策略。

   您可以看到在创建过程中附加至该角色的任何托管策略。

1. 记下角色摘要页面上显示的**角色 ARN**。

   您需要此 ARN 来配置外部 IdP，以允许用户代入此角色。

您的 SAML 联合角色已准备好投入使用。将外部 IdP 配置为在 SAML 断言中包含此角色的 ARN，经过身份验证的用户将能够代入此角色来访问 AWS 资源。

## 清理：删除资源
<a name="tutorial_saml-federated-role-delete"></a>

最后一步，您将删除该堆栈及其包含的资源。

1. 打开 CloudFormation 控制台。

1. 在**堆栈**页面上，选择根据该模板创建的堆栈，选择**删除**，然后确认**删除**。

   CloudFormation 启动删除堆栈及其包含的所有资源。

## CloudFormation 模板详细信息
<a name="tutorial_saml-federated-role-template-details"></a>

### 资源
<a name="tutorial_saml-federated-role-template-resources"></a>

本教程的 CloudFormation 模板将在您的账户中创建以下资源：
+ [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 角色。

### 配置
<a name="tutorial_saml-federated-role-configuration"></a>

模板包含如下可配置参数：
+ **RoleName** - IAM 角色的名称（自动生成的名称请留空）
+ **SAMLProviderARN** - SAML IdP 的 ARN（必需）
+ **RoleSessionDuration** - 最大会话持续时间，以秒为单位（3600-43200，默认为 7200）
+ **RolePermissionsBoundary** - 权限边界策略的可选 ARN
+ **RolePath** - IAM 角色的路径（默认为 /）
+ **ManagedPolicy1-5** - 最多 5 个待附加托管策略的可选 ARN

## CloudFormation 模板
<a name="tutorial_saml-federated-role-template"></a>

将以下 JSON 或 YAML 代码另存为单独文件，以用作本教程的 CloudFormation 模板。

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

------