UNPKG

serverless-tag-resources

Version:

Datamart: Tag all AWS resources with dual legacy + datamart:* tag support

383 lines (370 loc) 11.3 kB
"use strict"; /** * Resource classifier. * * Determines how each CloudFormation resource type should be tagged: * - 'list' → Tags: [{Key, Value}] in template * - 'dict' → Tags: {key: value} in template + post-deploy API * - 'api-only' → Tagged only post-deploy via AWS API (not in template) * - 'skip' → Cannot be tagged */ // Resources that use dict-based tag format in CloudFormation templates const DICT_BASED_TYPES = new Set([ "AWS::SSM::Parameter", "AWS::Pinpoint::App", "AWS::ApiGatewayV2::Api", "AWS::ApiGatewayV2::Stage", "AWS::ApiGatewayV2::DomainName", "AWS::ApiGatewayV2::VpcLink", "AWS::Glue::Job", "AWS::Glue::Crawler", "AWS::Glue::DevEndpoint", "AWS::Glue::MLTransform", "AWS::Glue::Trigger", "AWS::Glue::Workflow", "AWS::Batch::JobDefinition", "AWS::Batch::ComputeEnvironment", "AWS::Batch::JobQueue", "AWS::Batch::SchedulingPolicy", ]); // Resources tagged only via API post-deploy (not in template) const API_ONLY_TYPES = new Set([ "AWS::RDS::DBCluster", "AWS::KinesisFirehose::DeliveryStream", ]); // Resources whose related resources also get tagged (volumes, ENIs, etc.) const RELATED_TYPES = new Set([ "AWS::EC2::Instance", ]); // Common resource types known to support list-based tags [{Key, Value}]. // Types here won't generate a warning. Types NOT in any list will be // tagged as list-based but with a warning (unclassified). const LIST_BASED_TYPES = new Set([ // Lambda "AWS::Lambda::Function", // S3 "AWS::S3::Bucket", // DynamoDB "AWS::DynamoDB::Table", "AWS::DynamoDB::GlobalTable", // SNS / SQS "AWS::SNS::Topic", "AWS::SQS::Queue", // API Gateway v1 "AWS::ApiGateway::RestApi", "AWS::ApiGateway::Stage", "AWS::ApiGateway::UsagePlan", "AWS::ApiGateway::DomainName", "AWS::ApiGateway::VpcLink", "AWS::ApiGateway::ClientCertificate", // CloudWatch "AWS::Logs::LogGroup", // IAM "AWS::IAM::Role", "AWS::IAM::User", // EC2 / VPC "AWS::EC2::Instance", "AWS::EC2::SecurityGroup", "AWS::EC2::Subnet", "AWS::EC2::VPC", "AWS::EC2::InternetGateway", "AWS::EC2::NatGateway", "AWS::EC2::RouteTable", "AWS::EC2::NetworkInterface", "AWS::EC2::Volume", "AWS::EC2::EIP", "AWS::EC2::TransitGateway", "AWS::EC2::TransitGatewayAttachment", // ECS "AWS::ECS::Cluster", "AWS::ECS::Service", "AWS::ECS::TaskDefinition", // ELB "AWS::ElasticLoadBalancingV2::LoadBalancer", "AWS::ElasticLoadBalancingV2::TargetGroup", // RDS "AWS::RDS::DBInstance", "AWS::RDS::DBSubnetGroup", "AWS::RDS::DBParameterGroup", "AWS::RDS::DBClusterParameterGroup", "AWS::RDS::DBProxy", "AWS::RDS::EventSubscription", // ElastiCache "AWS::ElastiCache::CacheCluster", "AWS::ElastiCache::ReplicationGroup", "AWS::ElastiCache::SubnetGroup", "AWS::ElastiCache::ParameterGroup", // CloudFront "AWS::CloudFront::Distribution", // KMS "AWS::KMS::Key", // Secrets Manager "AWS::SecretsManager::Secret", // Step Functions "AWS::StepFunctions::StateMachine", "AWS::StepFunctions::Activity", // CodeBuild / CodePipeline "AWS::CodeBuild::Project", "AWS::CodePipeline::Pipeline", // Kinesis "AWS::Kinesis::Stream", // OpenSearch "AWS::OpenSearchService::Domain", "AWS::Elasticsearch::Domain", // WAF "AWS::WAFv2::WebACL", "AWS::WAFv2::IPSet", "AWS::WAFv2::RegexPatternSet", "AWS::WAFv2::RuleGroup", // AppSync "AWS::AppSync::GraphQLApi", // Cognito (taggable ones) // EventBridge "AWS::Events::EventBus", // Redshift "AWS::Redshift::Cluster", "AWS::Redshift::ClusterSubnetGroup", "AWS::Redshift::ClusterParameterGroup", // ECR "AWS::ECR::Repository", // GlobalAccelerator "AWS::GlobalAccelerator::Accelerator", // ACM "AWS::CertificateManager::Certificate", // SSM "AWS::SSM::Document", "AWS::SSM::MaintenanceWindow", "AWS::SSM::PatchBaseline", ]); // Resource types that do NOT support tagging. // Using a Set for O(1) lookups. Kept as skip-list because CF adds new // taggable types regularly and we want them tagged by default. const SKIP_TYPES = new Set([ // Lambda "AWS::Lambda::Version", "AWS::Lambda::EventSourceMapping", "AWS::Lambda::LayerVersion", "AWS::Lambda::EventInvokeConfig", "AWS::Lambda::Alias", "AWS::Lambda::Permission", "AWS::Lambda::LayerVersionPermission", "AWS::Lambda::Url", // CloudWatch Logs "AWS::Logs::LogStream", "AWS::Logs::Destination", "AWS::Logs::MetricFilter", "AWS::Logs::QueryDefinition", "AWS::Logs::ResourcePolicy", "AWS::Logs::SubscriptionFilter", // API Gateway v1 "AWS::ApiGateway::Account", "AWS::ApiGateway::ApiKey", "AWS::ApiGateway::Method", "AWS::ApiGateway::Deployment", "AWS::ApiGateway::UsagePlanKey", "AWS::ApiGateway::BasePathMapping", "AWS::ApiGateway::Resource", "AWS::ApiGateway::Model", "AWS::ApiGateway::RequestValidator", "AWS::ApiGateway::GatewayResponse", "AWS::ApiGateway::Authorizer", // API Gateway v2 "AWS::ApiGatewayV2::Integration", "AWS::ApiGatewayV2::Route", "AWS::ApiGatewayV2::ApiMapping", "AWS::ApiGatewayV2::ApiGatewayManagedOverrides", "AWS::ApiGatewayV2::Authorizer", "AWS::ApiGatewayV2::Deployment", "AWS::ApiGatewayV2::IntegrationResponse", "AWS::ApiGatewayV2::Model", "AWS::ApiGatewayV2::RouteResponse", // AppSync "AWS::AppSync::DataSource", "AWS::AppSync::ApiKey", "AWS::AppSync::ApiCache", "AWS::AppSync::DomainName", "AWS::AppSync::DomainNameApiAssociation", "AWS::AppSync::FunctionConfiguration", "AWS::AppSync::GraphQLSchema", "AWS::AppSync::Resolver", // AutoScaling "AWS::AutoScaling::AutoScalingGroup", // Backup "AWS::Backup::BackupVault", "AWS::Backup::BackupSelection", "AWS::Backup::BackupPlan", // CodeDeploy "AWS::CodeDeploy::Application", "AWS::CodeDeploy::DeploymentConfig", // Cognito "AWS::Cognito::IdentityPool", "AWS::Cognito::IdentityPoolRoleAttachment", "AWS::Cognito::UserPool", "AWS::Cognito::UserPoolDomain", "AWS::Cognito::UserPoolClient", "AWS::Cognito::UserPoolGroup", "AWS::Cognito::UserPoolUser", "AWS::Cognito::UserPoolUserToGroupAttachment", "AWS::Cognito::UserPoolIdentityProvider", "AWS::Cognito::UserPoolResourceServer", // CloudWatch "AWS::CloudWatch::Alarm", "AWS::CloudWatch::Dashboard", // CloudFront "AWS::CloudFront::CloudFrontOriginAccessIdentity", "AWS::CloudFront::OriginAccessControl", "AWS::CloudFront::OriginRequestPolicy", "AWS::CloudFront::Function", "AWS::CloudFront::ResponseHeadersPolicy", "AWS::CloudFront::CachePolicy", // Elastic Beanstalk "AWS::ElasticBeanstalk::ApplicationVersion", "AWS::ElasticBeanstalk::ConfigurationTemplate", // ELB "AWS::ElasticLoadBalancingV2::Listener", "AWS::ElasticLoadBalancingV2::ListenerRule", // ECS "AWS::ECS::ClusterCapacityProviderAssociations", "AWS::ECS::PrimaryTaskSet", // EC2 "AWS::EC2::SecurityGroupEgress", "AWS::EC2::SecurityGroupIngress", "AWS::EC2::LaunchTemplate", "AWS::EC2::VPCGatewayAttachment", "AWS::EC2::Route", "AWS::EC2::SubnetRouteTableAssociation", "AWS::EC2::VPCDHCPOptionsAssociation", "AWS::EC2::VPCEndpoint", "AWS::EC2::TransitGatewayRoute", "AWS::EC2::TransitGatewayRouteTableAssociation", "AWS::EC2::TransitGatewayRouteTablePropagation", "AWS::EC2::IPAMAllocation", "AWS::EC2::IPAMPoolCidr", // Events "AWS::Events::Rule", "AWS::Events::EventBus", "AWS::Events::EventBusPolicy", "AWS::Events::Connection", "AWS::Events::ApiDestination", "AWS::Events::Endpoint", "AWS::Events::Archive", // EFS "AWS::EFS::FileSystem", "AWS::EFS::MountTarget", "AWS::EFS::AccessPoint", // GlobalAccelerator "AWS::GlobalAccelerator::Listener", "AWS::GlobalAccelerator::EndpointGroup", // Glue "AWS::Glue::Database", "AWS::Glue::Classifier", "AWS::Glue::Crawler", "AWS::Glue::Connection", "AWS::Glue::DataCatalogEncryptionSettings", "AWS::Glue::Partition", "AWS::Glue::SchemaVersion", "AWS::Glue::SchemaVersionMetadata", "AWS::Glue::SecurityConfiguration", "AWS::Glue::Table", // Grafana "AWS::Grafana::Workspace", // KMS "AWS::KMS::Alias", // Route53 "AWS::Route53::HostedZone", "AWS::Route53::RecordSet", "AWS::Route53::RecordSetGroup", "AWS::Route53::HealthCheck", // RDS "AWS::RDS::DBProxyTargetGroup", // S3 "AWS::S3::AccessPoint", "AWS::S3::MultiRegionAccessPoint", "AWS::S3::MultiRegionAccessPointPolicy", "AWS::S3::BucketPolicy", // SES "AWS::SES::ReceiptRuleSet", "AWS::SES::ReceiptRule", "AWS::SES::ConfigurationSet", "AWS::SES::ConfigurationSetEventDestination", "AWS::SES::ReceiptFilter", "AWS::SES::Template", // SNS / SQS "AWS::SNS::Subscription", "AWS::SNS::TopicPolicy", "AWS::SQS::QueuePolicy", // SSM "AWS::SSM::ResourceDataSync", // Secrets Manager "AWS::SecretsManager::SecretTargetAttachment", "AWS::SecretsManager::RotationSchedule", "AWS::SecretsManager::ResourcePolicy", // IAM "AWS::IAM::Policy", "AWS::IAM::AccessKey", "AWS::IAM::UserToGroupAddition", "AWS::IAM::ServiceLinkedRole", "AWS::IAM::ManagedPolicy", "AWS::IAM::InstanceProfile", "AWS::IAM::Group", "AWS::IAM::RolePolicy", // Application AutoScaling "AWS::ApplicationAutoScaling::ScalableTarget", "AWS::ApplicationAutoScaling::ScalingPolicy", // WAF "AWS::WAFv2::WebACLAssociation", "AWS::WAFv2::LoggingConfiguration", // OpenSearch Serverless "AWS::OpenSearchServerless::AccessPolicy", "AWS::OpenSearchServerless::SecurityPolicy", "AWS::OpenSearchServerless::VpcEndpoint", // Pinpoint "AWS::PinpointEmail::ConfigurationSetEventDestination", "AWS::Pinpoint::ADMChannel", "AWS::Pinpoint::APNSChannel", "AWS::Pinpoint::APNSSandboxChannel", "AWS::Pinpoint::APNSVoipChannel", "AWS::Pinpoint::APNSVoipSandboxChannel", "AWS::Pinpoint::ApplicationSettings", "AWS::Pinpoint::BaiduChannel", "AWS::Pinpoint::EmailChannel", "AWS::Pinpoint::EventStream", "AWS::Pinpoint::GCMChannel", "AWS::Pinpoint::SMSChannel", "AWS::Pinpoint::VoiceChannel", // Others "AWS::Pipes::Pipe", "AWS::QLDB::Ledger", "AWS::Scheduler::Schedule", "AWS::Athena::NamedQuery", ]); /** * Classify a CloudFormation resource type. * * @param {string} resourceType - e.g. "AWS::Lambda::Function" * @returns {'list'|'dict'|'api-only'|'related'|'skip'} */ function classifyResource(resourceType) { if (!resourceType) return "skip"; // Custom resources are never tagged if (resourceType.toLowerCase().startsWith("custom::")) return "skip"; // Check specific classifications first if (DICT_BASED_TYPES.has(resourceType)) return "dict"; if (API_ONLY_TYPES.has(resourceType)) return "api-only"; if (RELATED_TYPES.has(resourceType)) return "related"; if (SKIP_TYPES.has(resourceType)) return "skip"; if (LIST_BASED_TYPES.has(resourceType)) return "list"; // Unknown type: attempt list-based tagging with warning return "unclassified"; } /** * Check if a resource type needs post-deploy API tagging. */ function needsPostDeployTagging(resourceType) { return ( DICT_BASED_TYPES.has(resourceType) || API_ONLY_TYPES.has(resourceType) || RELATED_TYPES.has(resourceType) ); } module.exports = { classifyResource, needsPostDeployTagging, LIST_BASED_TYPES, DICT_BASED_TYPES, API_ONLY_TYPES, RELATED_TYPES, SKIP_TYPES };