aws-cloudformation / cloudformation-coverage-roadmap

The AWS CloudFormation Public Coverage Roadmap
https://aws.amazon.com/cloudformation/
Creative Commons Attribution Share Alike 4.0 International
1.11k stars 56 forks source link

AWS::RDS::DBCluster - DBClusterResourceId accessible via Fn::GetAtt #192

Closed dan-lind closed 2 years ago

dan-lind commented 5 years ago

Scope of request

New option for an existing attribute is desired

Expected behavior

Fn::GetAtt on a AWS::RDS::DBCluster resource returns DBClusterResourceId for the resource

Helpful Links to speed up research and evaluation

IAM Database Authentication https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html

IAM Policy for IAM Database Access https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.html

Category (required)

DB (RDS, DynamoDB...)

Any additional context (optional)

IAM Database Authentication requires us to create an IAM role, as per the link above. The resource ARN of the user policy needs to follow this pattern

arn:aws:rds-db:region:account-id:dbuser:DbClusterResourceId/db-user-name

Creating a role in CloudFormation is easy, however there is currently no way to extract DbClusterResourceId from a AWS::RDS::DBCluster resource, meaning we have to resort to custom resources.

ScriptAutomate commented 4 years ago

Because of this limitation, it looks like people have to resort to some workarounds as seen here until CFN supports it: https://stackoverflow.com/questions/42770067/how-do-you-associate-a-iam-role-with-an-aurora-cluster-using-cloudformation

ScriptAutomate commented 4 years ago

Actually, this looks to be remedied with an update that came out back in August (as mentioned in an update to the StackOverflow answer):

As of August 29, 2019 this is finally supported! There is a new attribute named 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.

AssociatedRoles seems to be what you are looking for, as a property of the AWS::RDS::DBCluster resource type, which takes a list of the DBClusterRole property type.

This looks like you would need your CFN to create the IAM Role(s) first, and then reference them in AWS::RDS::DBCluster

dan-lind commented 4 years ago

Actually, this looks to be remedied with an update that came out back in August (as mentioned in an update to the StackOverflow answer):

As of August 29, 2019 this is finally supported! There is a new attribute named 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.

AssociatedRoles seems to be what you are looking for, as a property of the AWS::RDS::DBCluster resource type, which takes a list of the DBClusterRole property type.

This looks like you would need your CFN to create the IAM Role(s) first, and then reference them in AWS::RDS::DBCluster

This is not the same thing. The description of AssociatedRoles reads "Provides a list of the AWS Identity and Access Management (IAM) roles that are associated with the DB cluster. IAM roles that are associated with a DB cluster grant permission for the DB cluster to access other AWS services on your behalf."

What I'm talking about is the opposite; creating roles that allows e.g. a Lambda to connect to an RDS instance using IAM Authentication. You need the DBClusterResourceId to be able to create that role.

revmischa commented 4 years ago

I would like to get the cluster ARN for use with the aurora data API

jplock commented 4 years ago

@revmischa for the data API, you can recreate the ARN if you provide the DBClusterIdentifier property like https://github.com/smoketurner/serverless-vpc-plugin/blob/master/example/serverless.yml#L59

kuanhung-chen commented 4 years ago

I believe in most cases we need CF return RDS cluster ARN, but in RDS IAM authentication case, we need CF able to return DBClusterResourceId, so that we could create IAM role for that RDS resource. Its really inconvenient and tricky that RDS IAM authentication require DBClusterResourceId instead of the common cluster ARN.

https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html To allow an IAM user or role to connect to your DB cluster, you must create an IAM policy. After that, you attach the policy to an IAM user or role.

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-db:connect" ], "Resource": [ "arn:aws:rds-db:us-east-2:1234567890:dbuser:cluster-ABCDEFGHIJKL01234/db_user" ] } ] }

Noticed that in sample rule above, currently there is no way to have "DBClusterResourceId", cluster-ABCDEFGHIJKL01234/ via CF....

dan-lind commented 4 years ago

