Cloudformation-coverage-roadmap: AWS::RDS::DBInstance-DbiResourceId accessible via Fn::GetAtt

Created on 6 Aug 2019  路  13Comments  路  Source: aws-cloudformation/cloudformation-coverage-roadmap

Instructions for CloudFormation Coverage New Issues Template

  1. Title -> AWS::RDS::DBInstance-DbiResourceId accessible via Fn::GetAtt
  2. Scope of request -> Make the DbiResourceId accessible using Fn::GetAtt on an AWS::RDS::DBInstance resource.
  3. Expected behavior -> Get back the DbiResourceId.
  4. Test case recommendation (optional) ->
  5. Links to existing API doc (optional) ->
  6. Category tag (optional) -> Database
  7. Any additional context (optional)

The RDS instances now supports IAM authentication as described here. Unfortunately in the PolicyDocument you have to use the DbiResourceId as the reference to the target RDS instance. This is not known before and you can't query it from the CFn either. Currently I have to create the stack, fetch the DbiResourceId and then create a changeset to modify the Policy. It would so much simpler if we can do that in one step.

"DBInstance": {
  "Type": "AWS::RDS::DBInstance",
   "Properties": { }
},
"DBUserPolicy": {
  "Type": "AWS::IAM::ManagedPolicy",
  "Properties": {
      "Description": "blah blah not relevant",
      "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Action": [  "rds-db:connect" ],
            "Effect": "Allow",
            "Resource": [
              { "Fn::Sub": "arn:aws:rds-db:${AWS::Region}:${AWS::AccountId}:dbuser:db-KOUCHAID5AIS8GAN6XEIKOOTIZ/db_user_name" }
            ]
          }
        ]
      }
    }       
  }
}

I would like to change the Resource entry like this:

"Fn::Sub": [
"arn:aws:rds-db:${AWS::Region}:${AWS::AccountId}:dbuser:${DbiResourceId}/db_user_name",
{ "DbiResourceId": { "Fn::GetAtt": [ "DbInstance", "DbiResourceId" ] } } ]
database enhancement

Most helpful comment

@newtondev @andrius-paurys @SamMcFaddenBJSS

