serverless-tag-resources
Version:
Datamart: Tag all AWS resources with dual legacy + datamart:* tag support
97 lines (84 loc) • 3.7 kB
JavaScript
;
const {
CloudFormationClient,
DescribeStackResourcesCommand,
} = require("@aws-sdk/client-cloudformation");
const { getClient } = require("./aws-clients");
const { buildListTags, buildDictTags } = require("./tags");
const { needsPostDeployTagging, DICT_BASED_TYPES, API_ONLY_TYPES, RELATED_TYPES } = require("./resource-classifier");
const { tagSSMParameter } = require("./post-deploy/ssm");
const { tagPinpointApp } = require("./post-deploy/pinpoint");
const { tagApiGatewayV2 } = require("./post-deploy/apigatewayv2");
const { tagRDSCluster } = require("./post-deploy/rds");
const { tagFirehoseStream } = require("./post-deploy/firehose");
const { tagEC2RelatedResources } = require("./post-deploy/ec2-related");
/**
* Post-Deploy Tagger — Hook: after:deploy:deploy
*
* Tags resources that cannot be fully tagged in the CF template:
* dict-based, API-only, and related resources.
*/
async function updateTagsPostDeploy(config, stackName, stackTags, stage, partition, region, log) {
const cfnClient = getClient(CloudFormationClient, config);
const result = await cfnClient.send(
new DescribeStackResourcesCommand({ StackName: stackName })
);
const allResources = result.StackResources || [];
for (const resource of allResources) {
const type = resource.ResourceType;
if (!needsPostDeployTagging(type)) continue;
const logicalId = resource.LogicalResourceId;
const listTags = buildListTags(stackTags, stage, logicalId);
const dictTags = buildDictTags(stackTags, stage, logicalId);
try {
// Dict-based types: need API call because CF template tagging
// may not fully propagate for some of these types
if (DICT_BASED_TYPES.has(type)) {
switch (type) {
case "AWS::SSM::Parameter":
await tagSSMParameter(config, resource, listTags);
log(`TAGGING: post-deploy SSM Parameter ${logicalId}`);
break;
case "AWS::Pinpoint::App":
await tagPinpointApp(config, resource, dictTags);
log(`TAGGING: post-deploy Pinpoint App ${logicalId}`);
break;
case "AWS::ApiGatewayV2::Api":
case "AWS::ApiGatewayV2::Stage":
case "AWS::ApiGatewayV2::DomainName":
case "AWS::ApiGatewayV2::VpcLink":
await tagApiGatewayV2(config, resource, dictTags, partition, region, allResources);
log(`TAGGING: post-deploy ${type} ${logicalId}`);
break;
// Glue and Batch are tagged in template (dict-based), no post-deploy needed
}
}
// API-only types: not tagged in template at all
if (API_ONLY_TYPES.has(type)) {
switch (type) {
case "AWS::RDS::DBCluster":
await tagRDSCluster(config, resource, listTags, partition, region);
log(`TAGGING: post-deploy RDS Cluster ${logicalId}`);
break;
case "AWS::KinesisFirehose::DeliveryStream":
await tagFirehoseStream(config, resource, listTags);
log(`TAGGING: post-deploy Firehose ${logicalId}`);
break;
}
}
// Related types: tag associated resources (volumes, ENIs, etc.)
if (RELATED_TYPES.has(type)) {
switch (type) {
case "AWS::EC2::Instance": {
const relatedIds = await tagEC2RelatedResources(config, resource, listTags);
log(`TAGGING: post-deploy EC2 related resources for ${logicalId}: ${relatedIds.length} resources`);
break;
}
}
}
} catch (err) {
log(`TAGGING: ERROR post-deploy ${type} ${logicalId}: ${err.message}`);
}
}
}
module.exports = { updateTagsPostDeploy };