Instantly share code, notes, and snippets.
Last active
May 6, 2024 20:33
-
Star
(2)
2
You must be signed in to star a gist -
Fork
(2)
2
You must be signed in to fork a gist
-
Save tomekklas/f11172a0a2cdd5888c66ddb7f6d6c214 to your computer and use it in GitHub Desktop.
Copy tags from EC2 to associated resources
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
AWSTemplateFormatVersion: 2010-09-09 | |
Description: Copy tags from EC2 to associated resources | |
Resources: | |
Role: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
Action: | |
- sts:AssumeRole | |
Sid: "" | |
Path: "/" | |
Policies: | |
- PolicyName: "AllowXRay" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- xray:PutTraceSegments | |
- xray:PutTelemetryRecords | |
Resource: | |
- "*" | |
- PolicyName: "Create-EC2" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- ec2:CreateTags | |
Resource: | |
- !Sub "arn:aws:ec2:${AWS::Region}::snapshot/*" | |
- !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:volume/*" | |
- !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/*" | |
- PolicyName: "CreateLogs" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- logs:CreateLogStream | |
Resource: | |
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" | |
- PolicyName: "DescribeGeneric" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- elasticloadbalancing:DescribeLoadBalancers | |
- ec2:DescribeInstances | |
- ec2:DescribeNetworkInterfaces | |
- elasticloadbalancing:DescribeTags | |
- ec2:DescribeTags | |
- ec2:DescribeVolumes | |
Resource: | |
- "*" | |
Lambda: | |
Type: AWS::Lambda::Function | |
DependsOn: | |
- Role | |
Properties: | |
Handler: "index.lambda_handler" | |
Role: !GetAtt Role.Arn | |
Runtime: python3.7 | |
MemorySize: 128 | |
Timeout: 300 | |
TracingConfig: | |
Mode: Active | |
Code: | |
ZipFile: !Sub | | |
import boto3 | |
COPYABLE = ["ApplicationName", "EnvironmentType", "ProvisionedBy", "RequestedBy"] | |
def lambda_handler(event, context): | |
ec2(); | |
elb(); | |
elbv2(); | |
return; | |
def ec2(): | |
print('Processing EC2 Instances') | |
instances = boto3.resource('ec2', region_name='${AWS::Region}').instances.all() | |
for instance in instances: | |
tags = [t for t in instance.tags or [] if t['Key'] in COPYABLE] | |
if not tags: | |
continue | |
# Tag the EBS Volumes | |
for vol in instance.volumes.all(): | |
print('Updating tags for {}'.format(vol.id)) | |
vol.create_tags(Tags=tags) | |
# Tag the Elastic Network Interfaces | |
for eni in instance.network_interfaces: | |
print('Updating tags for {}'.format(eni.id)) | |
eni.create_tags | |
def elb(): | |
print('Processing ELB Instances') | |
def filter(i): | |
return (i.get('RequesterId') == 'amazon-elb' and | |
i['Description'].startswith('ELB') and | |
'/' not in i['Description']) | |
tags = _get_elb_tags('elb') | |
for interface in _network_interfaces(filter): | |
name = interface['Description'].split(' ')[1] | |
if name not in tags: | |
continue | |
_tag_network_interface(interface['NetworkInterfaceId'], tags[name]) | |
def elbv2(): | |
print('Processing ELBv2 Instances') | |
def filter(i): | |
return (i.get('RequesterId') == 'amazon-elb' and | |
i['Description'].startswith('ELB') and | |
'/' in i['Description']) | |
tags = _get_elb_tags('elbv2') | |
for interface in _network_interfaces(filter): | |
name = interface['Description'].split('/')[1] | |
if name not in tags: | |
continue | |
_tag_network_interface(interface['NetworkInterfaceId'], tags[name]) | |
def _get_elb_tags(name='elb'): | |
if name == 'elb': | |
page_name = 'LoadBalancerDescriptions' | |
key = 'LoadBalancerName' | |
kwname = 'LoadBalancerNames' | |
elif name == 'elbv2': | |
page_name = 'LoadBalancers' | |
key = 'LoadBalancerArn' | |
kwname = 'ResourceArns' | |
else: | |
raise ValueError('Invalid name: {}'.format(name)) | |
tags = {} | |
client = boto3.client(name, region_name='${AWS::Region}') | |
paginator = client.get_paginator('describe_load_balancers') | |
for page in paginator.paginate(): | |
for lb in page[page_name]: | |
response = client.describe_tags(**{kwname: [lb[key]]}) | |
lb_tags = [item for sublist in | |
[r.get('Tags', []) for r in response['TagDescriptions']] | |
for item in sublist] | |
tags[lb['LoadBalancerName']] = [t for t in lb_tags if | |
t['Key'] in COPYABLE] | |
tags[lb['LoadBalancerName']].append( | |
{'Key': 'Name', 'Value': lb['LoadBalancerName']}) | |
return tags | |
def _network_interfaces(filter=None): | |
client = boto3.client('ec2', region_name='${AWS::Region}') | |
paginator = client.get_paginator('describe_network_interfaces') | |
for page in paginator.paginate(): | |
for interface in page['NetworkInterfaces']: | |
if filter and not filter(interface): | |
continue | |
yield interface | |
def _tag_network_interface(eni_id, tags): | |
print('Updating tags for {}'.format(eni_id)) | |
ec2 = boto3.resource('ec2', region_name='${AWS::Region}') | |
eni = ec2.NetworkInterface(eni_id) | |
eni.create_tags(Tags=tags) | |
LambdaScheduledRule: | |
Type: AWS::Events::Rule | |
Properties: | |
Description: LambdaCopyTagsScxheduledRule | |
ScheduleExpression: "rate(1 day)" | |
State: ENABLED | |
Targets: | |
- | |
Arn: | |
Fn::GetAtt: | |
- "Lambda" | |
- "Arn" | |
Id: "ScheduleLambda" | |
PermissionForEventsToInvokeLambda: | |
Type: AWS::Lambda::Permission | |
Properties: | |
FunctionName: !Ref Lambda | |
Action: lambda:InvokeFunction | |
Principal: events.amazonaws.com | |
SourceArn: !GetAtt LambdaScheduledRule.Arn |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment