Skip to main content
This workshop assumes you’re already familiar with the techniques and concepts in the Introduction & Key Concepts section. If you haven’t reviewed it yet, start with AWS Architecture and work your way through the rest of the section to fully understand the mechanics and philosophy behind our setup.

Prerequisites

  • AWS SSO signed in: aws sso login --profile <your-profile>. See Getting Started with AWS SSO if you need help setting this up
  • Fast Foundation infrastructure repository cloned
  • You know the target account / environment / region you’ll deploy to
  • You have a VPC and at least one subnet ready (private subnet recommended)

Target folder (where to place your unit)

If your ec2 folder did not exist, add an empty ”…/ec2/_service.hcl” file with content
locals{
  service = basename(get_terragrunt_dir())
}
Pick the account, environment, and region. Example path for a dev workload:
fast-foundation-infrastructure/
└── Workloads/
    └── Development/
        └── <development_workload_account>/
            └── development
                └── <your_region>/
                    └── ec2/
                        └── service.hcl
                        └── web-ec2/
                            ├── terragrunt.hcl
                            └── inputs.hcl
If inputs.hcl isn’t there, your before-hooks will generate/sync it on terragrunt init.

Step 1 — Create terragrunt.hcl

This references the official module from the Terraform Registry and wires in Fast Foundation’s parameter sync.
terraform {
  # Use the official module directly (no wrapper)
  source = "tfr://registry.terraform.io/terraform-aws-modules/ec2-instance/aws?version=5.8.0"

  # Parameter management (before-hooks) — already present in your repo scripts
  before_hook "secrets_management" {
    commands = ["init", "plan", "apply"]
    execute  = [
      "${local.parameter_script}",
      "${local.local_file_path}",
      "${include.root.locals.project_name}-terragrunt-states",
      "${local.s3_key}",
      "${local.aws_profile_infrastructure}"
    ]
    run_on_error = false
  }
}

include "root" {
  path   = find_in_parent_folders("root.hcl")
  expose = true
}

locals {
  # Shared Configurations
  region_vars          = include.root.locals.region_vars.locals
  service_vars         = include.root.locals.service_vars.locals

  # Unit Inputs from inputs.hcl
  inputs = try(read_terragrunt_config("${get_terragrunt_dir()}/inputs.hcl").locals,{})

  # Parameter management
  s3_key                     = "${path_relative_to_include("root")}/inputs.hcl"
  local_file_path            = "${get_terragrunt_dir()}/inputs.hcl"
  aws_profile_infrastructure = "${include.root.locals.project_name}-infrastructure"
  project_name               = include.root.locals.project_name

  # Cross-platform script selection
  script_base_path = "${dirname(find_in_parent_folders("root.hcl"))}/_scripts/parameter-management"
  script_preference = get_env("TG_SCRIPT_TYPE", "auto")
  
  parameter_script = (
    local.script_preference == "powershell" ? "${local.script_base_path}.ps1" :
    local.script_preference == "python" ? "${local.script_base_path}.py" :
    local.script_preference == "bash" ? "${local.script_base_path}.sh" :
    "${local.script_base_path}.sh"  # Default fallback to bash
  )
  
  # Use folder name for a friendly resource name
  instance_name = basename(get_terragrunt_dir())
}

inputs = {
  # Minimal set for the module to launch an instance
  name          = basename(get_terragrunt_dir())            #Naming best practice
  instance_type = try(local.inputs.instance_type, "t3.micro")
  monitoring = try(local.inputs.monitoring, false)
  # key_name = try(local.inputs.key_name, "my-keypair")
  
  # Reference VPC from parameter store
  vpc_security_group_ids = [try(local.inputs.security_group_id, "sg-1234567890abcdef")]
  subnet_id = try(local.inputs.subnet_id, "subnet-1234567890abcdef")
  region = try(local.region_vars.region, "us-west-2")  
  
  tags = {
    Environment = try(local.environment_vars.environment, "development")
    Name = basename(get_terragrunt_dir()) 
    Instance = local.instance_name
    DeployedBy = "terragrunt"
  }
}

Step 2 — Define inputs.hcl

Parameter values are stored here and synced to S3. Example for development:
locals {
  instance_type = "t3.micro"
  subnet_id = "subnet-1234567890abcdef"  # Replace with a valid subnet ID
  security_group_id = "sg-1234567890abcdef"  # Replace with a valid security group ID
  monitoring = false
}

Step 3 — Deploy the instance

Run these commands from your new folder:
terragrunt init
terragrunt plan
terragrunt apply
The before-hooks will sync inputs.hcl and ensure your AWS SSO session is valid.

What you learned

  • How to use the official EC2 module directly with Terragrunt
  • How before-hooks protect you against drift and missing inputs
  • How to keep parameters in sync via S3

Cleanup

Important: Don’t forget to clean up the resources you created during this workshop to avoid unnecessary AWS charges.
To destroy the EC2 instance and all associated resources, run the following command from your deployment folder:
terragrunt destroy
When prompted, type yes to confirm the destruction of resources.