# Terraform AWS Key Pair Module

## Module Information

- **Module Name**: `key-pair`
- **Module ID**: `terraform-aws-modules/key-pair/aws`
- **Source**: `terraform-aws-modules/key-pair/aws`
- **GitHub Repository**: https://github.com/terraform-aws-modules/terraform-aws-key-pair
- **Terraform Registry**: https://registry.terraform.io/modules/terraform-aws-modules/key-pair/aws/latest
- **Latest Version**: 3.0.0
- **Purpose**: Terraform module that creates and manages a single AWS EC2 key pair for SSH access to EC2 instances
- **Service**: AWS EC2 (Elastic Compute Cloud) - Key Pairs
- **Category**: Security, Compute, Access Management
- **Keywords**: key-pair, ec2, ssh-key, rsa, ed25519, ecdsa, public-key, private-key, tls, openssh, pem, ssh-access, bastion-host, authentication, fingerprint
- **Use For**: EC2 SSH access, bastion host authentication, remote server administration, automated deployment access, CI/CD instance access, development environment setup, emergency/break-glass access, key rotation workflows, multi-account or multi-region key management

## Description

AWS EC2 key pairs are the cryptographic credentials used for SSH authentication to EC2 instances. This module wraps a single `aws_key_pair` resource (and optionally a `tls_private_key` resource) to provide a consistent, flexible way to create and manage that key pair as part of infrastructure-as-code. It supports three usage patterns: letting the module generate a brand-new private/public key pair via the `tls` provider, importing a public key generated by an external `tls_private_key` resource, or registering existing raw public key material (e.g., a key generated on a developer's laptop or by another tool).

The module supports RSA (configurable bit size, default 4096), ED25519, and ECDSA key algorithms for module-generated keys, and exposes key metadata (ARN, ID, name, MD5/SHA256 fingerprints) alongside key material in PEM and OpenSSH formats. A `region` argument allows the key pair to be managed in a Region other than the one configured on the provider, and a `create` flag allows the whole module to be toggled off since Terraform does not support `count`/`for_each` directly on `module` blocks in older syntax. An official `for_each`-based wrapper (in the `wrappers/` directory of the source repo) is available for creating multiple key pairs from a single module call.

This is a small, focused module (no submodules) intended to standardize key pair provisioning across environments and accounts, integrate with bastion hosts and automated deployment pipelines, and keep key material creation auditable and version-controlled. Because private keys generated by the module are stored in Terraform state, teams with strict security requirements typically prefer the "external public key" pattern over `create_private_key = true`.

## Key Features

- **Module-Generated Private Keys**: Set `create_private_key = true` to generate a private key in-line using the `tls` provider
- **External Public Key Import**: Pass `public_key` from a separately managed `tls_private_key` resource, keeping the private key out of this module's state
- **Existing Public Key Registration**: Pass a raw SSH public key string (e.g., from `ssh-keygen`) to register pre-existing key material
- **RSA, ED25519, and ECDSA Algorithm Support**: `private_key_algorithm` accepts `RSA` (default, configurable bit size via `private_key_rsa_bits`, default 4096), `ED25519`, or `ECDSA` (curve via `private_key_ecdsa_curve`: `P224`, `P256`, `P384`, `P521`) — note the variable description only calls out RSA/ED25519 as "currently supported" but ECDSA is wired through in `main.tf` and works
- **Conditional Creation**: `create = false` disables the module entirely, useful for environment-specific or feature-flagged deployments
- **Region Override**: `region` argument (new in v3.0.0, requires AWS provider >= 6.0) manages the key pair in a Region different from the provider default without a provider alias
- **Multiple Output Formats**: Key material available in both PEM and OpenSSH formats, plus MD5 and SHA256 fingerprints for verification
- **Dynamic Naming**: `key_name_prefix` generates a unique name automatically; conflicts with `key_name` (only set one)
- **Tagging Support**: `tags` map applied to the `aws_key_pair` resource
- **`for_each` Wrapper**: Official `wrappers/` submodule pattern (not a registry submodule) enables creating many key pairs from one module block via a `for_each` map of `items`

## Main Use Cases

1. **EC2 Instance SSH Access**: Create key pairs for secure SSH authentication to EC2 instances across environments
2. **Bastion Host Configuration**: Generate dedicated key pairs for bastion hosts and jump boxes in secure network architectures
3. **CI/CD Pipeline Access**: Provision key pairs for automated deployment systems to access EC2 instances
4. **Development Environment Setup**: Standardize key pair creation across development, staging, and production environments
5. **Multi-Account/Multi-Region Key Management**: Manage key pairs consistently across multiple AWS accounts and Regions (via `region` argument)
6. **Key Rotation Workflows**: Facilitate key rotation by programmatically creating new key pairs and deprecating old ones
7. **Emergency Access Keys**: Create and store emergency/break-glass access key pairs for disaster recovery scenarios
8. **Bulk Key Provisioning**: Use the `wrappers/` module with `for_each` to create key pairs for many instances or teams from one call
9. **Compliance and Auditing**: Track key pair creation and usage through Terraform state and AWS CloudTrail

## Main Input Variables

| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `create` | `bool` | `true` | Determines whether resources will be created |
| `create_private_key` | `bool` | `false` | Determines whether a private key will be created by the module |
| `key_name` | `string` | `null` | The name for the key pair (conflicts with `key_name_prefix`) |
| `key_name_prefix` | `string` | `null` | Creates a unique name beginning with the specified prefix (conflicts with `key_name`) |
| `public_key` | `string` | `""` | The public key material (required unless `create_private_key = true`) |
| `private_key_algorithm` | `string` | `"RSA"` | Algorithm for private key generation: `RSA`, `ED25519`, or `ECDSA` |
| `private_key_rsa_bits` | `number` | `4096` | When algorithm is `RSA`, the size of the generated key in bits |
| `private_key_ecdsa_curve` | `string` | `null` | When algorithm is `ECDSA`, the curve to use: `P224`, `P256`, `P384`, `P521` |
| `region` | `string` | `null` | Region where the resource(s) will be managed; defaults to the provider's configured Region |
| `tags` | `map(string)` | `{}` | A map of tags to add to the key pair resource |

## Main Outputs

| Output | Description |
|--------|-------------|
| `key_pair_id` | The key pair ID |
| `key_pair_arn` | The key pair ARN |
| `key_pair_name` | The key pair name |
| `key_pair_fingerprint` | The MD5 public key fingerprint (RFC 4716 format) |
| `private_key_id` | Unique identifier (SHA1 checksum in hex) of the generated private key |
| `private_key_openssh` | Private key data in OpenSSH PEM format (RFC 4716) — **SENSITIVE** |
| `private_key_pem` | Private key data in PEM format (RFC 1421) — **SENSITIVE** |
| `public_key_openssh` | Public key data in "Authorized Keys" format |
| `public_key_pem` | Public key data in PEM format (RFC 1421) |
| `public_key_fingerprint_md5` | MD5 fingerprint in `aa:bb:cc` notation |
| `public_key_fingerprint_sha256` | SHA256 fingerprint in OpenSSH format |

## Usage Examples

### Example 1: Module-Generated Key Pair

```hcl
module "key_pair" {
  source  = "terraform-aws-modules/key-pair/aws"
  version = "~> 3.0"

  key_name           = "deployer-one"
  create_private_key = true

  tags = {
    Environment = "production"
    Terraform   = "true"
  }
}

# Access the private key (store securely!)
output "private_key_pem" {
  value     = module.key_pair.private_key_pem
  sensitive = true
}
```

### Example 2: External Public Key (TLS Provider)

```hcl
resource "tls_private_key" "this" {
  algorithm = "ED25519"
}

module "key_pair" {
  source  = "terraform-aws-modules/key-pair/aws"
  version = "~> 3.0"

  key_name   = "deployer-two"
  public_key = trimspace(tls_private_key.this.public_key_openssh)

  tags = {
    Environment = "production"
  }
}
```

### Example 3: Existing Public Key Material

```hcl
module "key_pair" {
  source  = "terraform-aws-modules/key-pair/aws"
  version = "~> 3.0"

  key_name   = "deployer-three"
  public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAC...user@example.com"

  tags = {
    Environment = "production"
  }
}
```

### Example 4: Conditional Creation and Region Override

```hcl
variable "create_key_pair" {
  description = "Whether to create the key pair"
  type        = bool
  default     = true
}

module "key_pair" {
  source  = "terraform-aws-modules/key-pair/aws"
  version = "~> 3.0"

  create              = var.create_key_pair
  region              = "eu-west-1"
  key_name            = "conditional-key"
  create_private_key  = true
}
```

### Example 5: Multiple Key Pairs via `for_each` Wrapper

```hcl
module "key_pairs" {
  source  = "terraform-aws-modules/key-pair/aws//wrappers"
  version = "~> 3.0"

  for_each = {
    bastion = { key_name = "bastion", create_private_key = true }
    app     = { key_name = "app-servers", create_private_key = true }
  }

  items = each.value
}
```

## Best Practices

### Key Generation and Security

1. **Use Strong Key Algorithms**: Prefer ED25519 for new deployments, or RSA 4096-bit keys when broader tooling compatibility is required
2. **Avoid Storing Private Keys in State**: Where possible, generate keys outside Terraform (or in a separate, tightly-controlled state) and import only public keys via `public_key`
3. **Encrypt Terraform State**: Always use encrypted state backends (S3 with KMS, Terraform Cloud) when `create_private_key = true` puts private key material into state
4. **Use External Key Generation for Production**: Generate keys using secure key management tools (AWS KMS, HashiCorp Vault, `ssh-keygen`) and register only the public key
5. **Restrict State File Access**: Limit access to Terraform state files containing private keys using IAM policies and S3 bucket policies
6. **Rotate Keys Regularly**: Establish a rotation schedule (e.g., every 90-180 days) and manage it through Terraform
7. **Use Different Keys per Environment**: Create separate key pairs for development, staging, and production to limit blast radius
8. **Note the ECDSA Support Gap in Docs**: `private_key_algorithm = "ECDSA"` works functionally (wired through to `tls_private_key`) even though the variable description only mentions RSA/ED25519 — validate behavior in a test environment before relying on it
9. **Validate Key Fingerprints**: Verify `public_key_fingerprint_sha256` after creation to confirm key integrity

### Access Control and Management

1. **Implement Least Privilege**: Restrict key pair usage to only the instances and users that require access
2. **Use IAM Policies**: Control who can create, import, or delete key pairs
3. **Tag Key Pairs Consistently**: Apply tags indicating owner, environment, and purpose for tracking
4. **Use Unique Keys per Application**: Avoid sharing key pairs across unrelated applications or services
5. **Disable Unused Keys**: Remove key pairs (`create = false` or delete the resource) that are no longer in use

### Operational Excellence

1. **Version Control Key Configurations**: Store the Terraform configuration for key pairs in version control for audit trails
2. **Use Descriptive Key Names**: Name key pairs to indicate purpose, environment, and owner (e.g., `prod-bastion-ops-team`), or use `key_name_prefix` for auto-generated unique names
3. **Prefer the `region` Argument Over Provider Aliases**: For single-resource multi-region needs, set `region` directly instead of instantiating a new provider alias
4. **Use the `wrappers/` Module for Bulk Creation**: Use the official `for_each` wrapper when creating many key pairs from one call instead of duplicating module blocks
5. **Pin the Module Version**: Use `version = "~> 3.0"`; note that v3.0.0 raised the minimum Terraform version to 1.5.7 and the AWS provider to >= 6.0 (breaking change from v2.x)
6. **Test Key Access**: Validate new key pairs can authenticate to instances before distributing them

### Instance and Application Integration

1. **Consider Systems Manager Session Manager**: Prefer AWS Systems Manager Session Manager or EC2 Instance Connect over static SSH keys where possible
2. **Harden SSH Configuration**: Disable password authentication and root login on instances that use key pairs
3. **Use Bastion/Jump Hosts**: Centralize SSH entry points rather than distributing keys broadly
4. **Layer with Security Groups**: Restrict SSH access to known IP ranges or VPN/bastion endpoints regardless of key strength

### Monitoring and Compliance

1. **Enable CloudTrail Logging**: Log key pair API calls (`CreateKeyPair`, `DeleteKeyPair`, `ImportKeyPair`) for auditing
2. **Alert on New Key Pair Creation**: Use EventBridge rules or CloudWatch alarms to detect unexpected key pair creation
3. **Audit Key Usage Regularly**: Periodically review which key pairs exist and whether they are still needed
4. **Scan for Exposed Keys**: Use secret-scanning tools to catch accidentally committed private keys or Terraform state

## Additional Resources

- **Module Repository**: https://github.com/terraform-aws-modules/terraform-aws-key-pair
- **Terraform Registry**: https://registry.terraform.io/modules/terraform-aws-modules/key-pair/aws/latest
- **Module Examples**: https://github.com/terraform-aws-modules/terraform-aws-key-pair/tree/master/examples
- **CHANGELOG**: https://github.com/terraform-aws-modules/terraform-aws-key-pair/blob/master/CHANGELOG.md
- **AWS Key Pairs Documentation**: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html
- **AWS EC2 CreateKeyPair API Reference**: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateKeyPair.html
- **Terraform TLS Provider (`tls_private_key`)**: https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key
- **AWS `aws_key_pair` Resource**: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/key_pair
- **AWS Systems Manager Session Manager**: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html
- **EC2 Instance Connect**: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-connect-methods.html

## Notes for AI Agents

When using this module in automated workflows:

1. **Security First**: Never commit private keys to version control; use encrypted state backends whenever `create_private_key = true`
2. **Prefer External Generation for Production**: Generate keys outside Terraform (or in an isolated, tightly-controlled state) and import only public keys via `public_key`
3. **Default to ED25519**: Prefer ED25519 over RSA for new deployments unless broader compatibility with older tooling is required
4. **Pin Module Version**: Use `version = "~> 3.0"`; upgrading from v2.x requires Terraform >= 1.5.7 and AWS provider >= 6.0
5. **Only Set One of `key_name` / `key_name_prefix`**: These two variables conflict; setting both will error
6. **Mark Sensitive Outputs**: Always add `sensitive = true` when exposing `private_key_pem` or `private_key_openssh` as outputs
7. **Use `create` for Conditional Provisioning**: Toggle the whole module on/off with the `create` boolean instead of wrapping it in `count` at the call site
8. **Use `region` for Multi-Region Key Pairs**: Set the `region` variable directly rather than creating a separate provider alias just for this resource
9. **Use the `wrappers/` Submodule for Multiples**: Reference `terraform-aws-modules/key-pair/aws//wrappers` with `for_each` over an `items` map when provisioning many key pairs in one call
10. **Consider Alternatives**: Evaluate AWS Systems Manager Session Manager or EC2 Instance Connect as more secure alternatives to distributing static SSH key pairs
