better-aws-tags 0.3.1__py3-none-any.whl → 0.5.0__py3-none-any.whl
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.
- better_aws_tags/__init__.py +1 -1
- better_aws_tags/handlers/__init__.py +45 -0
- better_aws_tags/handlers/asg.py +54 -0
- better_aws_tags/handlers/base.py +13 -0
- better_aws_tags/handlers/cloudfront.py +27 -0
- better_aws_tags/handlers/glue.py +25 -0
- better_aws_tags/handlers/iam.py +79 -0
- better_aws_tags/handlers/inspector.py +26 -0
- better_aws_tags/handlers/rds.py +26 -0
- better_aws_tags/handlers/resource_explorer.py +25 -0
- better_aws_tags/handlers/ses.py +26 -0
- better_aws_tags/handlers/tagging_api.py +50 -0
- better_aws_tags/handlers/wafv2.py +27 -0
- better_aws_tags/supported_resources.py +1146 -0
- better_aws_tags/tags.py +18 -9
- better_aws_tags/unsupported_resources.py +29 -0
- better_aws_tags-0.5.0.dist-info/METADATA +47 -0
- better_aws_tags-0.5.0.dist-info/RECORD +19 -0
- better_aws_tags/arnparse.py +0 -139
- better_aws_tags/handlers.py +0 -134
- better_aws_tags-0.3.1.dist-info/METADATA +0 -20
- better_aws_tags-0.3.1.dist-info/RECORD +0 -7
- {better_aws_tags-0.3.1.dist-info → better_aws_tags-0.5.0.dist-info}/WHEEL +0 -0
better_aws_tags/__init__.py
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from .asg import ASGHandler
|
|
2
|
+
from .base import Handler
|
|
3
|
+
from .cloudfront import CloudFrontFunctionHandler
|
|
4
|
+
from .glue import GlueTableHandler
|
|
5
|
+
from .iam import IAMPolicyHandler, IAMRoleHandler, IAMUserHandler
|
|
6
|
+
from .inspector import InspectorHandler
|
|
7
|
+
from .rds import RDSAutoBackupHandler
|
|
8
|
+
from .resource_explorer import ResourceExplorer2Handler
|
|
9
|
+
from .ses import SESIdentityHandler
|
|
10
|
+
from .tagging_api import TaggingAPIHandler
|
|
11
|
+
from .wafv2 import WAFv2Handler
|
|
12
|
+
|
|
13
|
+
HANDLERS = {
|
|
14
|
+
("autoscaling", "autoscaling-group"): ASGHandler,
|
|
15
|
+
("cloudfront", "function"): CloudFrontFunctionHandler,
|
|
16
|
+
("glue", "table"): GlueTableHandler,
|
|
17
|
+
("iam", "iam-role"): IAMRoleHandler,
|
|
18
|
+
("iam", "user"): IAMUserHandler,
|
|
19
|
+
("iam", "policy"): IAMPolicyHandler,
|
|
20
|
+
("inspector", "target-template"): InspectorHandler,
|
|
21
|
+
("rds", "auto-backup"): RDSAutoBackupHandler,
|
|
22
|
+
("resource-explorer-2", "index"): ResourceExplorer2Handler,
|
|
23
|
+
("resource-explorer-2", "view"): ResourceExplorer2Handler,
|
|
24
|
+
("ses", "identity"): SESIdentityHandler,
|
|
25
|
+
("wafv2", "ipset"): WAFv2Handler,
|
|
26
|
+
("wafv2", "rulegroup"): WAFv2Handler,
|
|
27
|
+
("wafv2", "webacl"): WAFv2Handler,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"ASGHandler",
|
|
32
|
+
"CloudFrontFunctionHandler",
|
|
33
|
+
"GlueTableHandler",
|
|
34
|
+
"Handler",
|
|
35
|
+
"HANDLERS",
|
|
36
|
+
"IAMPolicyHandler",
|
|
37
|
+
"IAMRoleHandler",
|
|
38
|
+
"IAMUserHandler",
|
|
39
|
+
"InspectorHandler",
|
|
40
|
+
"RDSAutoBackupHandler",
|
|
41
|
+
"ResourceExplorer2Handler",
|
|
42
|
+
"SESIdentityHandler",
|
|
43
|
+
"TaggingAPIHandler",
|
|
44
|
+
"WAFv2Handler",
|
|
45
|
+
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from botocore.exceptions import ClientError
|
|
2
|
+
from arnmatch import arnmatch
|
|
3
|
+
|
|
4
|
+
from .base import Handler
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ASGHandler(Handler):
|
|
8
|
+
@classmethod
|
|
9
|
+
def get_tags(cls, session, arns):
|
|
10
|
+
client = session.client("autoscaling")
|
|
11
|
+
result = {}
|
|
12
|
+
|
|
13
|
+
names = {arnmatch(arn).resource_name: arn for arn in arns}
|
|
14
|
+
|
|
15
|
+
response = client.describe_tags(
|
|
16
|
+
Filters=[{"Name": "auto-scaling-group", "Values": list(names.keys())}]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
for arn in arns:
|
|
20
|
+
result[arn] = {}
|
|
21
|
+
|
|
22
|
+
for tag in response["Tags"]:
|
|
23
|
+
arn = names[tag["ResourceId"]]
|
|
24
|
+
result[arn][tag["Key"]] = tag["Value"]
|
|
25
|
+
|
|
26
|
+
return result
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def set_tags(cls, session, arns, tags):
|
|
30
|
+
client = session.client("autoscaling")
|
|
31
|
+
result = {}
|
|
32
|
+
|
|
33
|
+
for arn in arns:
|
|
34
|
+
name = arnmatch(arn).resource_name
|
|
35
|
+
payload = [
|
|
36
|
+
{
|
|
37
|
+
"ResourceId": name,
|
|
38
|
+
"ResourceType": "auto-scaling-group",
|
|
39
|
+
"Key": k,
|
|
40
|
+
"Value": v,
|
|
41
|
+
"PropagateAtLaunch": False,
|
|
42
|
+
}
|
|
43
|
+
for k, v in tags.items()
|
|
44
|
+
]
|
|
45
|
+
try:
|
|
46
|
+
client.create_or_update_tags(Tags=payload)
|
|
47
|
+
result[arn] = True
|
|
48
|
+
except ClientError as e:
|
|
49
|
+
if e.response["Error"]["Code"] == "ValidationError":
|
|
50
|
+
result[arn] = None
|
|
51
|
+
else:
|
|
52
|
+
raise
|
|
53
|
+
|
|
54
|
+
return result
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CloudFrontFunctionHandler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
client = session.client("cloudfront")
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
for arn in arns:
|
|
11
|
+
response = client.list_tags_for_resource(Resource=arn)
|
|
12
|
+
tags = response["Tags"].get("Items", [])
|
|
13
|
+
result[arn] = {t["Key"]: t["Value"] for t in tags}
|
|
14
|
+
|
|
15
|
+
return result
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def set_tags(cls, session, arns, tags):
|
|
19
|
+
client = session.client("cloudfront")
|
|
20
|
+
result = {}
|
|
21
|
+
|
|
22
|
+
payload = {"Items": [{"Key": k, "Value": v} for k, v in tags.items()]}
|
|
23
|
+
for arn in arns:
|
|
24
|
+
client.tag_resource(Resource=arn, Tags=payload)
|
|
25
|
+
result[arn] = True
|
|
26
|
+
|
|
27
|
+
return result
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GlueTableHandler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
client = session.client("glue")
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
for arn in arns:
|
|
11
|
+
response = client.get_tags(ResourceArn=arn)
|
|
12
|
+
result[arn] = response["Tags"]
|
|
13
|
+
|
|
14
|
+
return result
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def set_tags(cls, session, arns, tags):
|
|
18
|
+
client = session.client("glue")
|
|
19
|
+
result = {}
|
|
20
|
+
|
|
21
|
+
for arn in arns:
|
|
22
|
+
client.tag_resource(ResourceArn=arn, TagsToAdd=tags)
|
|
23
|
+
result[arn] = True
|
|
24
|
+
|
|
25
|
+
return result
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from arnmatch import arnmatch
|
|
2
|
+
|
|
3
|
+
from .base import Handler
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IAMRoleHandler(Handler):
|
|
7
|
+
@classmethod
|
|
8
|
+
def get_tags(cls, session, arns):
|
|
9
|
+
client = session.client("iam")
|
|
10
|
+
result = {}
|
|
11
|
+
for arn in arns:
|
|
12
|
+
name = arnmatch(arn).resource_name
|
|
13
|
+
response = client.list_role_tags(RoleName=name)
|
|
14
|
+
result[arn] = {t["Key"]: t["Value"] for t in response["Tags"]}
|
|
15
|
+
return result
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def set_tags(cls, session, arns, tags):
|
|
19
|
+
client = session.client("iam")
|
|
20
|
+
payload = [{"Key": k, "Value": v} for k, v in tags.items()]
|
|
21
|
+
result = {}
|
|
22
|
+
for arn in arns:
|
|
23
|
+
name = arnmatch(arn).resource_name
|
|
24
|
+
try:
|
|
25
|
+
client.tag_role(RoleName=name, Tags=payload)
|
|
26
|
+
result[arn] = True
|
|
27
|
+
except client.exceptions.NoSuchEntityException:
|
|
28
|
+
result[arn] = None
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class IAMUserHandler(Handler):
|
|
33
|
+
@classmethod
|
|
34
|
+
def get_tags(cls, session, arns):
|
|
35
|
+
client = session.client("iam")
|
|
36
|
+
result = {}
|
|
37
|
+
for arn in arns:
|
|
38
|
+
name = arnmatch(arn).resource_name
|
|
39
|
+
response = client.list_user_tags(UserName=name)
|
|
40
|
+
result[arn] = {t["Key"]: t["Value"] for t in response["Tags"]}
|
|
41
|
+
return result
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def set_tags(cls, session, arns, tags):
|
|
45
|
+
client = session.client("iam")
|
|
46
|
+
payload = [{"Key": k, "Value": v} for k, v in tags.items()]
|
|
47
|
+
result = {}
|
|
48
|
+
for arn in arns:
|
|
49
|
+
name = arnmatch(arn).resource_name
|
|
50
|
+
try:
|
|
51
|
+
client.tag_user(UserName=name, Tags=payload)
|
|
52
|
+
result[arn] = True
|
|
53
|
+
except client.exceptions.NoSuchEntityException:
|
|
54
|
+
result[arn] = None
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class IAMPolicyHandler(Handler):
|
|
59
|
+
@classmethod
|
|
60
|
+
def get_tags(cls, session, arns):
|
|
61
|
+
client = session.client("iam")
|
|
62
|
+
result = {}
|
|
63
|
+
for arn in arns:
|
|
64
|
+
response = client.list_policy_tags(PolicyArn=arn)
|
|
65
|
+
result[arn] = {t["Key"]: t["Value"] for t in response["Tags"]}
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def set_tags(cls, session, arns, tags):
|
|
70
|
+
client = session.client("iam")
|
|
71
|
+
payload = [{"Key": k, "Value": v} for k, v in tags.items()]
|
|
72
|
+
result = {}
|
|
73
|
+
for arn in arns:
|
|
74
|
+
try:
|
|
75
|
+
client.tag_policy(PolicyArn=arn, Tags=payload)
|
|
76
|
+
result[arn] = True
|
|
77
|
+
except client.exceptions.NoSuchEntityException:
|
|
78
|
+
result[arn] = None
|
|
79
|
+
return result
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class InspectorHandler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
client = session.client("inspector")
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
for arn in arns:
|
|
11
|
+
response = client.list_tags_for_resource(resourceArn=arn)
|
|
12
|
+
result[arn] = {t["key"]: t["value"] for t in response["tags"]}
|
|
13
|
+
|
|
14
|
+
return result
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def set_tags(cls, session, arns, tags):
|
|
18
|
+
client = session.client("inspector")
|
|
19
|
+
result = {}
|
|
20
|
+
|
|
21
|
+
payload = [{"key": k, "value": v} for k, v in tags.items()]
|
|
22
|
+
for arn in arns:
|
|
23
|
+
client.set_tags_for_resource(resourceArn=arn, tags=payload)
|
|
24
|
+
result[arn] = True
|
|
25
|
+
|
|
26
|
+
return result
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RDSAutoBackupHandler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
client = session.client("rds")
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
for arn in arns:
|
|
11
|
+
response = client.list_tags_for_resource(ResourceName=arn)
|
|
12
|
+
result[arn] = {t["Key"]: t["Value"] for t in response["TagList"]}
|
|
13
|
+
|
|
14
|
+
return result
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def set_tags(cls, session, arns, tags):
|
|
18
|
+
client = session.client("rds")
|
|
19
|
+
result = {}
|
|
20
|
+
|
|
21
|
+
payload = [{"Key": k, "Value": v} for k, v in tags.items()]
|
|
22
|
+
for arn in arns:
|
|
23
|
+
client.add_tags_to_resource(ResourceName=arn, Tags=payload)
|
|
24
|
+
result[arn] = True
|
|
25
|
+
|
|
26
|
+
return result
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ResourceExplorer2Handler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
client = session.client("resource-explorer-2")
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
for arn in arns:
|
|
11
|
+
response = client.list_tags_for_resource(resourceArn=arn)
|
|
12
|
+
result[arn] = response.get("Tags", {})
|
|
13
|
+
|
|
14
|
+
return result
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def set_tags(cls, session, arns, tags):
|
|
18
|
+
client = session.client("resource-explorer-2")
|
|
19
|
+
result = {}
|
|
20
|
+
|
|
21
|
+
for arn in arns:
|
|
22
|
+
client.tag_resource(resourceArn=arn, Tags=tags)
|
|
23
|
+
result[arn] = True
|
|
24
|
+
|
|
25
|
+
return result
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SESIdentityHandler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
client = session.client("sesv2")
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
for arn in arns:
|
|
11
|
+
response = client.list_tags_for_resource(ResourceArn=arn)
|
|
12
|
+
result[arn] = {t["Key"]: t["Value"] for t in response["Tags"]}
|
|
13
|
+
|
|
14
|
+
return result
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def set_tags(cls, session, arns, tags):
|
|
18
|
+
client = session.client("sesv2")
|
|
19
|
+
result = {}
|
|
20
|
+
|
|
21
|
+
payload = [{"Key": k, "Value": v} for k, v in tags.items()]
|
|
22
|
+
for arn in arns:
|
|
23
|
+
client.tag_resource(ResourceArn=arn, Tags=payload)
|
|
24
|
+
result[arn] = True
|
|
25
|
+
|
|
26
|
+
return result
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TaggingAPIHandler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
if not arns:
|
|
8
|
+
raise ValueError("At least one ARN must be provided to get_tags.")
|
|
9
|
+
|
|
10
|
+
client = session.client("resourcegroupstaggingapi")
|
|
11
|
+
result = {}
|
|
12
|
+
|
|
13
|
+
# https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_GetResources.html
|
|
14
|
+
batch_size = 100
|
|
15
|
+
for i in range(0, len(arns), batch_size):
|
|
16
|
+
batch = arns[i : i + batch_size]
|
|
17
|
+
response = client.get_resources(ResourceARNList=batch)
|
|
18
|
+
response = response["ResourceTagMappingList"]
|
|
19
|
+
for arn in batch:
|
|
20
|
+
if arn in response:
|
|
21
|
+
result[arn] = {
|
|
22
|
+
t["Key"]: t["Value"] for t in response[arn].get("Tags", [])
|
|
23
|
+
}
|
|
24
|
+
else:
|
|
25
|
+
result[arn] = None
|
|
26
|
+
return result
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def set_tags(cls, session, arns, tags):
|
|
30
|
+
if not arns:
|
|
31
|
+
raise ValueError("At least one ARN must be provided to set_tags.")
|
|
32
|
+
|
|
33
|
+
if len(tags) == 0:
|
|
34
|
+
raise ValueError("At least one tag must be provided to set_tags.")
|
|
35
|
+
|
|
36
|
+
if len(tags) > 50:
|
|
37
|
+
raise ValueError("A maximum of 50 tags can be set at once.")
|
|
38
|
+
|
|
39
|
+
client = session.client("resourcegroupstaggingapi")
|
|
40
|
+
result = {}
|
|
41
|
+
|
|
42
|
+
# https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_TagResources.html
|
|
43
|
+
batch_size = 20
|
|
44
|
+
for i in range(0, len(arns), batch_size):
|
|
45
|
+
batch = arns[i : i + batch_size]
|
|
46
|
+
response = client.tag_resources(ResourceARNList=batch, Tags=tags)
|
|
47
|
+
for arn in batch:
|
|
48
|
+
result[arn] = arn not in response["FailedResourcesMap"]
|
|
49
|
+
|
|
50
|
+
return result
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .base import Handler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WAFv2Handler(Handler):
|
|
5
|
+
@classmethod
|
|
6
|
+
def get_tags(cls, session, arns):
|
|
7
|
+
client = session.client("wafv2")
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
for arn in arns:
|
|
11
|
+
response = client.list_tags_for_resource(ResourceARN=arn)
|
|
12
|
+
tags = response["TagInfoForResource"]["TagList"]
|
|
13
|
+
result[arn] = {t["Key"]: t["Value"] for t in tags}
|
|
14
|
+
|
|
15
|
+
return result
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def set_tags(cls, session, arns, tags):
|
|
19
|
+
client = session.client("wafv2")
|
|
20
|
+
result = {}
|
|
21
|
+
|
|
22
|
+
payload = [{"Key": k, "Value": v} for k, v in tags.items()]
|
|
23
|
+
for arn in arns:
|
|
24
|
+
client.tag_resource(ResourceARN=arn, Tags=payload)
|
|
25
|
+
result[arn] = True
|
|
26
|
+
|
|
27
|
+
return result
|