Workaround to get the DB Clusters ARN in CloudFormation:

    Value:  
      Fn::Join:  
        - ""  
        - - !Sub "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:cluster:"  
          - !Select [0, !Split ['.', !GetAtt DBCluster.Endpoint.Address]]. 

Not relevant here. DBClusterResourceId (which is what this request is about) and DB cluster ARN is not the same thing

thkapasi commented 4 years ago

Workaround to get the DB Clusters ARN in CloudFormation:

    Value:  
      Fn::Join:  
        - ""  
        - - !Sub "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:cluster:"  
          - !Select [0, !Split ['.', !GetAtt DBCluster.Endpoint.Address]]. 

Not revlevant here. DBClusterResourceId (which is what this request is about) and DB cluster ARN is not the same thing

My bad - deleted comment

BenSmith commented 4 years ago

Please add this.

qoomon commented 4 years ago

We desperately need this too.

mmahmood commented 4 years ago

Much needed. Thanks!

marcossantiago commented 4 years ago

As a workaround I had to create a custom resource to retrieve this information:

Resources:
  CustomLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: RDSDescribe
          PolicyDocument:
            Version: 2012-10-17
            Statement:
            - Effect: Allow
              Action:
                - rds:DescribeDBClusters
              Resource: '*'

  RDSUtilsLambda:
    Type: 'AWS::Lambda::Function'
    DeletionPolicy: Delete
    Properties:
      Code:
        ZipFile: |
          import boto3
          import os
          import cfnresponse
          region = os.environ['AWS_REGION']
          client = boto3.client('rds', region_name=region)
          def handler(event, context):
              responseData = {}
              try:
                rds_cluster = event['ResourceProperties']['rdsCluster']
                clusters = client.describe_db_clusters(DBClusterIdentifier=rds_cluster)
                responseData['DbClusterResourceId'] = clusters['DBClusters'][0]['DbClusterResourceId'] # Returns Cluster Resource Id
                cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
              except Exception as e:
                cfnresponse.send(event, context, cfnresponse.FAILED, responseData)
      Handler: index.handler
      Runtime: python3.7
      MemorySize: 128
      Role: !GetAtt CustomLambdaRole.Arn
      Timeout: 30

  ClusterResourceIdOutput:
    Type: Custom::RDSClusterResourceIdOutput
    Properties:
      rdsCluster: !Ref RDSCluster
      ServiceToken: !GetAtt RDSUtilsLambda.Arn
      Region: !Ref "AWS::Region"

  UserIAMRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          -
            Effect: Allow
            Principal:
              Service: 
                - "ec2.amazonaws.com"
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: IAMDbAuthentication
          PolicyDocument:
            Version: 2012-10-17
            Statement:
            - Effect: Allow
              Action:
                - rds-db:connect
              Resource:
                - !Sub "arn:aws:rds-db:${AWS::Region}:${AWS::AccountId}:dbuser:${ClusterResourceIdOutput.DbClusterResourceId}/my_nice_user"
jebcolesd commented 3 years ago

It's two years since the initial request and a property that is easily available in AWS' APIs and necessary for a common configuration is still not available in cloudformation. Perhaps Cloudformation should allow a custom resource that returns the string output of a pseudo aws cli call - so that we can solve these problems ourselves without the convoluted (but inspired) workaround marcossantiago posted above.

ghost commented 3 years ago

I put in a pull request some time ago which I believe resolves this. There was a comment from the maintainers, but I don't think it's relevant because of how the code was written in the first place. If anyone can confirm that it works as is, tell me what needs to change, or make any other suggestions for the code, please do! It'd be great to get it merged in.

iainb123 commented 2 years ago

This is important if you're trying to set up IAM authentication with RDS clusters through CloudFormation.

@kara-uoy seems to have done all the work (see above), and it just needs to be reviewed. What's going on?

karaken12 commented 2 years ago

Updated the previous PR, as there had been some changes to the code in the meantime: https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-rds/pull/296

kabo commented 2 years ago

Can confirm this works now, I'm able to do

          - Action: rds-db:connect
            Effect: Allow
            Resource: !Sub "arn:aws:rds-db:eu-west-1:112233445566:dbuser:${MyDb.DBClusterResourceId}/myUser"