# terraform-elasticache

## Overview

> [!WARNING]
> Breaking changes! Starting with version 4.0.0, the Route 53 zone ID is automatically selected based on the environment.
> The `route53_zone_id` variable has been removed. See [Route 53 Records](#route-53-records) for more information.

> [!WARNING]
> Breaking changes! Starting with version 3.0.0, the module has been refactored to use a second provider for DNS record creation. This provider should be defined in the same directory as the module and should use the `networking` profile. See [Providers](#providers) for more information.

## Description

This module provides functionality to create Redis and Memcached Elasticache clusters.

This module follows a few conventions:

* Cache clusters will be named `var.environment-var.service_name` (e.g. qa-ows-search)
* Cache SNS notification topics will be named `var.environment-var.service_name-elasticache`
* Security groups are optional; if not provided, the module will create a security group allowing access only from inside the VPC
* Redis replication groups have cluster mode disabled. Node count corresponds to number of replica nodes - [AWS Reference](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Replication.Redis-RedisCluster.html)

## Resources

#### The module manages the following: 

* SNS topic for elasticache event notifications
* Memcached clusters
* Redis clusters
* Security groups
* Route53 DNS records (conditionally based on `route53_record_creation_enabled`)

#### The module does not manage the following: 

* Subscriptions to SNS topics
    - Use SNS topic output (`cache_notification_sns_topic`) to subscribe as needed
* Parameter groups (override by specifying existing groups if needed by setting `var.cache_parameter_group_name`)
* Subnets (override by specifying existing groups if needed by setting `var.cache_subnet_group_name`)
* Major version updates (see below)

##### Major Version Updates

AWS handles [minor and patch version upgrades](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/VersionManagement.html) automatically when the `auto_minor_version_upgrade_enabled` variable is set to `true`. As a result, the tradeoff for this functionality is that this module ignores engine version changes (in order to avoid unintentional downgrades due to drift between Terraform and the real world), which means that *major* (only) version changes should be handled via the AWS console or CLI.

## Input variables

The module includes secure defaults, and most options will not need to be overridden. There are, however, a few values that you will almost always need to specify:

```
* environment
* service_name
* cache_engine (should be redis or memcached)
```

Similarly, there are a number of variables for which you should likely provide values specific to the AWS account and region (i.e set in qa/prod):

```
* additional_cidr_blocks_enabled (only set to `true` if cache needs to be accessed outside VPC)
* additional_cidr_blocks (list of additional CIDR blocks that need access)
* cache_subnet_group_name
* vpc_id
```

The defaults of these variables are set for working dev account values and do not need to be explicitly specified. They should, however, be set for qa/prod or other AWS accounts.

Finally, there are variables that you may want to change to suit a given use case:

```
* aws_region
* cache_node_count
* cache_maintenance_window
* cache_node_type
* redis_engine_version (only applicable for Redis clusters)
* redis_snapshot_window (only applicable for Redis clusters)
* redis_snapshot_retention_limit (only applicable for Redis clusters)
* memcached_engine_version (only applicable for Memcached clusters)
```

### Route 53 Records

The module manages Route 53 records for the cluster by default. This can be disabled by setting the `route53_record_creation_enabled` variable to false.

The Route 53 Zone ID is automatically selected based on the environment. If `environment` is dev, the zone ID defaults to that of the `dev.theorchard.io` domain. Otherwise, the zone ID defaults to that of the `theorchard.io` domain.

If you require a different zone to the default selection, you can specify it using the `override_route53_zone_id` variable instead.

The Route 53 records are created in the AWS account specified by the `dns` provider. See [Providers](#providers) for more information.

### Providers

The module requires an explicitly defined second AWS provider which specifies where Route 53 records will be created.

If you are creating Route 53 records in the same account as the cluster (e.g. in dev), you can simply alias the default provider as `dns` as follows:

```hcl
provider "aws" {
  region  = var.aws_region
}

module "cache" {
  source = "git@github.com:theorchard/terraform-elasticache.git//?ref=x.x.x"

  providers = {
    aws.dns = aws
  }

  // other settings
}
```

If you are creating Route 53 records in a different account to the cluster (e.g. `theorchard.io`), you will need to define a second provider with the appropriate credentials:

```hcl
provider "aws" {
  region  = var.aws_region
}

provider "aws" {
  region  = var.aws_region
  alias   = "networking"
  profile = "networking"
}

module "cache" {
  source = "git@github.com:theorchard/terraform-elasticache.git//?ref=x.x.x"

  providers = {
    aws.dns = aws.networking
  }

  // other settings
}
```

## Outputs

Modules can also return values. This is done using output variables, which can be consumed by other pieces of terraform, and are located in outputs.tf. Examples:

```
output "memcached_cluster_address" {
  value = element(
    concat(aws_elasticache_cluster.memcached[*].cluster_address, [""]),
    0,
  )
}

output "redis_primary_endpoint_address" {
  value = element(
    concat(
      aws_elasticache_replication_group.redis[*].primary_endpoint_address,
      [""]
    ),
    0,
  )
}

output "memcached_cluster_cname_output" {
  description = "The FQDN of the memcached cluster CNAME record"
  value       = var.route53_record_creation_enabled && var.cache_engine == "memcached" ? aws_route53_record.memcached_cluster_cname[*].fqdn : []
}

output "redis_cluster_cname_output" {
  description = "The FQDN of the redis cluster CNAME record"
  value       = var.route53_record_creation_enabled && var.cache_engine == "redis" ? aws_route53_record.redis_cluster_cname[*].fqdn : []
}
```

## Usage examples

### Memcached (dev)
```hcl
provider "aws" {
  region = "us-east-1"
}

module "elasticache" {
  source = "git@github.com:theorchard/terraform-elasticache.git//?ref=x.x.x"
  
  providers = {
    aws.dns = aws
  }

  environment                 = "dev"
  service_name                = "ows-service"
  cache_engine                = "memcached"
}
```

### Memcached (prod)
```hcl
provider "aws" {
  region = "us-east-1"
}

provider "aws" {
  region  = "us-east-1"
  alias   = "networking"
  profile = "networking"
}

data "aws_route53_zone" "route53_zone" {
  name = "theorchard.io"
}

module "elasticache" {
  source = "git@github.com:theorchard/terraform-elasticache.git//?ref=x.x.x"
  
  providers = {
    aws.dns = aws.networking
  }

  environment                 = "prod"
  service_name                = "ows-service"
  cache_engine                = "memcached"
  cache_subnet_group_name     = "prod-vpc-orchard-subnet"
  vpc_id                      = "vpc-7f0e841a"
}
```

### Redis (dev)
```hcl
provider "aws" {
  region = "us-east-1"
}

module "elasticache" {
  source = "git@github.com:theorchard/terraform-elasticache.git//?ref=x.x.x"

  providers = {
    aws.dns = aws
  }

  environment                 = "dev"
  service_name                = "ows-service"
  cache_engine                = "redis"
}
```

### Redis (prod)
```hcl
provider "aws" {
  region = "us-east-1"
}

provider "aws" {
  region  = "us-east-1"
  alias   = "networking"
  profile = "networking"
}

module "elasticache" {
  source = "git@github.com:theorchard/terraform-elasticache.git//?ref=x.x.x"

  providers = {
    aws.dns = aws.networking
  }

  environment                 = "prod"
  service_name                = "ows-service"
  cache_engine                = "redis"
  cache_subnet_group_name     = "prod-vpc-orchard-subnet"
  vpc_id                      = "vpc-7f0e841a"
}
```
