December 2020 I wrote about custom Cloudformation resource types. Back then I finished with a thought of resource type that could get attributes of resources regardless how they were created or maintained. I neved got back to this topic, until recently my colleague notified me there is now such a thing added into Cloudformation Community Registry Extensions.

Or maybe, instead of creating separate types for each use-case, have a generic data provider resource to get attributes of any AWS resource needed.

Cloudformation Community Registry Extensions

Cloudformation Community Registry Extensions is a GitHub repository hosting custom Cloudformaton resource types, modules and hooks, published under AwsCommunity:: namespace. All production level extensions are available from public registry, but you must enable them before use. You can list all available community extensions and status if they are enabled or not, with aws cloudformation list-types command.

$ aws cloudformation list-types --output text --visibility PUBLIC \
  --filter TypeNamePrefix=AwsCommunity:: \
  --query 'TypeSummaries[].[IsActivated,Type,TypeName]'

False   HOOK            AwsCommunity::EC2::SecurityGroupRestrictedSSH
False   HOOK            AwsCommunity::S3::BucketVersioningEnabled
False   HOOK            AwsCommunity::S3::PublicAccessControlsRestricted
False   MODULE          AwsCommunity::CloudFront::S3Website::MODULE
False   MODULE          AwsCommunity::S3::Bucket::MODULE
False   RESOURCE        AwsCommunity::Account::AlternateContact
False   RESOURCE        AwsCommunity::DynamoDB::Item
False   RESOURCE        AwsCommunity::S3::DeleteBucketContents
False   RESOURCE        AwsCommunity::Time::Offset
False   RESOURCE        AwsCommunity::Time::Sleep
False   RESOURCE        AwsCommunity::Time::Static

Unfortunately the list doesn’t have yet AwsCommunity::Resource::Lookup as it isn’t production version yet. Good news is, I can register this myself, as private extension, and the lookup resource comes with rather good documentation how to build and register it.

Building and registering

“Usage walkthrough” -chapter in README.md has good step-by-step instructions how you install Resource::Lookup as your private extension. Before you start you should have the following prereqs installed.

Installing Lookup resource itself has 3 simple steps.

  • Creating a role for custom resource. By default this is AWS managed ReadOnlyAccess to allow lookup to query all your resources. If you have some more limited use-case in mind, take examples/example-resource-lookup-role.template as your baseline and adjust policy as needed.

  • Building the resource type. This is as simple as saying mvn clean verify. I’m not a Java programmer and for some reason, Maven build didn’t work for me out-of-the-box until commenting out spotbugs-maven-plugin from pom.xml. Your mileage may vary.

  • Once you have build the resource it needs to be deployed and registered with cfn submit --set-default --region REGION. If you plan to use this on multiple regions, you should do the same for each region.

You can now verify Resource::Lookup private extension is available on your account.

$ aws cloudformation list-types --visibility PRIVATE --filter TypeNamePrefix=AwsCommunity::Resource::Lookup
{
"TypeSummaries": [
    {
        "Type": "RESOURCE",
        "TypeName": "AwsCommunity::Resource::Lookup",
        "DefaultVersionId": "00000001",
        "TypeArn": "arn:aws:cloudformation:eu-central-1:123456789012:type/resource/AwsCommunity-Resource-Lookup",
        "LastUpdated": "2023-04-19T12:03:50.415000+00:00",
        "Description": "This resource uses the `ListResources` and `GetResource` actions of AWS Cloud Control API to perform a lookup of a resource of a given type (such as, `AWS::EC2::VPC`) in your AWS account -and current region if you are using a regional AWS service- based on a query you specify.  If only one match is found, this resource returns the primary identifier of the resource (in the `AWS::EC2::VPC` example, the ID of the VPC), that you can then consume by referencing it in your template with the `Fn::GetAtt` intrinsic function.  Note: as this resource type uses Cloud Control API, you can specify resource type search targets -like `AWS::EC2::VPC`- that are supported by Cloud Control API; for more information, see `Determining if a resource type supports Cloud Control API`: https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/resource-types.html#resource-types-determine-support ."
    }
]
}