If you react with the 馃憤 button to the original issue, (the first comment, click on the smiley face if you're the first reacting), your votes can be used to sort issues and determine priorities.

A comment will send a notification to everyone (participants and watchers), but cannot be easily counted as a vote for an issue. Thus It's generally better to vote than to comment with "+1". To keep up to date, you can also add yourself as a watcher.

All 13 comments

Any news?

This is a daft omission. The fact that you can enable IAM authentication via CFN but can't get the resource ID to setup an IAM policy for it is absurd. Why was this not included in the CFN update providing for enabling IAM authentication in the first place?

Come on. This was raised nearly 10 months ago. We're not asking for much...

Its really absurd, this issue is nearly 10 months old and nobody seems to care about this issue. The feature is completely useless i am not able to fetch the db-resource-id inside the cloudformation template.

Any news? Are we not taking it as valuable feature to have?

I would also like an update on this.

Totally agree, it is absurd. I meditated on the architecture for a project for two days with IAM authorization in mind to discover, that can't automate policy creation with CF. Several workarounds are definitely possible here, but it will inevitably break "encapsulation". Hope this will be implemented soon.

DbiResourceId should have been available from the start.
I keep coming back to this Github issue every few months when I have the same headache for a different project..
Strong +1 for this to be added to enhancement!

Here's a snippet from our template to lookup the DbiResourceId via a CustomResource:

    "DBResourceId": {
      "Properties": {
        "DBInstanceIdentifier": {
          "Ref": "Instance"
        },
        "ServiceToken": {
          "Fn::GetAtt": [
            "DBResourceIdLookupLambda",
            "Arn"
          ]
        }
      },
      "Type": "AWS::CloudFormation::CustomResource"
    },
    "DBResourceIdLookupLambda": {
      "Properties": {
        "Code": {
          "ZipFile": "import logging\nimport boto3\nimport cfnresponse\n\nlog = logging.getLogger(__name__)\nlog.setLevel(logging.INFO)\n\n\ndef db_resource_name(instance_identifier: str):\n    rds = boto3.client(\"rds\")\n    return rds.describe_db_instances(DBInstanceIdentifier=instance_identifier)[\n        \"DBInstances\"\n    ][0][\"DbiResourceId\"]\n\n\ndef lambda_handler(event: dict, context) -> None:\n    physical_resource_id = None\n    if \"DBInstanceIdentifier\" not in event[\"ResourceProperties\"]:\n        log.error(\"DBInstanceIdentifier not provided in ResourceProperties\")\n        status = cfnresponse.FAILED\n    elif event[\"RequestType\"] == \"Delete\":\n        log.info(\"Stack is deleting. Skipping ID lookup.\")\n        status = cfnresponse.SUCCESS\n    else:\n        instance_identifier = event[\"ResourceProperties\"][\"DBInstanceIdentifier\"]\n        try:\n            physical_resource_id = db_resource_name(instance_identifier)\n            status = cfnresponse.SUCCESS\n        except Exception:\n            log.error(\n                \"Error looking up DB instance identifier for '%s'\",\n                instance_identifier,\n                exc_info=True,\n            )\n            status = cfnresponse.FAILED\n\n    cfnresponse.send(event, context, status, {}, physical_resource_id)\n"
        },
        "Handler": "index.lambda_handler",
        "Role": {
          "Fn::GetAtt": [
            "DBResourceIdLookupRole",
            "Arn"
          ]
        },
        "Runtime": "python3.7",
        "Timeout": 10
      },
      "Type": "AWS::Lambda::Function"
    },
    "DBResourceIdLookupRole": {
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                "Service": "lambda.amazonaws.com"
              },
              "Sid": ""
            }
          ],
          "Version": "2012-10-17"
        },
        "ManagedPolicyArns": [
          "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        ],
        "Policies": [
          {
            "PolicyDocument": {
              "Statement": [
                {
                  "Action": "rds:DescribeDBInstances",
                  "Effect": "Allow",
                  "Resource": [
                    {
                      "Fn::Sub": "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:db:${Instance}"
                    },
                    {
                      "Fn::If": [
                        "IsMultiAz",
                        {
                          "Fn::Sub": "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:db:${ReplicaInstance}"
                        },
                        {
                          "Ref": "AWS::NoValue"
                        }
                      ]
                    }
                  ],
                  "Sid": ""
                }
              ],
              "Version": "2012-10-17"
            },
            "PolicyName": {
              "Fn::Sub": "describe-db-instance"
            }
          }
        ]
      },
      "Type": "AWS::IAM::Role"
    },
    "DBResourceIdReplica": {
      "Condition": "IsMultiAz",
      "Properties": {
        "DBInstanceIdentifier": {
          "Ref": "ReplicaInstance"
        },
        "ServiceToken": {
          "Fn::GetAtt": [
            "DBResourceIdLookupLambda",
            "Arn"
          ]
        }
      },
      "Type": "AWS::CloudFormation::CustomResource"
    }

The actual code of the lambda is:

import logging
import boto3
import cfnresponse

log = logging.getLogger(__name__)
log.setLevel(logging.INFO)


def db_resource_name(instance_identifier: str) -> str:
    rds = boto3.client("rds")
    return rds.describe_db_instances(DBInstanceIdentifier=instance_identifier)[
        "DBInstances"
    ][0]["DbiResourceId"]


def lambda_handler(event: dict, context) -> None:
    physical_resource_id = None
    if "DBInstanceIdentifier" not in event["ResourceProperties"]:
        log.error("DBInstanceIdentifier not provided in ResourceProperties")
        status = cfnresponse.FAILED
    elif event["RequestType"] == "Delete":
        log.info("Stack is deleting. Skipping ID lookup.")
        status = cfnresponse.SUCCESS
    else:
        instance_identifier = event["ResourceProperties"]["DBInstanceIdentifier"]
        try:
            physical_resource_id = db_resource_name(instance_identifier)
            status = cfnresponse.SUCCESS
        except Exception:
            log.error(
                "Error looking up DB instance identifier for '%s'",
                instance_identifier,
                exc_info=True,
            )
            status = cfnresponse.FAILED

    cfnresponse.send(event, context, status, {}, physical_resource_id)

+1

+1

+1

@newtondev @andrius-paurys @SamMcFaddenBJSS

If you react with the 馃憤 button to the original issue, (the first comment, click on the smiley face if you're the first reacting), your votes can be used to sort issues and determine priorities.

A comment will send a notification to everyone (participants and watchers), but cannot be easily counted as a vote for an issue. Thus It's generally better to vote than to comment with "+1". To keep up to date, you can also add yourself as a watcher.

+1

Was this page helpful?
0 / 5 - 0 ratings