diff --git a/README.md b/README.md index b9564cc..4b960cb 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_policy.ecs_cluster_infrastructure_ecs_asg_diff_metric_kms_encrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.ecs_cluster_infrastructure_ecs_asg_diff_metric_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.ecs_cluster_infrastructure_instance_refresh_allow_instance_refresh](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.ecs_cluster_infrastructure_instance_refresh_allow_modify_launch_template](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.ecs_cluster_infrastructure_instance_refresh_kms_encrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.ecs_cluster_infrastructure_instance_refresh_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.ecs_cluster_infrastructure_pending_task_metric_cloudwatch_put_metric_data_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | @@ -237,6 +238,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_ecs_asg_diff_metric_ecs_describe_cluster_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_ecs_asg_diff_metric_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_instance_refresh_allow_instance_refresh](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_instance_refresh_allow_modify_launch_template](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_instance_refresh_kms_encrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_instance_refresh_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_pending_task_cloudwatch_metric_put_metric_data_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | diff --git a/data.tf b/data.tf index bace70c..23b4d0c 100644 --- a/data.tf +++ b/data.tf @@ -19,7 +19,7 @@ data "aws_ami" "ecs_cluster_ami" { filter { name = "name" values = [ - "al2023-ami-ecs-hvm-${local.infrastructure_ecs_cluster_ami_version}" + local.infrastructure_ecs_cluster_ami_name_filter ] } diff --git a/ecs-cluster-infrastructure-instance-refresh-lambda.tf b/ecs-cluster-infrastructure-instance-refresh-lambda.tf index 50aac4c..6cbe294 100644 --- a/ecs-cluster-infrastructure-instance-refresh-lambda.tf +++ b/ecs-cluster-infrastructure-instance-refresh-lambda.tf @@ -57,6 +57,20 @@ resource "aws_iam_role_policy_attachment" "ecs_cluster_infrastructure_instance_r policy_arn = aws_iam_policy.ecs_cluster_infrastructure_instance_refresh_allow_instance_refresh[0].arn } +resource "aws_iam_policy" "ecs_cluster_infrastructure_instance_refresh_allow_modify_launch_template" { + count = local.infrastructure_ecs_cluster_instance_refresh_lambda_schedule_expression != "" ? 1 : 0 + + name = "${local.resource_prefix}-ecs-cluster-infrastructure-instance-refresh-allow-modify-launch-template" + policy = templatefile("${path.root}/policies/ec2-modify-launch-template.json.tpl", {}) +} + +resource "aws_iam_role_policy_attachment" "ecs_cluster_infrastructure_instance_refresh_allow_modify_launch_template" { + count = local.infrastructure_ecs_cluster_instance_refresh_lambda_schedule_expression != "" ? 1 : 0 + + role = aws_iam_role.ecs_cluster_infrastructure_instance_refresh_lambda[0].name + policy_arn = aws_iam_policy.ecs_cluster_infrastructure_instance_refresh_allow_modify_launch_template[0].arn +} + resource "aws_iam_policy" "ecs_cluster_infrastructure_instance_refresh_kms_encrypt" { count = local.infrastructure_ecs_cluster_instance_refresh_lambda_schedule_expression != "" && local.infrastructure_kms_encryption ? 1 : 0 @@ -98,7 +112,9 @@ resource "aws_lambda_function" "ecs_cluster_infrastructure_instance_refresh" { environment { variables = { - asgName = aws_autoscaling_group.infrastructure_ecs_cluster[0].name + asgName = aws_autoscaling_group.infrastructure_ecs_cluster[0].name + launchTemplateName = aws_launch_template.infrastructure_ecs_cluster[0].name + amiVersion = local.infrastructure_ecs_cluster_ami_name_filter } } diff --git a/ecs-cluster-infrastructure.tf b/ecs-cluster-infrastructure.tf index 86f9e90..ec62f0a 100644 --- a/ecs-cluster-infrastructure.tf +++ b/ecs-cluster-infrastructure.tf @@ -176,7 +176,7 @@ resource "aws_autoscaling_group" "infrastructure_ecs_cluster" { launch_template { id = aws_launch_template.infrastructure_ecs_cluster[0].id - version = aws_launch_template.infrastructure_ecs_cluster[0].latest_version + version = "$Latest" } vpc_zone_identifier = local.infrastructure_ecs_cluster_publicly_avaialble ? [ diff --git a/lambdas/ecs-asg-instance-refresh/function.py b/lambdas/ecs-asg-instance-refresh/function.py index be2592a..afb4527 100644 --- a/lambdas/ecs-asg-instance-refresh/function.py +++ b/lambdas/ecs-asg-instance-refresh/function.py @@ -3,9 +3,62 @@ import os asgName = os.environ['asgName'] +launchTemplateName = os.environ['launchTemplateName'] +amiVersion = os.environ['amiVersion'] def lambda_handler(event, context): asgClient = boto3.client('autoscaling') + ec2Client = boto3.client('ec2') + + # Update launch template to use the latest AMI + response = ec2Client.describe_images( + Owners=['amazon'], + Filters=[ + {'Name': 'name', 'Values': [amiVersion]}, + {'Name': 'state', 'Values': ['available']}, + {'Name': 'architecture', 'Values': ['x86_64']} + ] + ) + + images = sorted( + response['Images'], + key=lambda x: x['CreationDate'], + reverse=True + ) + if not images: + raise Exception("No AMIs found!") + + latest_ami_id = images[0]['ImageId'] + print(f"Latest ECS-optimized AMI: {latest_ami_id}") + + try: + template_response = ec2Client.describe_launch_template_versions( + LaunchTemplateName=launchTemplateName, + Versions=["$Latest"] + ) + template_data = template_response['LaunchTemplateVersions'][0]['LaunchTemplateData'] + except Exception as e: + print(f"Error retrieving launch template: {e}") + raise + + print(f"Currnt AMI ID: {template_data['ImageId']}") + + if template_data['ImageId'] != latest_ami_id: + template_data['ImageId'] = latest_ami_id + + try: + new_version_response = ec2Client.create_launch_template_version( + LaunchTemplateName=launchTemplateName, + SourceVersion="$Latest", + LaunchTemplateData=template_data + ) + new_version_number = new_version_response['LaunchTemplateVersion']['VersionNumber'] + print(f"Created new version: {new_version_number}") + except Exception as e: + print(f"Error creating new launch template version: {e}") + raise + + # Start instance refresh try: response = asgClient.start_instance_refresh( AutoScalingGroupName=asgName, diff --git a/locals.tf b/locals.tf index 09eb62d..580b938 100644 --- a/locals.tf +++ b/locals.tf @@ -127,6 +127,7 @@ locals { enable_infrastructure_ecs_cluster = var.enable_infrastructure_ecs_cluster && local.infrastructure_vpc infrastructure_ecs_cluster_name = "${local.resource_prefix}-infrastructure" infrastructure_ecs_cluster_ami_version = var.infrastructure_ecs_cluster_ami_version + infrastructure_ecs_cluster_ami_name_filter = "al2023-ami-ecs-hvm-${local.infrastructure_ecs_cluster_ami_version}" infrastructure_ecs_cluster_ebs_docker_storage_volume_device_name = "/dev/xvdcz" infrastructure_ecs_cluster_ebs_docker_storage_volume_size = var.infrastructure_ecs_cluster_ebs_docker_storage_volume_size infrastructure_ecs_cluster_ebs_docker_storage_volume_type = var.infrastructure_ecs_cluster_ebs_docker_storage_volume_type diff --git a/policies/ec2-modify-launch-template.json.tpl b/policies/ec2-modify-launch-template.json.tpl new file mode 100644 index 0000000..039e12d --- /dev/null +++ b/policies/ec2-modify-launch-template.json.tpl @@ -0,0 +1,14 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeImages", + "ec2:DescribeLaunchTemplateVersions", + "ec2:CreateLaunchTemplateVersion" + ], + "Resource": "*" + } + ] +}