IaC - Deploying AWS EC2 user-data with Terraform

Infrastructure Developer
Search for a command to run...

Infrastructure Developer
No comments yet. Be the first to comment.
Prerequisite Install AWS Load Balancer Controller: This controller is essential for managing AWS load balancers in a Kubernetes environment. Step 1: Enable Proxy Protocol in NLB Target Group Add the following annotation to your NLB Target Group to...

AWS Managed NAT Gateway is a widely-used service that provides seamless internet access for resources within a private VPC subnet. However, its cost structure has been a pain point for many AWS users, with high fees for both hourly usage and data pro...
Configure NTP in timesyncd.conf echo "NTP=169.254.169.123" | sudo tee -a /etc/systemd/timesyncd.conf cat /etc/systemd/timesyncd.conf Enable the timesyncd.service sudo systemctl unmask systemd-timesyncd.service sudo apt install systemd-timesyncd ...

Error waiting to create Instance: Error waiting for Creating Instance: Error code 7, message: Required 'compute.subnetworks.use' permission for

automating time tracking for a more accurate record of time spent on projects

When we launch an EC2 instance, we can pass user data to the instance for performing common automated configuration tasks or running scripts after the instance boot.
With Terraform, like using the console, we could 'paste' the script we would like to use in user-data.
Here is a sample user-data embedded into tf file:
resource "aws_instance" "my-instance" {
ami = "ami-04169656fea786776"
instance_type = "t2.nano"
user_data = << EOF
#! /bin/bash
apt-get update
apt-get install -y apache2
systemctl start apache2
systemctl enable apache2
echo "<h1>Deployed via Terraform</h1>" | sudo tee /var/www/html/index.html
EOF
tags = {
Name = "Terraform"
}
}
So we prefer to use file() function.
Here is the code example of installing metricbeat on Amazon Linux.
ec2.tf
# https://aws.amazon.com/blogs/compute/query-for-the-latest-amazon-linux-ami-ids-using-aws-systems-manager-parameter-store/
data "aws_ssm_parameter" "amazonlinux2" {
name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}
resource "aws_instance" "metricbeat" {
ami = data.aws_ssm_parameter.amazonlinux2.value
associate_public_ip_address = false
iam_instance_profile = aws_iam_instance_profile.ec2_metricbeat_instance_profile.name
instance_type = var.ec2_instance_type
user_data = file("metricbeat.sh")
subnet_id = var.private_subnet
timeouts { create = "30m" }
}
metricbeat.sh
#!/bin/bash -xe
cd /tmp/
yum update -y
curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.8.0-x86_64.rpm
rpm -iv metricbeat-7.8.0-x86_64.rpm
metricbeat modules disable system
rm /etc/metricbeat/metricbeat.yml
touch /etc/metricbeat/metricbeat.yml
cat > /etc/metricbeat/metricbeat.yml << EOM
##################### Metricbeat Configuration Example #######################
# =========================== Modules configuration ============================
metricbeat.config.modules:
# Glob pattern for configuration loading
path: /etc/metricbeat/modules.d/*.yml
# Set to true to enable config reloading
reload.enabled: false
# ======================= Elasticsearch template setting =======================
setup.template:
name: "metricbeat--inframetrics"
pattern: "metricbeat--inframetrics-*"
setup.ilm.enabled: true
setup.ilm.rollover_alias: "metricbeat--inframetrics"
setup.ilm.policy_name: "metricbeat-*" # should be ALWAYS <beatsname>-*
setup.ilm.pattern: "{now/M{yyyy.MM}}-001"
# =================================== Kibana ===================================
setup.kibana:
host: "https://"
space.id: "dev"
# ---------------------------- Elasticsearch Output ----------------------------
output.elasticsearch:
hosts: ["https://"]
# Authentication credentials - either API key or username/password.
api_key: "key"
# ================================= Processors =================================
# Configure processors to enhance or manipulate events generated by the beat.
processors:
- add_host_metadata: ~
- add_docker_metadata: ~
- add_kubernetes_metadata: ~
logging.level: debug
EOM
rm /etc/metricbeat/modules.d/aws.yml.disabled
touch /etc/metricbeat/modules.d/aws.yml
cat > /etc/metricbeat/modules.d/aws.yml << \EOM
- module: aws
period: 2m
region: eu-west-1
metricsets:
- cloudwatch
metrics:
- namespace: AWS/SQS
- module: aws
period: 2m
region: eu-west-1
metricsets:
- cloudwatch
metrics:
- namespace: AWS/ECS
- module: aws
period: 300s
region: eu-west-1
metricsets:
- sqs
- module: aws
period: 86400s
region: eu-west-1
metricsets:
- s3_daily_storage
- s3_request
EOM
systemctl start metricbeat
systemctl enable metricbeat
Furthermore, at some point, we would like to pass terraform attributes or variables to the script.
templatefile() function to the rescue.
templatefile reads the file at the given path and renders its content as a template using a supplied set of template variables.
ec2.tf
# https://aws.amazon.com/blogs/compute/query-for-the-latest-amazon-linux-ami-ids-using-aws-systems-manager-parameter-store/
data "aws_ssm_parameter" "amazonlinux2" {
name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}
resource "aws_instance" "metricbeat" {
ami = data.aws_ssm_parameter.amazonlinux2.value
associate_public_ip_address = false
iam_instance_profile = aws_iam_instance_profile.ec2_metricbeat_instance_profile.name
instance_type = var.ec2_instance_type
user_data = templatefile("${path.module}/metricbeat.tpl", {
space = "${var.short_name}_${var.environment}", api_key = var.elk_api_key })
subnet_id = var.private_subnet
timeouts { create = "30m" }
}
metricbeat.tpl
#!/bin/bash -xe
cd /tmp/
yum update -y
curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.8.0-x86_64.rpm
rpm -iv metricbeat-7.8.0-x86_64.rpm
metricbeat modules disable system
rm /etc/metricbeat/metricbeat.yml
touch /etc/metricbeat/metricbeat.yml
cat > /etc/metricbeat/metricbeat.yml << EOM
##################### Metricbeat Configuration Example #######################
# =========================== Modules configuration ============================
metricbeat.config.modules:
# Glob pattern for configuration loading
path: /etc/metricbeat/modules.d/*.yml
# Set to true to enable config reloading
reload.enabled: false
# ======================= Elasticsearch template setting =======================
setup.template:
name: "metricbeat--inframetrics"
pattern: "metricbeat--inframetrics-*"
setup.ilm.enabled: true
setup.ilm.rollover_alias: "metricbeat--inframetrics"
setup.ilm.policy_name: "metricbeat-*" # should be ALWAYS <beatsname>-*
setup.ilm.pattern: "{now/M{yyyy.MM}}-001"
# =================================== Kibana ===================================
setup.kibana:
host: "https://"
space.id: ${space}
# ---------------------------- Elasticsearch Output ----------------------------
output.elasticsearch:
hosts: ["https://"]
# Authentication credentials - either API key or username/password.
api_key: ${api_key}
# ================================= Processors =================================
#Configure processors to enhance or manipulate events generated by the beat.
processors:
- add_host_metadata: ~
- add_docker_metadata: ~
- add_kubernetes_metadata: ~
logging.level: debug
EOM
rm /etc/metricbeat/modules.d/aws.yml.disabled
touch /etc/metricbeat/modules.d/aws.yml
cat > /etc/metricbeat/modules.d/aws.yml << \EOM
- module: aws
period: 2m
region: eu-west-1
metricsets:
- cloudwatch
metrics:
- namespace: AWS/SQS
- module: aws
period: 2m
region: eu-west-1
metricsets:
- cloudwatch
metrics:
- namespace: AWS/ECS
- module: aws
period: 300s
region: eu-west-1
metricsets:
- sqs
- module: aws
period: 86400s
region: eu-west-1
metricsets:
- s3_daily_storage
- s3_request
EOM
systemctl start metricbeat
systemctl enable metricbeat