serverless-tag-resources 3.1.1 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/post-deploy/untag.js +21 -14
- package/src/resource-classifier.js +1 -0
package/package.json
CHANGED
package/src/post-deploy/untag.js
CHANGED
|
@@ -10,20 +10,19 @@ const {
|
|
|
10
10
|
DescribeStackResourcesCommand,
|
|
11
11
|
} = require("@aws-sdk/client-cloudformation");
|
|
12
12
|
const { getClient } = require("../aws-clients");
|
|
13
|
+
const { SKIP_TYPES } = require("../resource-classifier");
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Remove unwanted tags from the stack itself and all its resources.
|
|
16
17
|
* Uses Resource Groups Tagging API (UntagResources) which works on
|
|
17
18
|
* both CF stacks and individual resources.
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
* that we cannot prevent at template/deploy time.
|
|
20
|
+
* No UpdateStack — only tag removal, zero infrastructure changes.
|
|
21
21
|
*/
|
|
22
22
|
async function removeUnwantedTags(config, stackName, tagKeysToRemove, log) {
|
|
23
23
|
const cfnClient = getClient(CloudFormationClient, config);
|
|
24
24
|
const taggingClient = getClient(ResourceGroupsTaggingAPIClient, config);
|
|
25
25
|
|
|
26
|
-
// Collect ARNs: stack + all resources
|
|
27
26
|
const arns = [];
|
|
28
27
|
|
|
29
28
|
// 1. Get stack ARN
|
|
@@ -33,13 +32,18 @@ async function removeUnwantedTags(config, stackName, tagKeysToRemove, log) {
|
|
|
33
32
|
const stackArn = stackResult.Stacks?.[0]?.StackId;
|
|
34
33
|
if (stackArn) arns.push(stackArn);
|
|
35
34
|
|
|
36
|
-
// 2. Get
|
|
35
|
+
// 2. Get resource ARNs (deduplicated, excluding types that don't support tags)
|
|
36
|
+
const seen = new Set();
|
|
37
37
|
const resourceResult = await cfnClient.send(
|
|
38
38
|
new DescribeStackResourcesCommand({ StackName: stackName })
|
|
39
39
|
);
|
|
40
40
|
for (const resource of resourceResult.StackResources || []) {
|
|
41
|
+
if (SKIP_TYPES.has(resource.ResourceType)) continue;
|
|
41
42
|
const arn = resolveArn(resource);
|
|
42
|
-
if (arn
|
|
43
|
+
if (arn && !seen.has(arn)) {
|
|
44
|
+
seen.add(arn);
|
|
45
|
+
arns.push(arn);
|
|
46
|
+
}
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
if (arns.length === 0) return;
|
|
@@ -47,7 +51,7 @@ async function removeUnwantedTags(config, stackName, tagKeysToRemove, log) {
|
|
|
47
51
|
// UntagResources accepts max 20 ARNs per call
|
|
48
52
|
const batchSize = 20;
|
|
49
53
|
let untagged = 0;
|
|
50
|
-
let
|
|
54
|
+
let skipped = 0;
|
|
51
55
|
|
|
52
56
|
for (let i = 0; i < arns.length; i += batchSize) {
|
|
53
57
|
const batch = arns.slice(i, i + batchSize);
|
|
@@ -58,16 +62,16 @@ async function removeUnwantedTags(config, stackName, tagKeysToRemove, log) {
|
|
|
58
62
|
TagKeys: tagKeysToRemove,
|
|
59
63
|
})
|
|
60
64
|
);
|
|
61
|
-
const
|
|
65
|
+
const failedMap = result.FailedResourcesMap || {};
|
|
66
|
+
const failures = Object.keys(failedMap).length;
|
|
62
67
|
untagged += batch.length - failures;
|
|
63
|
-
|
|
68
|
+
skipped += failures;
|
|
64
69
|
} catch (err) {
|
|
65
|
-
|
|
66
|
-
failed += batch.length;
|
|
70
|
+
skipped += batch.length;
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
log(`TAGGING: Removed [${tagKeysToRemove.join(", ")}] from ${untagged} resources (${
|
|
74
|
+
log(`TAGGING: Removed [${tagKeysToRemove.join(", ")}] from ${untagged} resources (${skipped} skipped)`);
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
/**
|
|
@@ -98,21 +102,24 @@ function resolveArn(resource) {
|
|
|
98
102
|
`arn:${partition}:sns:${region}:${account}:${physicalId}`,
|
|
99
103
|
"AWS::Events::EventBus": () =>
|
|
100
104
|
`arn:${partition}:events:${region}:${account}:event-bus/${physicalId}`,
|
|
105
|
+
"AWS::Events::Rule": () =>
|
|
106
|
+
`arn:${partition}:events:${region}:${account}:rule/${physicalId}`,
|
|
101
107
|
"AWS::Logs::LogGroup": () =>
|
|
102
108
|
`arn:${partition}:logs:${region}:${account}:log-group:${physicalId}`,
|
|
103
109
|
"AWS::IAM::Role": () =>
|
|
104
110
|
`arn:${partition}:iam::${account}:role/${physicalId}`,
|
|
105
|
-
"AWS::IAM::ManagedPolicy": () =>
|
|
106
|
-
`arn:${partition}:iam::${account}:policy/${physicalId}`,
|
|
107
111
|
"AWS::S3::Bucket": () =>
|
|
108
112
|
`arn:${partition}:s3:::${physicalId}`,
|
|
109
113
|
"AWS::SSM::Parameter": () =>
|
|
110
114
|
`arn:${partition}:ssm:${region}:${account}:parameter${physicalId.startsWith("/") ? "" : "/"}${physicalId}`,
|
|
111
115
|
"AWS::KMS::Key": () =>
|
|
112
116
|
`arn:${partition}:kms:${region}:${account}:key/${physicalId}`,
|
|
113
|
-
"AWS::KMS::Alias": () => null, // aliases don't support tagging
|
|
114
117
|
"AWS::CodeBuild::Project": () =>
|
|
115
118
|
`arn:${partition}:codebuild:${region}:${account}:project/${physicalId}`,
|
|
119
|
+
"AWS::ApiGateway::RestApi": () =>
|
|
120
|
+
`arn:${partition}:apigateway:${region}::/restapis/${physicalId}`,
|
|
121
|
+
"AWS::WAFv2::WebACL": () =>
|
|
122
|
+
`arn:${partition}:wafv2:${region}:${account}:regional/webacl/${physicalId}`,
|
|
116
123
|
};
|
|
117
124
|
|
|
118
125
|
const builder = builders[type];
|