# terraform-chef-bootstrap

This module generates a user-data script for EC2 instances to bootstrap them with Chef/Cinc configuration management.

## Features

- **Cinc/Chef client installation** - Automatic download and installation of Cinc client
- **IMDSv2 support** - Secure instance metadata service v2 token-based authentication
- **Block device configuration** - Optional EBS and instance store volume discovery, formatting, and mounting
- **Bash strict mode** - Uses `set -euo pipefail` for reliable error handling
- **Automatic retries** - Cinc client bootstrap retries once on failure
- **Secure file permissions** - Sensitive files (validator keys, secrets) are chmod 600

## Variables

### Required

| Variable          | Description                                           | Example                                                    |
| ----------------- | ----------------------------------------------------- | ---------------------------------------------------------- |
| chef_role         | Name of Chef role to run                              | `"web_server"`                                             |
| aws_instance_name | Name used for Chef node name                          | `"myapp-production"`                                       |
| aws_iam_role_id   | IAM role ID for instances (needs S3 access)           | `aws_iam_role.instance.name`                               |

### Optional - Chef Configuration

| Variable                         | Description                              | Default                                              |
| -------------------------------- | ---------------------------------------- | ---------------------------------------------------- |
| chef_version                     | Version of Cinc/Chef client              | `"18.8.11"`                                          |
| chef_server                      | Name of Chef server                      | `"shared-chef-server"`                               |
| chef_server_url                  | Chef server URL                          | `"https://chef.theorchard.io/organizations/orchard"` |
| chef_environment                 | Chef environment                         | `"_default"`                                         |
| s3_chef_bucket                   | S3 bucket with Chef validator keys       | `"shared-chef-server-secrets"`                       |
| log_file                         | Path to bootstrap log file               | `"/var/log/cinc/bootstrap.log"`                      |
| ssl_verify_mode                  | SSL verification mode                    | `"verify_peer"`                                      |
| encrypted_data_bag_secret        | Encrypted data bag secret filename       | `"encrypted_data_bag_secret"`                        |
| qualys_encrypted_data_bag_secret | Qualys data bag secret filename          | `"qualys_encrypted_data_bag_secret"`                 |
| node_attributes                  | Map of additional Chef node attributes   | `{}`                                                 |

### Optional - Block Device Configuration

| Variable                       | Description                                         | Default  |
| ------------------------------ | --------------------------------------------------- | -------- |
| ebs_data_mount_point           | Mount point for additional EBS volume (empty=skip)  | `""`     |
| instance_store_mount_point     | Mount point for instance store volume (empty=skip)  | `""`     |
| block_device_filesystem_type   | Filesystem type (ext4, xfs)                         | `"ext4"` |

## Usage

### Basic Usage

```hcl
module "chef_bootstrap" {
  source = "path/to/terraform-chef-bootstrap"

  chef_role         = "web_server"
  aws_instance_name = "myapp-${var.environment}"
  aws_iam_role_id   = aws_iam_role.instance.name
}

resource "aws_launch_template" "app" {
  name      = "myapp"
  image_id  = data.aws_ami.debian.id
  user_data = base64encode(module.chef_bootstrap.user_data_output)

  # Recommended: Enforce IMDSv2
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"
    http_put_response_hop_limit = 2
  }
}
```

### With EBS Data Volume

```hcl
module "chef_bootstrap" {
  source = "path/to/terraform-chef-bootstrap"

  chef_role         = "database"
  aws_instance_name = "postgres-${var.environment}"
  aws_iam_role_id   = aws_iam_role.instance.name

  ebs_data_mount_point         = "/data"
  block_device_filesystem_type = "xfs"
}

resource "aws_launch_template" "db" {
  # ... other config ...

  # Additional EBS volume
  block_device_mappings {
    device_name = "/dev/sdf"
    ebs {
      volume_size = 100
      volume_type = "gp3"
      encrypted   = true
    }
  }
}
```

### With Instance Store (Local NVMe)

```hcl
module "chef_bootstrap" {
  source = "path/to/terraform-chef-bootstrap"

  chef_role         = "cache_server"
  aws_instance_name = "redis-${var.environment}"
  aws_iam_role_id   = aws_iam_role.instance.name

  instance_store_mount_point = "/scratch"
}

# Use instance type with NVMe instance store (e.g., m5d, c5d, i3)
resource "aws_launch_template" "cache" {
  instance_type = "m5d.large"
  # ... other config ...
}
```

### With Both EBS and Instance Store

```hcl
module "chef_bootstrap" {
  source = "path/to/terraform-chef-bootstrap"

  chef_role         = "analytics"
  aws_instance_name = "spark-${var.environment}"
  aws_iam_role_id   = aws_iam_role.instance.name

  ebs_data_mount_point         = "/data"      # Persistent storage
  instance_store_mount_point   = "/scratch"   # Fast ephemeral storage
  block_device_filesystem_type = "xfs"
}
```

## Outputs

| Output              | Description                        |
| ------------------- | ---------------------------------- |
| user_data_output    | The rendered user-data script      |
| s3_chef_bucket_output | The S3 bucket name for Chef keys |

## Block Device Behavior

### EBS Volumes
- Automatically discovered by checking device model (`Amazon Elastic Block Store`)
- Formatted only if no existing filesystem detected
- Added to `/etc/fstab` with UUID, `noatime`, and `nofail` options
- Persists across stop/start cycles

### Instance Store Volumes
- Automatically discovered by checking device model (`Amazon EC2 NVMe Instance Storage`)
- Formatted only if no existing filesystem detected
- Added to `/etc/fstab` with `noatime`, `discard`, `nofail`, and `x-systemd.device-timeout=5`
- **Note**: Data is ephemeral - lost on stop/start. UUID changes after stop/start.

## Supported Platforms

- Debian 12 (Bookworm) - amd64
- Debian 13 (Trixie) - amd64, arm64

## Testing

### Unit Tests

Unit tests validate the user-data script rendering without deploying infrastructure:

```bash
# Run all unit tests (requires Terraform >= 1.6)
terraform test

# Run specific test file
terraform test -filter=tests/user_data_basic.tftest.hcl

# Verbose output
terraform test -verbose
```

### Integration Tests

Integration tests deploy actual EC2 instances in the `test/` directory:

```bash
cd test/
terraform init
terraform apply

# With block device tests (uses larger instance types)
terraform apply -var="block_device_tests_enabled=true"
```

## Requirements

- Terraform >= 1.6 (for unit tests)
- AWS provider
- IAM role with S3 read access to the Chef secrets bucket