And then you are ready to test it in action.

Using Resource::Lookup in templates

To test out what I missed or misunderstood from documentation I did 2 simple use-cases to lookup resources not typically part of application stacks.

Mapping Route53 hosted zone name to ID

When you want to register a DNS record and get matching certificate for your ALB you can do this using AWS::Route53::HostedZone::Id as reference to R53 zone. However it would be more human friendly if you could just state the zone name instead. Unfortunately you must have a matching pair of name and id to do this. You can read the back story from Devil in Details.

Using AwsCommunity::Resource::Lookup you can input only the R53 zone name and then resolve matching zone id. Having just one input parameter makes it sure you zone name and id are always matching pair.

Below is how lookup for zone name would be. Note there is a dot added to the end of zone name automatically for the lookup JmesPathQuery.

ZoneLookup:
    Type: AwsCommunity::Resource::Lookup
    Properties:
        JmesPathQuery: !Sub "Name == '${R53Zone}.'" <-- NOTE THE DOT
        ResourceLookupRoleArn: !Ref 'ResourceLookupRoleArn'
        TypeName: AWS::Route53::HostedZone

And then you can resolve matching zone id with GetAtt

!GetAtt ZoneLookup.ResourceIdentifier

Here is rewritten template of original demo using Resource::Lookup resolving ID from name.

VPC Lookup

Second test I wanted to do was to test VPC releated lookups. Use-case would have been to resolve VPC ID and CIDR from Name -tag. Simply resolving VPC (or subnet) ID from Name is useful, especially when deploying stack from command line, but there are also Parameter Types that help in picking the correct values and validating the inputs.

As Lookup is using GetAtt to resolve ResourceIdentifier, I somehow got the idea it would be possible to get other properties as well. Unfortunately it doesn’t work like that (nor it doesn’t claim in documentation this should be possible).

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Demo of Resource::Lookup
        VpcId: !GetAtt VPCLookup.ResourceIdentifier
        SecurityGroupIngress:
        - 
         CidrIp: !GetAtt VPCLookup.Properties.CidrBlock <-- THIS WONT WORK
         Description: Allow SSH from within the VPC
         IpProtocol: tcp
         FromPort: 22
         ToPort: 22

This was a bit of dissappointment. I was hoping I could treat all AWS resources as they were read-only resources of my stack. Getting the resource ID is a good start but having access to resource properties would enable many valuable use-cases.

$ aws cloudcontrol get-resource --type-name AWS::EC2::VPC \
--identifier vpc-deadbeef12345678 | jq '.ResourceDescription.Properties | fromjson'
{
  "VpcId": "vpc-deadbeef12345678",
  "InstanceTenancy": "default",
  "CidrBlockAssociations": [
    "vpc-cidr-assoc-deadbeef12345678"
  ],
  "CidrBlock": "10.0.0.0/21",
  "DefaultNetworkAcl": "acl-deadbeef12345678",
  "EnableDnsSupport": true,
  "Ipv6CidrBlocks": [],
  "DefaultSecurityGroup": "sg-deadbeef12345678",
  "EnableDnsHostnames": true,
  "Tags": [
    {
      "Value": "MyVPC",
      "Key": "Name"
    }
  ]
}

Summary

Cloudformation Community Registry Extensions has nice add-ons to cover some corner cases like setting alternate contacts for an account. Best thing is you only need to enable them, build and deployment is already taken care of. Once AwsCommunity::Resource::Lookup gets to it’s production version it will be available the same way as other extensions. For now you need to build and deploy it yourself, but that is rather simple to do.

Only setback I found was the inability to access resource properties. If you have some Java coding skills and would want to extend it to cover resource properties, I’m sure the pull request would be welcomed ;-)