in squid_app/squid_asg_construct.py [0:0]
def __init__(self, scope: core.Construct, id: str, vpc: ec2.Vpc, region: str) -> None:
super().__init__(scope, id)
# create an IAM role to attach to the squid instances
squid_iam_role = iam.Role(self,"squid-role",
assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
managed_policies=[iam.ManagedPolicy.from_aws_managed_policy_name("CloudWatchAgentServerPolicy"),
iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AmazonEC2RoleforSSM")]
)
# Add policy to allow EC2 update instance attributes
squid_iam_role.add_to_policy(statement= iam.PolicyStatement(effect=iam.Effect.ALLOW,
actions=['ec2:ModifyInstanceAttribute',],
resources=['*']
)
)
# Create bucket to hold Squid config and whitelist files
squid_config_bucket = s3.Bucket(self,"squid-config",
encryption = s3.BucketEncryption.KMS_MANAGED)
# Upload config and whiteliest files to S3 bucket
s3_deployment.BucketDeployment(self,"config",
destination_bucket=squid_config_bucket,
sources=[s3_deployment.Source.asset(path='./squid_app/squid_config_files/config_files_s3')]
)
# Provide access to EC2 instance role to read and write to bucket
squid_config_bucket.grant_read_write(identity=squid_iam_role)
# Set the AMI to the latest Amazon Linux 2
amazon_linux_2_ami = ec2.MachineImage.latest_amazon_linux(
generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
edition=ec2.AmazonLinuxEdition.STANDARD,
virtualization=ec2.AmazonLinuxVirt.HVM,
storage=ec2.AmazonLinuxStorage.GENERAL_PURPOSE
)
if vpc.public_subnets:
# Squid ASGs with desired capacity as 1 Instance in each of the AZs
self.squid_asgs = []
for count, az in enumerate(vpc.availability_zones, start=1):
asg = autoscaling.AutoScalingGroup(self,f"asg-{count}",vpc=vpc,
instance_type=ec2.InstanceType("t3.nano"),
desired_capacity=1,
max_capacity=1,
min_capacity=1,
machine_image=amazon_linux_2_ami,
role=squid_iam_role,
vpc_subnets=ec2.SubnetSelection(
availability_zones=[az],
one_per_az=True,
subnet_type=ec2.SubnetType.PUBLIC
),
health_check=autoscaling.HealthCheck.ec2(grace=core.Duration.minutes(5)),
resource_signal_count=1,
resource_signal_timeout=core.Duration.minutes(10)
)
cfn_asg : autoscaling.CfnAutoScalingGroup = asg.node.default_child
asg_logical_id = cfn_asg.logical_id
# User data: Required parameters in user data script
user_data_mappings = {"__S3BUCKET__": squid_config_bucket.bucket_name,
"__ASG__": asg_logical_id,
"__CW_ASG__": "${aws:AutoScalingGroupName}"
}
# Replace parameters with values in the user data
with open("./squid_app/squid_config_files/user_data/squid_user_data.sh", 'r') as user_data_h:
# Use a substitution
user_data_sub = core.Fn.sub(user_data_h.read(), user_data_mappings)
# Add User data to Launch Config of the autoscaling group
asg.add_user_data(user_data_sub)
# Security group attached to the ASG Squid instances
# Outbound: All allowed
# Inboud: Allowed from VPC CIDR on ports 80, 443)
asg.connections.allow_from(other=ec2.Peer.ipv4(vpc.vpc_cidr_block),
port_range=ec2.Port(
protocol=ec2.Protocol.TCP,
string_representation="HTTP from VPC",
from_port=80,
to_port=80
)
)
asg.connections.allow_from(other=ec2.Peer.ipv4(vpc.vpc_cidr_block),
port_range=ec2.Port(
protocol=ec2.Protocol.TCP,
string_representation="HTTPS from VPC",
from_port=443,
to_port=443
)
)
# Create ASG Lifecycle hook to enable updating of route table using Lambda when instance launches and is marked Healthy
autoscaling.LifecycleHook(self,f"asg-hook-{count}",
auto_scaling_group=asg,
lifecycle_transition=autoscaling.LifecycleTransition.INSTANCE_LAUNCHING,
notification_target=hooktargets.TopicHook(sns.Topic(self,f"squid-asg-{count}-lifecycle-hook-topic",
display_name=f"Squid ASG {count} Lifecycle Hook topic")
),
default_result=autoscaling.DefaultResult.ABANDON,
heartbeat_timeout=core.Duration.minutes(5)
)
# Tag ASG with the route table IDs used by the isolated and/or private subnets in the availability zone
# This tag will be used by the Squid Lambda function to identify route tables to update when alarm changes from ALARM to OK
private_subnets_in_az = []
isolated_subnets_in_az = []
route_table_ids = ''
if vpc.private_subnets:
private_subnets_in_az = vpc.select_subnets(availability_zones=[az],
subnet_type=ec2.SubnetType.PRIVATE).subnets
if vpc.isolated_subnets:
isolated_subnets_in_az = vpc.select_subnets(availability_zones=[az],
subnet_type=ec2.SubnetType.ISOLATED).subnets
non_public_subnets_in_az = isolated_subnets_in_az + private_subnets_in_az
# Loop through all non public subnets in AZ to identify route table and create a tag value string
for subnet in non_public_subnets_in_az:
if route_table_ids:
route_table_ids=f"{route_table_ids},{subnet.route_table.route_table_id}"
else:
route_table_ids=subnet.route_table.route_table_id
# Tag the ASG with route table ids
core.Tag.add(asg,
key='RouteTableIds',
value=route_table_ids,
apply_to_launched_instances=False
)
self.squid_asgs.append(asg)
else:
raise ValueError("No public subnets in VPC")