Продолжаем создавать Lambda-функцию для копирования тегов EC2 на EBS.
В первой части — AWS: Lambda — копирование тегов EC2 на EBS, часть 1 — Python и boto3, мы написали Python-скрипт, который умеет получать список всех EC2 в регионе, и потом для каждого ЕС2 получает список всех его EBS, на которые копирует теги плюс добавляет один новый.
В этой части мы создадим AWS Lambda-функцию, которая будет тригеррится через AWS CloudWatch Event.
Содержание
Создание AWS Lambda-функции
Переходим в AWS Lambda, создаём новую функцию, выбираем «Author from scratch«, Runtime — Python 3.8:
В Execution role оставляем «Create a new role» — потом к этой роли добавим нужные IAM Policy.
Вставляем код функции, в конце, в вызове lambda_hanlder() вместо 0, 0 из предыдущего поста — указываем передачу event, context:
Что бы увидеть содержимое context, из которого мы будем получать EC2 ID — добавим вывод его содержимого в лог.
Добавляем import json и print("CONTEXT: " + json.dumps(event)):
Кроме того, в вызове ec2 = boto3.resource() убираем аутентификацию через ключи, оставляем только тип ресурса ec2:
Не забываем нажать Deploy, что бы изменения кода загрузить в Lambda.
Добавление триггера Amazon EventBridge (CloudWatch Events)
Далее, нам надо эту функцию как-то запускать при появлении новых ЕС2 в регионе.
Используем Amazon EventBridge, он же CloudWatch Events. Переходим в CloudWatch Events > Rules, кликаем Create rule:
В Event Pattern выбираем Service Name == EC2, в Event Type выбираем «EC2 Instance State-change Notification«, а в Specific state(s) указываем running:
Справа в Add target выбираем Lambda function, которую создали выше:
Внизу в Show sample event(s) видим пример данных, которые мы и будем парсить, что бы получить EC2 ID:
Сохраняем правило:
Проверяем, что в самой Lambda-функции появился триггер:
Проверяем, как это всё будет работать.
В CloudWatch Rules открываем мониторинг созданного правила:
Запускаем новый инстанс, например через скейлинг AutoScale-группы, ждём его создания:
Видим, что CloudWatch Rule затриггерился:
Проверяем логи Lambda:
AWS Lambda: UnauthorizedOperation
В логах нам интересны две записи.
Первая — содержимое event, которое мы выводим через print("CONTEXT: " + json.dumps(event)):
Второе — ошибка «ClientError: An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation»:
С ошибкой всё ясно — сейчас наша Lambda-функция использует IAM-роль, созданную при создании функции, и которой не хватает прав на выполнение действий с EC2:
Переходим в AWS IAM, находим роль, кликаем Attach policies:
Создаём политику:
Описываем разрешения:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DeleteTags",
"ec2:CreateTags",
"ec2:DescribeVolumes"
],
"Resource": "*"
}
]
}
Сохраняем её:
Возвращаемся к роли, добавляем политику:
Повторяем запуск ЕС2, и ещё раз проверяем логи функции:
Ура! «It works!» (c)
Далее, нам осталось обновить саму функцию — получать из event значение instance-id.
Парсинг Lambda event
Пример event мы видели в Cloudwatch, и знаем, что он представляет собой python-dictionary с несколькими ключами:
{
"version": "0",
"id": "a99597e5-90a5-5ac3-3aba-da3ecd51452a",
"detail-type": "EC2 Instance State-change Notification",
"source": "aws.ec2",
"account": "534***385",
"time": "2021-10-11T09:02:09Z",
"region": "eu-west-3",
"resources": [
"arn:aws:ec2:eu-west-3:534***385:instance/i-0cc24729109ba61e5"
],
"detail": {
"instance-id": "i-0cc24729109ba61e5",
"state": "running"
}
}
Из которого мы хотим получить detail.instance-id.
Добавляем новую переменную instance_id, значение которой получаем из event["detail"]["instance-id"], а потом используя этот ID — создаём новый объект класса ec2.Instance — ec2.Instance(instance_id):
Вместо запуска ЕС2 вручную — можем создать тестовый евент, в который передадим данные.
Создаём евент:
Добвляем данные евента, которые мы будем получать от CloudWatch:
{
"version": "0",
"id": "a99597e5-90a5-5ac3-3aba-da3ecd51452a",
"detail-type": "EC2 Instance State-change Notification",
"source": "aws.ec2",
"account": "534***385",
"time": "2021-10-11T09:02:09Z",
"region": "eu-west-3",
"resources": [
"arn:aws:ec2:eu-west-3:534***385:instance/i-0cc24729109ba61e5"
],
"detail": {
"instance-id": "i-0cc24729109ba61e5",
"state": "running"
}
}
Запускаем ещё раз тест:
Работает.
И запускаем создание нового ЕС2, что бы проверить всю схему.
Триггерим AutoScaling:
Запущен инстанс i-051f2e1f1bc8d332b, проверяем логи Lambda:
Находим EBS этого инстанса:
И проверяем его теги:
И код функции целиком:
#!/usr/bin/env python
import os
import json
import boto3
def lambda_handler(event, context):
ec2 = boto3.resource('ec2')
instance_id = event["detail"]["instance-id"]
instance = ec2.Instance(instance_id)
print("[DEBUG] EC2\n\t\tID: " + str(instance))
print("\tEBS")
for vol in instance.volumes.all():
vol_id = str(vol)
print("VOLUME: " + str(vol))
device_id = "ec2.vol.Device('" + str(vol.attachments[0]['Device']) + "')"
print("\t\tID: " + vol_id + "\n\t\tDev: " + device_id)
role_tag = vol.create_tags(Tags=set_role_tag(vol))
ec2_tags = vol.create_tags(Tags=copy_ec2_tags(instance))
print("\t\tTags set:\n\t\t\t" + str(role_tag) + "\n\t\t\t" + str(ec2_tags) + "\n")
def is_pvc(vol):
try:
for tag in vol.tags:
if tag['Key'] == 'kubernetes.io/created-for/pvc/name':
return True
break
except TypeError:
return False
def set_role_tag(vol):
device = vol.attachments[0]['Device']
tags_list = []
values = {}
if is_pvc(vol):
values['Key'] = "Role"
values['Value'] = "PvcDisk"
tags_list.append(values)
elif device == "/dev/xvda":
values['Key'] = "Role"
values['Value'] = "RootDisk"
tags_list.append(values)
else:
values['Key'] = "Role"
values['Value'] = "DataDisk"
tags_list.append(values)
return tags_list
def copy_ec2_tags(instance):
tags_list = []
values = {}
for instance_tag in instance.tags:
if instance_tag['Key'] == 'Env':
tags_list.append(instance_tag)
elif instance_tag['Key'] == 'Tier':
tags_list.append(instance_tag)
elif instance_tag['Key'] == 'DataClass':
tags_list.append(instance_tag)
elif instance_tag['Key'] == 'JiraTicket':
tags_list.append(instance_tag)
return tags_list
if __name__ == "__main__":
lambda_handler(event, context)
Готово.





























