您如何使用 CloudFormation 将 IAM 角色与 Aurora 集群相关联?

How do you associate a IAM Role with an Aurora Cluster using CloudFormation?

按照找到的说明 here,我创建了以下 IAM 角色

"DatabaseS3Role": {
    "Type": "AWS::IAM::Role",
    "Properties": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": ["rds.amazonaws.com"]
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        },
        "Policies": [
            {
                "PolicyName": "AllowAuroraToReadS3",
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": ["s3:GetObject", "s3:GetObjectVersion", "s3:ListBucket"],
                            "Resource": {"Fn::Join": ["", [
                                "arn:aws:s3:::",
                                {"Fn::Join": ["-",[
                                    {"Ref": "ClientName"}, 
                                    {"Ref": "SourceBucketName"},
                                    {"Ref": "EnvironmentType"},
                                    { "Fn::FindInMap" : [ "Regions", { "Ref" : "AWS::Region" }, "Name" ] }
                                ]]} ,
                                "*"
                            ]]}
                        }
                    ]
                }
            }
        ]
    }
}

我可以将它添加到集群参数组并使用以下方法关联它。

"RDSDBClusterParameterGroup" : {
    "DependsOn": "DatabaseS3Role",
    "Type": "AWS::RDS::DBClusterParameterGroup",
    "Properties" : {
        "Description" : "CloudFormation Aurora Cluster Parameter Group",
        "Family" : "aurora5.6",
        "Parameters" : {
            "time_zone" : "US/Eastern",
            "aws_default_s3_role": {"Fn::GetAtt": ["DatabaseS3Role", "Arn"]}
        }
    }
},
"RDSAuroraCluster" : {
    "Type" : "AWS::RDS::DBCluster",
    "Properties" : {
        "MasterUsername" : { "Ref" : "Username" },
        "MasterUserPassword" : { "Ref" : "Password" },
        "Engine" : "aurora",
        "DBSubnetGroupName" : { "Ref" : "RDSSubnetGroup" },
        "DBClusterParameterGroupName" : { "Ref" : "RDSDBClusterParameterGroup" },
        "VpcSecurityGroupIds" : [ { "Ref" : "SecurityGroupId" } ],
        "Tags" : [
              { "Key" : "Name", "Value" : { "Fn::Join" : [ "-", [ 
              { "Ref" : "ClientName" }, 
              "aurclstr001",
              {"Ref" : "EnvironmentType" },
              { "Fn::FindInMap" : [ "Regions", { "Ref" : "AWS::Region" }, "Name" ] }
          ] ] } }
        ]
    }
}

但是 Aurora 仍然无法连接到 S3,除非我通过控制台或使用 cli 命令 add-role-to-db-cluster 手动将角色与集群相关联。

通过云形成文档进行挖掘并没有提供通过模板执行此操作的任何方法。 This documentation 没有提供任何允许关联角色的参数。

如何在不向部署过程添加手动步骤的情况下执行此操作?

August 29, 2019 开始,终于支持了!

有一个名为 AssociatedRoles that takes an array of DBClusterRoles. These are basically an object with a RoleArn and an optional FeatureName which can currently only be s3Import per this reference showing SupportedFeatureNames.member.N 的新属性。

2017-06-30 的原始答案:

这不是一个很好的解决方案,但我决定在输出中生成 运行 所需的命令。我将向 Amazon 提出支持请求,以确认无法通过 DSL 向集群添加角色。

当我 运行 aws rds describe-db-clusters 时,我看到 "AssociatedRoles" 的一个条目,其中包含一组具有 Status 和 RoleArn 的对象。

PostRunCommand:
  Description: You must run this awscli command after the stack is created and may also need to reboot the cluster/instance.
  Value: !Join [" ", [
    "aws rds add-role-to-db-cluster --db-cluster-identifier",
    !Ref AuroraSandboxCluster,
    "--role-arn",
    !GetAtt AuroraS3Role.Arn,
    "--profile",
    !FindInMap [ AccountNameMap, !Ref AccountNamespace, profile ]
  ]]

您很可能不需要 WRT 配置文件的最后一部分...

亚马逊回复我后的跟进。他们说:

I understand that you were looking for a way to associate an IAM role with an Aurora cluster in Cloudformation to access other AWS services on your behalf.

As you correctly state there's no role property for a RDS cluster resource as CloudFormation does not support it yet. There's an already open feature request for this as it's a very common issue and I have added your voice to it to add the feature with even more weight. As usual, I can't provide you with an ETA, however as soon as it's released it should be published in the Cloudformation release history page:

http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/ReleaseHistory.html

