diff --git a/iam_role.tf b/iam_role.tf index 661f53e..41d7f14 100644 --- a/iam_role.tf +++ b/iam_role.tf @@ -82,3 +82,21 @@ resource "aws_iam_role_policy_attachment" "s3_access" { role = aws_iam_role.app_ec2_role.name policy_arn = aws_iam_policy.s3_app_policy.arn } + +# ----------- +# CloudWatch +# ----------- + +# Grants CloudWatch Agent permissions to create log groups/streams and put log events, +# and to send custom metrics (PutMetricData). +resource "aws_iam_role_policy_attachment" "cloudwatch_agent" { + role = aws_iam_role.app_ec2_role.name + policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" +} + +# Enables SSM connectivity (optional but highly recommended to manage the instance/agent +# without SSH and to fetch agent binaries or run commands). +resource "aws_iam_role_policy_attachment" "ssm_core" { + role = aws_iam_role.app_ec2_role.name + policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" +} diff --git a/rds.tf b/rds.tf index 60ecabb..704426d 100644 --- a/rds.tf +++ b/rds.tf @@ -26,8 +26,9 @@ resource "random_password" "rds" { # Store the generated pwd in AWS Secrets Manager resource "aws_secretsmanager_secret" "rds" { - name = "${var.name_prefix}-rds-master-password" - description = "Master password for the ${var.name_prefix} RDS instance" + name = "${var.name_prefix}-rds-master-strong-password" + description = "Master password for the ${var.name_prefix} RDS instance" + recovery_window_in_days = 0 } resource "aws_secretsmanager_secret_version" "rds" { diff --git a/route53_records.tf b/route53_records.tf new file mode 100644 index 0000000..ee05697 --- /dev/null +++ b/route53_records.tf @@ -0,0 +1,24 @@ +# Decide at plan time if we create DNS (e.g., public subnet envs) +variable "create_dns_record" { + type = bool + description = "Whether to create the Route53 A record for the app." + default = true +} + +data "aws_route53_zone" "env" { + name = var.route53_zone_name + private_zone = false +} + +# Safety check — only create the record if an IP is found +resource "aws_route53_record" "env_apex_a" { + count = var.create_dns_record != "" ? 1 : 0 + + zone_id = data.aws_route53_zone.env.zone_id + name = var.record_name + type = "A" + ttl = var.record_ttl + records = [aws_instance.app.public_ip] + + allow_overwrite = true +} diff --git a/scripts/user_data.sh b/scripts/user_data.sh index 2012d99..4727a6d 100644 --- a/scripts/user_data.sh +++ b/scripts/user_data.sh @@ -32,3 +32,36 @@ systemctl enable "${service_name}.service" systemctl restart "${service_name}.service" info "=== Web App started successfully ===" + +# --------------------------------------------------------------------------- +# CloudWatch Agent: Refresh and start using baked config (from Packer) +# --------------------------------------------------------------------------- +CWA_BIN="/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl" +CWA_CFG="/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json" + +info "=== Preparing CloudWatch Agent ===" + +# Ensure application log directory exists (for ${app_dir}/log/app.log) +install -d -m 0755 -o "${app_user}" -g "${app_group}" "${app_dir}/log" || true + +if [ -x "$CWA_BIN" ] && [ -f "$CWA_CFG" ]; then + info "CloudWatch Agent binary and config found. Enabling and refreshing..." + + # Enable on boot (idempotent) + systemctl enable amazon-cloudwatch-agent || true + + # Stop agent if running (safe if it's not) + "$CWA_BIN" -a stop || true + + # Fetch baked config and start + "$CWA_BIN" -a fetch-config -m ec2 -c file:"$CWA_CFG" -s + + # Check service status (non-fatal) + systemctl status amazon-cloudwatch-agent --no-pager || true + + info "CloudWatch Agent started with baked config." +else + err "CloudWatch Agent binary or config not found. Skipping agent start." +fi + +info "=== User data script completed successfully ===" diff --git a/variables.tf b/variables.tf index 291aca1..e894f72 100644 --- a/variables.tf +++ b/variables.tf @@ -150,6 +150,29 @@ variable "s3_prefix" { default = "" } +variable "route53_zone_name" { + description = "Public hosted zone name for this environment (e.g., dev.domain.tld or demo.domain.tld)" + type = string +} + +variable "app_public_ip" { + description = "Fallback IP if no in-plan EC2 resource is referenced" + type = string + default = "" +} + +variable "record_name" { + description = "Record name inside the zone; empty string for apex" + type = string + default = "" +} + +variable "record_ttl" { + description = "TTL for A records" + type = number + default = 300 +} + variable "tags" { description = "Common tags applied to all resources" type = map(string)