However you can create a CFN Template which creates a RDS Aurora setup and in the end of the template a Custom Resource 1, as a Lambda Function, which makes an API call to attach the IAM Role to the RDS Cluster, this way the whole RDS Aurora Cluster setup stay centralized inside the CFN Template without manual actions and the cluster will be able to invoke the Lambda Function.

Please find attached an "example" template of this workaround described above.

I will as well send a feedback on your behalf about the lack of the principal property in the examples which is needed to create a Role to Delegate Permissions to an AWS Service.

{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS Cloud resources for DevTools services.",
"Metadata": {
    "Version": "0.2.0"
},
"Parameters": {
    "RDSPassword": {
        "Description": "Password for root user on the RDS instance.",
        "Type": "String",
        "NoEcho":"true"
    },
    "RDSDatabaseName": {
        "Description": "DB Identifier for RDS instance.",
        "Type": "String",
        "Default": "mydbname"
    },
    "RDSClass": {
        "Description": "RDS Instance Class",
        "Type": "String",
        "Default": "db.r3.xlarge"
    },
    "DBIdentifier": {
        "Description": "Database Instance Identifier",
        "Type": "String"
    },
    "DBSubnetGroupName": {
        "Description": " The Subnet group Group for the RDS instance",
        "Type": "String"
    },
    "RDSSecurityGroupId": {
        "Description": "Existing internal SG for RDS instance access",
        "Type": "AWS::EC2::SecurityGroup::Id"
    },
    "RDSRetention": {
        "Description": "How long to retain RDS snapshots",
        "Type": "String"
    },
    "RDSVpcId": {
        "Description": "VpcId for RDS instance",
        "Type": "AWS::EC2::VPC::Id"
    },
    "PubliclyAccessible": {
        "Description": "Set the RDS to be publically available",
        "Type": "String",
        "AllowedValues" : ["true", "false"],
        "Default": "true"
    },
    "DBClusterIdentifier": {
        "Description": "The name of the DBCluster",
        "Type": "String"
    },
    "RDSRoleTag": {
        "Description": "sets if the tag for dev/prod use",
        "Type": "String",
        "Default": "dev"
    }
},

"Resources": {

  "LambdaRole" : {
    "Type" : "AWS::IAM::Role",
    "Properties" : {
        "AssumeRolePolicyDocument" : {
            "Version" : "2012-10-17",
            "Statement" : [
                {
                    "Effect" : "Allow",
                    "Principal" : {
                        "Service" : [
                            "lambda.amazonaws.com"
                        ]
                    },
                    "Action"    : [
                        "sts:AssumeRole"
                    ]
                }
            ]
        }
      }
    },

    "LambdaPolicy": {
      "Type" : "AWS::IAM::Policy",
      "Properties" : {
         "PolicyName" : "LambdaPolicy",
         "PolicyDocument" : {
            "Version" : "2012-10-17",
            "Statement": [ {
            "Effect"   : "Allow",
            "Action"   : [
               "iam:*",
               "ec2:*",
               "rds:*",
               "logs:*"
            ],
            "Resource" : "*"
            } ]
         },
         "Roles": [ { "Ref": "LambdaRole" } ]
      }
   },

    "LambdaFunction": {
      "Type" : "AWS::Lambda::Function",
      "DeletionPolicy" : "Delete",
      "DependsOn"      : [
          "LambdaRole"
      ],
      "Properties"     : {
          "Code" : {
              "ZipFile" : {
                  "Fn::Join" : [
                      "\n",
                      [
                        "          var AWS = require('aws-sdk');",
                        "          var rds = new AWS.RDS();",
                        "          var response = require('cfn-response');",
                        "          exports.handler = (event, context, callback) => {",
                        "              var rolearn = event.ResourceProperties.RDSRole;",
                        "              var dbclusteridentifier = event.ResourceProperties.DBClusterIdentifier;",
                        "              var responseData = {};",
                        "              console.log('Role ARN: ' + rolearn);",
                        "              console.log('DBClusterIdentifier: ' + dbclusteridentifier);",
                        "              var addroleparams = {",
                        "                  RoleArn: rolearn,",
                        "                  DBClusterIdentifier: dbclusteridentifier",
                        "                };",
                        "                if (event.RequestType == 'Delete') {",
                        "                  response.send(event, context, response.SUCCESS);",
                        "                  return;",
                        "                }",
                        "                rds.addRoleToDBCluster(addroleparams, function(err, data) {",
                        "                  if (err) {",
                        "                  console.log(err, err.stack); // an error occurred",
                        "                  responseData = {Error: 'Create call failed'};",
                        "                  response.send(event, context, response.FAILED, responseData);",
                        "                  }",
                        "                  else {",
                        "                  response.send(event, context, response.SUCCESS, responseData);",
                        "                  console.log(data);  // successful response",
                        "                  }",
                        "                });",
                        "          };",
                      ]
                  ]
              }
          },
          "Handler" : "index.handler",
          "MemorySize" : 128,
          "Role"       : {
              "Fn::GetAtt" : [
                  "LambdaRole",
                  "Arn"
              ]
          },
          "Runtime"    : "nodejs4.3",
          "Timeout"    : 10
      }
    },

    "RDSRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": ["rds.amazonaws.com"]
              },
              "Action": ["sts:AssumeRole"]
            }
          ]
        },
        "Path": "/"
      }
    },

    "RDSPolicy": {
       "Type" : "AWS::IAM::Policy",
       "Properties" : {
          "PolicyName" : "RDSPolicy",
          "PolicyDocument" : {
             "Version" : "2012-10-17",
             "Statement": [ {
             "Effect"   : "Allow",
             "Action"   : [
                "lambda:InvokeFunction"
             ],
             "Resource" : "*"
             } ]
          },
          "Roles": [ { "Ref": "RDSRole" } ]
       }
    },

    "RDSDBClusterParameterGroup" : {
      "Type" : "AWS::RDS::DBClusterParameterGroup",
      "Properties" : {
        "Parameters" : {
          "aws_default_lambda_role" : { "Fn::GetAtt" : [ "LambdaFunction", "Arn" ] }

        },
        "Family" : "aurora5.6",
        "Description" : "A sample parameter group"
      }
    },

    "RDSDBCluster": {
        "Type" : "AWS::RDS::DBCluster",
        "DeletionPolicy": "Retain",
        "Properties" : {
            "BackupRetentionPeriod" : { "Ref": "RDSRetention" },
            "DatabaseName": { "Ref": "RDSDatabaseName" },
            "DBSubnetGroupName": { "Ref": "DBSubnetGroupName" },
            "DBClusterParameterGroupName": { "Ref" : "RDSDBClusterParameterGroup" },
            "Engine" : "aurora",
            "StorageEncrypted" : true,
            "MasterUsername" : "sa",
            "MasterUserPassword" : { "Ref": "RDSPassword" },
            "Port" : 3306,
            "Tags": [
                { "Key": "Role", "Value": { "Ref": "RDSRoleTag" } }
                ],
            "VpcSecurityGroupIds": [{ "Ref": "RDSSecurityGroupId" } ]
        }
    },
    "RDSInstance": {
        "Type": "AWS::RDS::DBInstance",
        "DeletionPolicy": "Retain",
        "Properties": {
            "AllowMajorVersionUpgrade": false,
            "AutoMinorVersionUpgrade": true,
            "DBClusterIdentifier" : { "Ref": "RDSDBCluster" },
            "DBInstanceIdentifier": { "Ref": "DBIdentifier" },
            "DBInstanceClass": { "Ref": "RDSClass" },
            "Engine": "aurora",
            "PubliclyAccessible": { "Ref": "PubliclyAccessible" },
            "Tags": [
                { "Key": "Role", "Value": { "Ref": "RDSRoleTag" } }
                ]
        }
    },
    "RDSInstanceSecurityGroup": {
        "Type": "AWS::EC2::SecurityGroup",
        "DeletionPolicy": "Retain",
        "Properties": {
            "GroupDescription": "Security group for the RDSInstance resource",
            "SecurityGroupEgress": [
                {
                    "IpProtocol": "tcp",
                    "CidrIp": "127.0.0.1/32",
                    "FromPort": "1",
                    "ToPort": "1"
                }
            ],
            "SecurityGroupIngress": [
                {
                    "IpProtocol": "tcp",
                    "SourceSecurityGroupId": { "Ref": "RDSSecurityGroupId" },
                    "FromPort": "3306",
                    "ToPort": "3306"
                }
            ],
            "VpcId": { "Ref": "RDSVpcId" },
            "Tags": [
                { "Key": "Role", "Value": { "Ref": "RDSRoleTag" } }
                ]
        }
    },

    "AddRoleToDBCluster": {
        "DependsOn" : [
            "RDSDBCluster",
            "RDSInstance"
        ],
        "Type": "Custom::AddRoleToDBCluster",
        "Properties" : {
            "ServiceToken" : {
                "Fn::GetAtt" : [
                    "LambdaFunction",
                    "Arn"
                ]
            },
            "RDSRole" : { "Fn::GetAtt" : [ "RDSRole", "Arn" ] },
            "DBClusterIdentifier" : {"Ref":"RDSDBCluster"}
        }
    }
}

}