Commit 3f6856a5 authored by Andriy Knysh's avatar Andriy Knysh Committed by GitHub

Convert to TF 0.12. Add tests. Add Codefresh test pipeline (#44)

* Convert to TF 0.12. Add tests. Add Codefresh test pipeline

* Convert to TF 0.12. Add tests. Add Codefresh test pipeline
parent 307e6300
addons:
apt:
packages:
- git
- make
- curl
install:
- make init
script:
- make terraform/install
- make terraform/get-plugins
- make terraform/get-modules
- make terraform/lint
- make terraform/validate
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
[![Cloud Posse][logo]](https://cpco.io/homepage) [![Cloud Posse][logo]](https://cpco.io/homepage)
# terraform-aws-elasticache-redis [![Build Status](https://travis-ci.org/cloudposse/terraform-aws-elasticache-redis.svg?branch=master)](https://travis-ci.org/cloudposse/terraform-aws-elasticache-redis) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-elasticache-redis.svg)](https://github.com/cloudposse/terraform-aws-elastic-beanstalk-environment/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) # terraform-aws-elasticache-redis [![Codefresh Build Status](https://g.codefresh.io/api/badges/pipeline/cloudposse/terraform-modules%2Fterraform-aws-elasticache-redis?type=cf-1)](https://g.codefresh.io/public/accounts/cloudposse/pipelines/5d23a11695dc3006a29862e2) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-elasticache-redis.svg)](https://github.com/cloudposse/terraform-aws-elastic-beanstalk-environment/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com)
Terraform module to provision an [`ElastiCache`](https://aws.amazon.com/elasticache/) Redis Cluster Terraform module to provision an [`ElastiCache`](https://aws.amazon.com/elasticache/) Redis Cluster
...@@ -57,29 +57,28 @@ resource "random_string" "auth_token" { ...@@ -57,29 +57,28 @@ resource "random_string" "auth_token" {
module "example_redis" { module "example_redis" {
source = "git::https://github.com/cloudposse/terraform-aws-elasticache-redis.git?ref=master" source = "git::https://github.com/cloudposse/terraform-aws-elasticache-redis.git?ref=master"
namespace = "general" namespace = "eg"
stage = "dev"
name = "redis" name = "redis"
stage = "prod" zone_id = var.route53_zone_id
zone_id = "${var.route53_zone_id}" security_groups = [var.security_group_id]
security_groups = ["${var.security_group_id}"]
auth_token = "${random_string.auth_token.result}" auth_token = random_string.auth_token.result
vpc_id = "${var.vpc_id}" vpc_id = var.vpc_id
subnets = "${var.private_subnets}" subnets = var.private_subnets
maintenance_window = "wed:03:00-wed:04:00" maintenance_window = "wed:03:00-wed:04:00"
cluster_size = "2" cluster_size = 2
instance_type = "cache.t2.micro" instance_type = "cache.t2.micro"
engine_version = "4.0.10" engine_version = "4.0.10"
alarm_cpu_threshold_percent = "${var.cache_alarm_cpu_threshold_percent}" alarm_cpu_threshold_percent = var.cache_alarm_cpu_threshold_percent
alarm_memory_threshold_bytes = "${var.cache_alarm_memory_threshold_bytes}" alarm_memory_threshold_bytes = var.cache_alarm_memory_threshold_bytes
apply_immediately = "true" apply_immediately = true
availability_zones = "${var.availability_zones}" availability_zones = var.availability_zones
automatic_failover = false
automatic_failover = "false"
} }
output "auth_token" { output "auth_token" {
value = "${random_string.auth_token.result}" value = random_string.auth_token.result
} }
``` ```
...@@ -106,37 +105,37 @@ Available targets: ...@@ -106,37 +105,37 @@ Available targets:
| Name | Description | Type | Default | Required | | Name | Description | Type | Default | Required |
|------|-------------|:----:|:-----:|:-----:| |------|-------------|:----:|:-----:|:-----:|
| alarm_actions | Alarm action list | list | `<list>` | no | | alarm_actions | Alarm action list | list(string) | `<list>` | no |
| alarm_cpu_threshold_percent | CPU threshold alarm level | string | `75` | no | | alarm_cpu_threshold_percent | CPU threshold alarm level | number | `75` | no |
| alarm_memory_threshold_bytes | Ram threshold alarm level | string | `10000000` | no | | alarm_memory_threshold_bytes | Ram threshold alarm level | number | `10000000` | no |
| apply_immediately | Apply changes immediately | string | `true` | no | | apply_immediately | Apply changes immediately | bool | `true` | no |
| at_rest_encryption_enabled | Enable encryption at rest | string | `false` | no | | at_rest_encryption_enabled | Enable encryption at rest | bool | `false` | no |
| attributes | Additional attributes (_e.g._ "1") | list | `<list>` | no | | attributes | Additional attributes (_e.g._ "1") | list(string) | `<list>` | no |
| auth_token | Auth token for password protecting redis, transit_encryption_enabled must be set to 'true'! Password must be longer than 16 chars | string | `` | no | | auth_token | Auth token for password protecting redis, `transit_encryption_enabled` must be set to `true`. Password must be longer than 16 chars | string | `` | no |
| automatic_failover | Automatic failover (Not available for T1/T2 instances) | string | `false` | no | | automatic_failover | Automatic failover (Not available for T1/T2 instances) | bool | `false` | no |
| availability_zones | Availability zone ids | list | `<list>` | no | | availability_zones | Availability zone IDs | list(string) | `<list>` | no |
| cluster_size | Count of nodes in cluster | string | `1` | no | | cluster_size | Count of nodes in cluster | number | `1` | no |
| delimiter | Delimiter between `name`, `namespace`, `stage` and `attributes` | string | `-` | no | | delimiter | Delimiter between `name`, `namespace`, `stage` and `attributes` | string | `-` | no |
| elasticache_subnet_group_name | Subnet group name for the ElastiCache instance | string | `` | no | | elasticache_subnet_group_name | Subnet group name for the ElastiCache instance | string | `` | no |
| enabled | Set to false to prevent the module from creating any resources | string | `true` | no | | enabled | Set to false to prevent the module from creating any resources | bool | `true` | no |
| engine_version | Redis engine version | string | `4.0.10` | no | | engine_version | Redis engine version | string | `4.0.10` | no |
| family | Redis family | string | `redis4.0` | no | | family | Redis family | string | `redis4.0` | no |
| instance_type | Elastic cache instance type | string | `cache.t2.micro` | no | | instance_type | Elastic cache instance type | string | `cache.t2.micro` | no |
| maintenance_window | Maintenance window | string | `wed:03:00-wed:04:00` | no | | maintenance_window | Maintenance window | string | `wed:03:00-wed:04:00` | no |
| name | Name | string | `redis` | no | | name | Name of the application | string | - | yes |
| namespace | Namespace | string | `global` | no | | namespace | Namespace (e.g. `eg` or `cp`) | string | `` | no |
| notification_topic_arn | Notification topic arn | string | `` | no | | notification_topic_arn | Notification topic arn | string | `` | no |
| ok_actions | The list of actions to execute when this alarm transitions into an OK state from any other state. Each action is specified as an Amazon Resource Number (ARN) | list | `<list>` | no | | ok_actions | The list of actions to execute when this alarm transitions into an OK state from any other state. Each action is specified as an Amazon Resource Number (ARN) | list(string) | `<list>` | no |
| parameter | A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another | list | `<list>` | no | | parameter | A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another | object | `<list>` | no |
| port | Redis port | string | `6379` | no | | port | Redis port | number | `6379` | no |
| replication_group_id | Replication group ID with the following constraints: A name must contain from 1 to 20 alphanumeric characters or hyphens. The first character must be a letter. A name cannot end with a hyphen or contain two consecutive hyphens. | string | `` | no | | replication_group_id | Replication group ID with the following constraints: A name must contain from 1 to 20 alphanumeric characters or hyphens. The first character must be a letter. A name cannot end with a hyphen or contain two consecutive hyphens. | string | `` | no |
| security_groups | AWS security group ids | list | `<list>` | no | | security_groups | Security Group IDs | list(string) | `<list>` | no |
| stage | Stage | string | `default` | no | | stage | Stage (e.g. `prod`, `dev`, `staging`) | string | `` | no |
| subnets | AWS subnet IDs | list | `<list>` | no | | subnets | Subnet IDs | list(string) | `<list>` | no |
| tags | Additional tags (_e.g._ map("BusinessUnit","ABC") | map | `<map>` | no | | tags | Additional tags (_e.g._ map("BusinessUnit","ABC") | map(string) | `<map>` | no |
| transit_encryption_enabled | Enable TLS | string | `true` | no | | transit_encryption_enabled | Enable TLS | bool | `true` | no |
| vpc_id | AWS VPC id | string | - | yes | | vpc_id | VPC ID | string | - | yes |
| zone_id | Route53 DNS Zone id | string | `` | no | | zone_id | Route53 DNS Zone ID | string | `` | no |
## Outputs ## Outputs
......
...@@ -32,9 +32,9 @@ github_repo: cloudposse/terraform-aws-elasticache-redis ...@@ -32,9 +32,9 @@ github_repo: cloudposse/terraform-aws-elasticache-redis
# Badges to display # Badges to display
badges: badges:
- name: "Build Status" - name: "Codefresh Build Status"
image: "https://travis-ci.org/cloudposse/terraform-aws-elasticache-redis.svg?branch=master" image: "https://g.codefresh.io/api/badges/pipeline/cloudposse/terraform-modules%2Fterraform-aws-elasticache-redis?type=cf-1"
url: "https://travis-ci.org/cloudposse/terraform-aws-elasticache-redis" url: "https://g.codefresh.io/public/accounts/cloudposse/pipelines/5d23a11695dc3006a29862e2"
- name: "Latest Release" - name: "Latest Release"
image: "https://img.shields.io/github/release/cloudposse/terraform-aws-elasticache-redis.svg" image: "https://img.shields.io/github/release/cloudposse/terraform-aws-elasticache-redis.svg"
url: "https://github.com/cloudposse/terraform-aws-elastic-beanstalk-environment/releases/latest" url: "https://github.com/cloudposse/terraform-aws-elastic-beanstalk-environment/releases/latest"
...@@ -58,29 +58,28 @@ usage: |- ...@@ -58,29 +58,28 @@ usage: |-
module "example_redis" { module "example_redis" {
source = "git::https://github.com/cloudposse/terraform-aws-elasticache-redis.git?ref=master" source = "git::https://github.com/cloudposse/terraform-aws-elasticache-redis.git?ref=master"
namespace = "general" namespace = "eg"
stage = "dev"
name = "redis" name = "redis"
stage = "prod" zone_id = var.route53_zone_id
zone_id = "${var.route53_zone_id}" security_groups = [var.security_group_id]
security_groups = ["${var.security_group_id}"]
auth_token = "${random_string.auth_token.result}" auth_token = random_string.auth_token.result
vpc_id = "${var.vpc_id}" vpc_id = var.vpc_id
subnets = "${var.private_subnets}" subnets = var.private_subnets
maintenance_window = "wed:03:00-wed:04:00" maintenance_window = "wed:03:00-wed:04:00"
cluster_size = "2" cluster_size = 2
instance_type = "cache.t2.micro" instance_type = "cache.t2.micro"
engine_version = "4.0.10" engine_version = "4.0.10"
alarm_cpu_threshold_percent = "${var.cache_alarm_cpu_threshold_percent}" alarm_cpu_threshold_percent = var.cache_alarm_cpu_threshold_percent
alarm_memory_threshold_bytes = "${var.cache_alarm_memory_threshold_bytes}" alarm_memory_threshold_bytes = var.cache_alarm_memory_threshold_bytes
apply_immediately = "true" apply_immediately = true
availability_zones = "${var.availability_zones}" availability_zones = var.availability_zones
automatic_failover = false
automatic_failover = "false"
} }
output "auth_token" { output "auth_token" {
value = "${random_string.auth_token.result}" value = random_string.auth_token.result
} }
``` ```
......
version: '1.0'
stages:
- Prepare
- Test
steps:
wait:
title: Wait
stage: Prepare
image: codefresh/cli:latest
commands:
- codefresh get builds --pipeline=${{CF_REPO_NAME}} --status running --limit 1000 -o json | jq --arg id ${{CF_BUILD_ID}} -ser 'flatten|.[-1].id==$id'
retry:
maxAttempts: 10
delay: 20
exponentialFactor: 1.1
main_clone:
title: "Clone repository"
type: git-clone
stage: Prepare
description: "Initialize"
repo: ${{CF_REPO_OWNER}}/${{CF_REPO_NAME}}
git: CF-default
revision: ${{CF_REVISION}}
clean_init:
title: Prepare build-harness and test-harness
image: ${{TEST_IMAGE}}
stage: Prepare
commands:
- cf_export PATH="/usr/local/terraform/0.12/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
- make init
- git -C build-harness checkout master
- make -C test/ clean init TEST_HARNESS_BRANCH=master
- make -C test/src clean init
- find . -type d -name '.terraform' | xargs rm -rf
- find . -type f -name 'terraform.tfstate*' -exec rm -f {} \;
test:
type: "parallel"
title: "Run tests"
description: "Run all tests in parallel"
stage: Test
steps:
test_readme_lint:
title: "Test README.md updated"
stage: "Test"
image: ${{TEST_IMAGE}}
description: Test "readme/lint"
commands:
- make readme/lint
test_module:
title: Test module with bats
image: ${{TEST_IMAGE}}
stage: Test
commands:
- make -C test/ module
test_examples_complete:
title: Test "examples/complete" with bats
image: ${{TEST_IMAGE}}
stage: Test
commands:
- make -C test/ examples/complete
test_examples_complete_terratest:
title: Test "examples/complete" with terratest
image: ${{TEST_IMAGE}}
stage: Test
commands:
- make -C test/src
...@@ -2,37 +2,37 @@ ...@@ -2,37 +2,37 @@
| Name | Description | Type | Default | Required | | Name | Description | Type | Default | Required |
|------|-------------|:----:|:-----:|:-----:| |------|-------------|:----:|:-----:|:-----:|
| alarm_actions | Alarm action list | list | `<list>` | no | | alarm_actions | Alarm action list | list(string) | `<list>` | no |
| alarm_cpu_threshold_percent | CPU threshold alarm level | string | `75` | no | | alarm_cpu_threshold_percent | CPU threshold alarm level | number | `75` | no |
| alarm_memory_threshold_bytes | Ram threshold alarm level | string | `10000000` | no | | alarm_memory_threshold_bytes | Ram threshold alarm level | number | `10000000` | no |
| apply_immediately | Apply changes immediately | string | `true` | no | | apply_immediately | Apply changes immediately | bool | `true` | no |
| at_rest_encryption_enabled | Enable encryption at rest | string | `false` | no | | at_rest_encryption_enabled | Enable encryption at rest | bool | `false` | no |
| attributes | Additional attributes (_e.g._ "1") | list | `<list>` | no | | attributes | Additional attributes (_e.g._ "1") | list(string) | `<list>` | no |
| auth_token | Auth token for password protecting redis, transit_encryption_enabled must be set to 'true'! Password must be longer than 16 chars | string | `` | no | | auth_token | Auth token for password protecting redis, `transit_encryption_enabled` must be set to `true`. Password must be longer than 16 chars | string | `` | no |
| automatic_failover | Automatic failover (Not available for T1/T2 instances) | string | `false` | no | | automatic_failover | Automatic failover (Not available for T1/T2 instances) | bool | `false` | no |
| availability_zones | Availability zone ids | list | `<list>` | no | | availability_zones | Availability zone IDs | list(string) | `<list>` | no |
| cluster_size | Count of nodes in cluster | string | `1` | no | | cluster_size | Count of nodes in cluster | number | `1` | no |
| delimiter | Delimiter between `name`, `namespace`, `stage` and `attributes` | string | `-` | no | | delimiter | Delimiter between `name`, `namespace`, `stage` and `attributes` | string | `-` | no |
| elasticache_subnet_group_name | Subnet group name for the ElastiCache instance | string | `` | no | | elasticache_subnet_group_name | Subnet group name for the ElastiCache instance | string | `` | no |
| enabled | Set to false to prevent the module from creating any resources | string | `true` | no | | enabled | Set to false to prevent the module from creating any resources | bool | `true` | no |
| engine_version | Redis engine version | string | `4.0.10` | no | | engine_version | Redis engine version | string | `4.0.10` | no |
| family | Redis family | string | `redis4.0` | no | | family | Redis family | string | `redis4.0` | no |
| instance_type | Elastic cache instance type | string | `cache.t2.micro` | no | | instance_type | Elastic cache instance type | string | `cache.t2.micro` | no |
| maintenance_window | Maintenance window | string | `wed:03:00-wed:04:00` | no | | maintenance_window | Maintenance window | string | `wed:03:00-wed:04:00` | no |
| name | Name | string | `redis` | no | | name | Name of the application | string | - | yes |
| namespace | Namespace | string | `global` | no | | namespace | Namespace (e.g. `eg` or `cp`) | string | `` | no |
| notification_topic_arn | Notification topic arn | string | `` | no | | notification_topic_arn | Notification topic arn | string | `` | no |
| ok_actions | The list of actions to execute when this alarm transitions into an OK state from any other state. Each action is specified as an Amazon Resource Number (ARN) | list | `<list>` | no | | ok_actions | The list of actions to execute when this alarm transitions into an OK state from any other state. Each action is specified as an Amazon Resource Number (ARN) | list(string) | `<list>` | no |
| parameter | A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another | list | `<list>` | no | | parameter | A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another | object | `<list>` | no |
| port | Redis port | string | `6379` | no | | port | Redis port | number | `6379` | no |
| replication_group_id | Replication group ID with the following constraints: A name must contain from 1 to 20 alphanumeric characters or hyphens. The first character must be a letter. A name cannot end with a hyphen or contain two consecutive hyphens. | string | `` | no | | replication_group_id | Replication group ID with the following constraints: A name must contain from 1 to 20 alphanumeric characters or hyphens. The first character must be a letter. A name cannot end with a hyphen or contain two consecutive hyphens. | string | `` | no |
| security_groups | AWS security group ids | list | `<list>` | no | | security_groups | Security Group IDs | list(string) | `<list>` | no |
| stage | Stage | string | `default` | no | | stage | Stage (e.g. `prod`, `dev`, `staging`) | string | `` | no |
| subnets | AWS subnet IDs | list | `<list>` | no | | subnets | Subnet IDs | list(string) | `<list>` | no |
| tags | Additional tags (_e.g._ map("BusinessUnit","ABC") | map | `<map>` | no | | tags | Additional tags (_e.g._ map("BusinessUnit","ABC") | map(string) | `<map>` | no |
| transit_encryption_enabled | Enable TLS | string | `true` | no | | transit_encryption_enabled | Enable TLS | bool | `true` | no |
| vpc_id | AWS VPC id | string | - | yes | | vpc_id | VPC ID | string | - | yes |
| zone_id | Route53 DNS Zone id | string | `` | no | | zone_id | Route53 DNS Zone ID | string | `` | no |
## Outputs ## Outputs
......
module "elasticsearch" {
source = "../../"
namespace = "eg"
stage = "dev"
name = "es"
dns_zone_id = "Z14EN2YD427LRQ"
security_groups = ["sg-XXXXXXXXX", "sg-YYYYYYYY"]
vpc_id = "vpc-XXXXXXXXX"
subnet_ids = ["subnet-XXXXXXXXX", "subnet-YYYYYYYY"]
zone_awareness_enabled = "true"
elasticsearch_version = "6.5"
instance_type = "t2.small.elasticsearch"
instance_count = 4
iam_role_arns = ["arn:aws:iam::XXXXXXXXX:role/ops", "arn:aws:iam::XXXXXXXXX:role/dev"]
iam_actions = ["es:ESHttpGet", "es:ESHttpPut", "es:ESHttpPost"]
encrypt_at_rest_enabled = "true"
kibana_subdomain_name = "kibana-es"
advanced_options = {
rest.action.multi.allow_explicit_index = "true"
}
}
region = "us-west-1"
availability_zones = ["us-west-1b", "us-west-1c"]
namespace = "eg"
stage = "test"
name = "redis-test"
instance_type = "cache.t2.micro"
cluster_size = 1
family = "redis4.0"
engine_version = "4.0.10"
at_rest_encryption_enabled = false
transit_encryption_enabled = true
zone_id = "Z3SO0TKDDQ0RGG"
provider "aws" {
region = var.region
}
module "vpc" {
source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=tags/0.7.0"
namespace = var.namespace
stage = var.stage
name = var.name
cidr_block = "172.16.0.0/16"
}
module "subnets" {
source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=tags/0.16.0"
availability_zones = var.availability_zones
namespace = var.namespace
stage = var.stage
name = var.name
vpc_id = module.vpc.vpc_id
igw_id = module.vpc.igw_id
cidr_block = module.vpc.vpc_cidr_block
nat_gateway_enabled = true
nat_instance_enabled = false
}
module "redis" {
source = "../../"
availability_zones = var.availability_zones
namespace = var.namespace
stage = var.stage
name = var.name
zone_id = var.zone_id
vpc_id = module.vpc.vpc_id
security_groups = [module.vpc.vpc_default_security_group_id]
subnets = module.subnets.private_subnet_ids
cluster_size = var.cluster_size
instance_type = var.instance_type
apply_immediately = true
automatic_failover = false
engine_version = var.engine_version
family = var.family
at_rest_encryption_enabled = var.at_rest_encryption_enabled
transit_encryption_enabled = var.transit_encryption_enabled
parameter = [
{
name = "notify-keyspace-events"
value = "lK"
}
]
}
output "public_subnet_cidrs" {
value = module.subnets.public_subnet_cidrs
description = "Public subnet CIDRs"
}
output "private_subnet_cidrs" {
value = module.subnets.private_subnet_cidrs
description = "Private subnet CIDRs"
}
output "vpc_cidr" {
value = module.vpc.vpc_cidr_block
description = "VPC CIDR"
}
output "cluster_id" {
value = module.redis.id
description = "Redis cluster ID"
}
output "cluster_security_group_id" {
value = module.redis.security_group_id
description = "Cluster Security Group ID"
}
output "cluster_host" {
value = module.redis.host
description = "Redis host"
}
variable "region" {
type = string
description = "AWS region"
}
variable "availability_zones" {
type = list(string)
description = "Availability zone IDs"
}
variable "namespace" {
type = string
description = "Namespace (e.g. `eg` or `cp`)"
}
variable "stage" {
type = string
description = "Stage (e.g. `prod`, `dev`, `staging`)"
}
variable "name" {
type = string
description = "Name (e.g. `app` or `cluster`)"
}
variable "cluster_size" {
type = number
description = "Count of nodes in cluster"
}
variable "instance_type" {
type = string
description = "Elastic cache instance type"
}
variable "family" {
type = string
description = "Redis family"
}
variable "engine_version" {
type = string
description = "Redis engine version"
}
variable "at_rest_encryption_enabled" {
type = bool
description = "Enable encryption at rest"
}
variable "transit_encryption_enabled" {
type = bool
description = "Enable TLS"
}
variable "zone_id" {
type = string
description = "Route53 DNS Zone ID"
}
terraform {
required_version = ">= 0.11.2"
backend "s3" {}
}
variable "aws_assume_role_arn" {}
provider "aws" {
assume_role {
role_arn = "${var.aws_assume_role_arn}"
}
}
variable "namespace" {}
variable "name" {}
variable "stage" {}
variable "region" {}
variable "availability_zones" {
type = "list"
}
variable "zone_id" {}
module "vpc" {
source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=tags/0.3.3"
namespace = "${var.namespace}"
stage = "${var.stage}"
name = "${var.name}"
}
module "subnets" {
source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=tags/0.3.5"
namespace = "${var.namespace}"
stage = "${var.stage}"
name = "${var.name}"
region = "${var.region}"
availability_zones = "${var.availability_zones}"
vpc_id = "${module.vpc.vpc_id}"
igw_id = "${module.vpc.igw_id}"
cidr_block = "10.0.0.0/16"
}
module "redis" {
source = "../../"
namespace = "${var.namespace}"
stage = "${var.stage}"
name = "${var.name}"
zone_id = "${var.zone_id}"
vpc_id = "${module.vpc.vpc_id}"
subnets = "${module.subnets.private_subnet_ids}"
maintenance_window = "wed:03:00-wed:04:00"
cluster_size = "2"
instance_type = "cache.t2.micro"
apply_immediately = "true"
availability_zones = "${var.availability_zones}"
automatic_failover = "false"
engine_version = "4.0.10"
family = "redis4.0"
port = "6379"
alarm_cpu_threshold_percent = "75"
alarm_memory_threshold_bytes = "10000000"
at_rest_encryption_enabled = "true"
parameter = [
{
name = "notify-keyspace-events"
value = "lK"
},
]
}
namespace = "eg"
name = "redis"
stage = "testing"
zone_id = "Z3SO0TKDDQ0RGG"
region = "us-west-2"
availability_zones = ["us-west-2a", "us-west-2b"]
# Define composite variables for resources
module "label" { module "label" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.5.3" source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.14.1"
enabled = "${var.enabled}" enabled = var.enabled
namespace = "${var.namespace}" namespace = var.namespace
name = "${var.name}" name = var.name
stage = "${var.stage}" stage = var.stage
delimiter = "${var.delimiter}" delimiter = var.delimiter
attributes = "${var.attributes}" attributes = var.attributes
tags = "${var.tags}" tags = var.tags
} }
# #
# Security Group Resources # Security Group Resources
# #
resource "aws_security_group" "default" { resource "aws_security_group" "default" {
count = "${var.enabled == "true" ? 1 : 0}" count = var.enabled ? 1 : 0
vpc_id = "${var.vpc_id}" vpc_id = var.vpc_id
name = "${module.label.id}" name = module.label.id
ingress { ingress {
from_port = "${var.port}" # Redis from_port = var.port # Redis
to_port = "${var.port}" to_port = var.port
protocol = "tcp" protocol = "tcp"
security_groups = ["${var.security_groups}"] security_groups = var.security_groups
} }
egress { egress {
...@@ -32,54 +31,61 @@ resource "aws_security_group" "default" { ...@@ -32,54 +31,61 @@ resource "aws_security_group" "default" {
cidr_blocks = ["0.0.0.0/0"] cidr_blocks = ["0.0.0.0/0"]
} }
tags = "${module.label.tags}" tags = module.label.tags
} }
locals { locals {
elasticache_subnet_group_name = "${var.elasticache_subnet_group_name != "" ? var.elasticache_subnet_group_name : join("", aws_elasticache_subnet_group.default.*.name) }" elasticache_subnet_group_name = var.elasticache_subnet_group_name != "" ? var.elasticache_subnet_group_name : join("", aws_elasticache_subnet_group.default.*.name)
} }
resource "aws_elasticache_subnet_group" "default" { resource "aws_elasticache_subnet_group" "default" {
count = "${var.enabled == "true" && var.elasticache_subnet_group_name == "" && length(var.subnets) > 0 ? 1 : 0}" count = var.enabled && var.elasticache_subnet_group_name == "" && length(var.subnets) > 0 ? 1 : 0
name = "${module.label.id}" name = module.label.id
subnet_ids = ["${var.subnets}"] subnet_ids = var.subnets
} }
resource "aws_elasticache_parameter_group" "default" { resource "aws_elasticache_parameter_group" "default" {
count = "${var.enabled == "true" ? 1 : 0}" count = var.enabled ? 1 : 0
name = "${module.label.id}" name = module.label.id
family = "${var.family}" family = var.family
parameter = "${var.parameter}"
dynamic "parameter" {
for_each = var.parameter
content {
name = parameter.value.name
value = parameter.value.value
}
}
} }
resource "aws_elasticache_replication_group" "default" { resource "aws_elasticache_replication_group" "default" {
count = "${var.enabled == "true" ? 1 : 0}" count = var.enabled ? 1 : 0
auth_token = "${var.auth_token}" auth_token = var.auth_token
replication_group_id = "${var.replication_group_id == "" ? module.label.id : var.replication_group_id}" replication_group_id = var.replication_group_id == "" ? module.label.id : var.replication_group_id
replication_group_description = "${module.label.id}" replication_group_description = module.label.id
node_type = "${var.instance_type}" node_type = var.instance_type
number_cache_clusters = "${var.cluster_size}" number_cache_clusters = var.cluster_size
port = "${var.port}" port = var.port
parameter_group_name = "${aws_elasticache_parameter_group.default.name}" parameter_group_name = join("", aws_elasticache_parameter_group.default.*.name)
availability_zones = ["${slice(var.availability_zones, 0, var.cluster_size)}"] availability_zones = slice(var.availability_zones, 0, var.cluster_size)
automatic_failover_enabled = "${var.automatic_failover}" automatic_failover_enabled = var.automatic_failover
subnet_group_name = "${local.elasticache_subnet_group_name}" subnet_group_name = local.elasticache_subnet_group_name
security_group_ids = ["${aws_security_group.default.id}"] security_group_ids = [join("", aws_security_group.default.*.id)]
maintenance_window = "${var.maintenance_window}" maintenance_window = var.maintenance_window
notification_topic_arn = "${var.notification_topic_arn}" notification_topic_arn = var.notification_topic_arn
engine_version = "${var.engine_version}" engine_version = var.engine_version
at_rest_encryption_enabled = "${var.at_rest_encryption_enabled}" at_rest_encryption_enabled = var.at_rest_encryption_enabled
transit_encryption_enabled = "${var.transit_encryption_enabled}" transit_encryption_enabled = var.transit_encryption_enabled
tags = "${module.label.tags}" tags = module.label.tags
} }
# #
# CloudWatch Resources # CloudWatch Resources
# #
resource "aws_cloudwatch_metric_alarm" "cache_cpu" { resource "aws_cloudwatch_metric_alarm" "cache_cpu" {
count = "${var.enabled == "true" ? 1 : 0}" count = var.enabled ? 1 : 0
alarm_name = "${module.label.id}-cpu-utilization" alarm_name = "${module.label.id}-cpu-utilization"
alarm_description = "Redis cluster CPU utilization" alarm_description = "Redis cluster CPU utilization"
comparison_operator = "GreaterThanThreshold" comparison_operator = "GreaterThanThreshold"
...@@ -89,19 +95,19 @@ resource "aws_cloudwatch_metric_alarm" "cache_cpu" { ...@@ -89,19 +95,19 @@ resource "aws_cloudwatch_metric_alarm" "cache_cpu" {
period = "300" period = "300"
statistic = "Average" statistic = "Average"
threshold = "${var.alarm_cpu_threshold_percent}" threshold = var.alarm_cpu_threshold_percent
dimensions { dimensions = {
CacheClusterId = "${module.label.id}" CacheClusterId = module.label.id
} }
alarm_actions = ["${var.alarm_actions}"] alarm_actions = var.alarm_actions
ok_actions = ["${var.ok_actions}"] ok_actions = var.ok_actions
depends_on = ["aws_elasticache_replication_group.default"] depends_on = [aws_elasticache_replication_group.default]
} }
resource "aws_cloudwatch_metric_alarm" "cache_memory" { resource "aws_cloudwatch_metric_alarm" "cache_memory" {
count = "${var.enabled == "true" ? 1 : 0}" count = var.enabled ? 1 : 0
alarm_name = "${module.label.id}-freeable-memory" alarm_name = "${module.label.id}-freeable-memory"
alarm_description = "Redis cluster freeable memory" alarm_description = "Redis cluster freeable memory"
comparison_operator = "LessThanThreshold" comparison_operator = "LessThanThreshold"
...@@ -111,24 +117,22 @@ resource "aws_cloudwatch_metric_alarm" "cache_memory" { ...@@ -111,24 +117,22 @@ resource "aws_cloudwatch_metric_alarm" "cache_memory" {
period = "60" period = "60"
statistic = "Average" statistic = "Average"
threshold = "${var.alarm_memory_threshold_bytes}" threshold = var.alarm_memory_threshold_bytes
dimensions { dimensions = {
CacheClusterId = "${module.label.id}" CacheClusterId = module.label.id
} }
alarm_actions = ["${var.alarm_actions}"] alarm_actions = var.alarm_actions
ok_actions = ["${var.ok_actions}"] ok_actions = var.ok_actions
depends_on = ["aws_elasticache_replication_group.default"] depends_on = [aws_elasticache_replication_group.default]
} }
module "dns" { module "dns" {
source = "git::https://github.com/cloudposse/terraform-aws-route53-cluster-hostname.git?ref=tags/0.2.6" source = "git::https://github.com/cloudposse/terraform-aws-route53-cluster-hostname.git?ref=tags/0.3.0"
enabled = "${var.enabled == "true" && length(var.zone_id) > 0 ? "true" : "false"}" enabled = var.enabled && var.zone_id != "" ? true : false
namespace = "${var.namespace}" name = var.name
name = "${var.name}"
stage = "${var.stage}"
ttl = 60 ttl = 60
zone_id = "${var.zone_id}" zone_id = var.zone_id
records = ["${aws_elasticache_replication_group.default.*.primary_endpoint_address}"] records = [join("", aws_elasticache_replication_group.default.*.primary_endpoint_address)]
} }
output "id" { output "id" {
value = "${join("", aws_elasticache_replication_group.default.*.id)}" value = join("", aws_elasticache_replication_group.default.*.id)
description = "Redis cluster ID" description = "Redis cluster ID"
} }
output "security_group_id" { output "security_group_id" {
value = "${join("", aws_security_group.default.*.id)}" value = join("", aws_security_group.default.*.id)
description = "Security group ID" description = "Security group ID"
} }
output "port" { output "port" {
value = "${var.port}" value = var.port
description = "Redis port" description = "Redis port"
} }
output "host" { output "host" {
value = "${coalesce(module.dns.hostname, join("", aws_elasticache_replication_group.default.*.primary_endpoint_address))}" value = coalesce(
module.dns.hostname,
join(
"",
aws_elasticache_replication_group.default.*.primary_endpoint_address
)
)
description = "Redis host" description = "Redis host"
} }
.test-harness
TEST_HARNESS ?= https://github.com/cloudposse/test-harness.git
TEST_HARNESS_BRANCH ?= master
TEST_HARNESS_PATH = $(realpath .test-harness)
BATS_ARGS ?= --tap
BATS_LOG ?= test.log
# Define a macro to run the tests
define RUN_TESTS
@echo "Running tests in $(1)"
@cd $(1) && bats $(BATS_ARGS) $(addsuffix .bats,$(addprefix $(TEST_HARNESS_PATH)/test/terraform/,$(TESTS)))
endef
default: all
-include Makefile.*
## Provision the test-harnesss
.test-harness:
[ -d $@ ] || git clone --depth=1 -b $(TEST_HARNESS_BRANCH) $(TEST_HARNESS) $@
## Initialize the tests
init: .test-harness
## Install all dependencies (OS specific)
deps::
@exit 0
## Clean up the test harness
clean:
[ "$(TEST_HARNESS_PATH)" == "/" ] || rm -rf $(TEST_HARNESS_PATH)
## Run all tests
all: module examples/complete
## Run basic sanity checks against the module itself
module: export TESTS ?= installed lint get-modules module-pinning get-plugins provider-pinning validate terraform-docs input-descriptions output-descriptions
module: deps
$(call RUN_TESTS, ../)
## Run tests against example
examples/complete: export TESTS ?= installed lint get-modules get-plugins validate
examples/complete: deps
$(call RUN_TESTS, ../$@)
ifneq (,$(wildcard /sbin/apk))
## Install all dependencies for alpine
deps:: init
@apk add --update terraform-docs@cloudposse json2hcl@cloudposse
endif
.gopath
vendor/
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:75d6042fc66aebc974cc49b0c6c7cc3b9adb5f8130fbfa0dbec0820d990afa25"
name = "github.com/gruntwork-io/terratest"
packages = [
"modules/collections",
"modules/customerrors",
"modules/files",
"modules/logger",
"modules/retry",
"modules/shell",
"modules/ssh",
"modules/terraform",
]
pruneopts = "UT"
revision = "892abb2c35878d0808101bbfe6559e931dc2d354"
version = "v0.16.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:5da8ce674952566deae4dbc23d07c85caafc6cfa815b0b3e03e41979cedb8750"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require",
]
pruneopts = "UT"
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
version = "v1.3.0"
[[projects]]
branch = "master"
digest = "1:831470c2758c8b733941144f2803a0ccad0632c5a767415b777ebd296b5f463e"
name = "golang.org/x/crypto"
packages = [
"curve25519",
"ed25519",
"ed25519/internal/edwards25519",
"internal/chacha20",
"internal/subtle",
"poly1305",
"ssh",
"ssh/agent",
]
pruneopts = "UT"
revision = "22d7a77e9e5f409e934ed268692e56707cd169e5"
[[projects]]
branch = "master"
digest = "1:76ee51c3f468493aff39dbacc401e8831fbb765104cbf613b89bef01cf4bad70"
name = "golang.org/x/net"
packages = ["context"]
pruneopts = "UT"
revision = "f3200d17e092c607f615320ecaad13d87ad9a2b3"
[[projects]]
branch = "master"
digest = "1:181f3fd33e620b958b5ab77da177cf775cdcccd7db82963607875fbd09ae995e"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "9cd6430ef91e39e1a0ec0470cf1321a33ef1b887"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/gruntwork-io/terratest/modules/terraform",
"github.com/stretchr/testify/assert",
]
solver-name = "gps-cdcl"
solver-version = 1
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.2"
[prune]
go-tests = true
unused-packages = true
PACKAGE = terraform-aws-elasticache-redis
GOEXE ?= /usr/bin/go
GOPATH = $(CURDIR)/.gopath
GOBIN = $(GOPATH)/bin
BASE = $(GOPATH)/src/$(PACKAGE)
PATH := $(PATH):$(GOBIN)
export TF_DATA_DIR ?= $(CURDIR)/.terraform
export TF_CLI_ARGS_init ?= -get-plugins=true
export GOPATH
.PHONY: all
## Default target
all: test
ifneq (,$(wildcard /sbin/apk))
## Install go, if not installed
$(GOEXE):
apk add --update go
endif
ifeq ($(shell uname -s),Linux)
## Install all `dep`, if not installed
$(GOBIN)/dep:
@mkdir -p $(GOBIN)
@curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
endif
## Prepare the GOPATH
$(BASE): $(GOEXE)
@mkdir -p $(dir $@)
@ln -sf $(CURDIR) $@
## Download vendor dependencies to vendor/
$(BASE)/vendor: $(BASE) $(GOBIN)/dep
cd $(BASE) && dep ensure
.PHONY : init
## Initialize tests
init: $(BASE)/vendor
.PHONY : test
## Run tests
test: init
cd $(BASE) && go test -v -timeout 30m -run TestExamplesComplete
.PHONY : clean
## Clean up files
clean:
rm -rf .gopath/ vendor/ $(TF_DATA_DIR)
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
// Test the Terraform module in examples/complete using Terratest.
func TestExamplesComplete(t *testing.T) {
t.Parallel()
terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: "../../examples/complete",
Upgrade: true,
// Variables to pass to our Terraform code using -var-file options
VarFiles: []string{"fixtures.us-west-1.tfvars"},
}
// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer terraform.Destroy(t, terraformOptions)
// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)
// Run `terraform output` to get the value of an output variable
vpcCidr := terraform.Output(t, terraformOptions, "vpc_cidr")
// Verify we're getting back the outputs we expect
assert.Equal(t, "172.16.0.0/16", vpcCidr)
// Run `terraform output` to get the value of an output variable
privateSubnetCidrs := terraform.OutputList(t, terraformOptions, "private_subnet_cidrs")
// Verify we're getting back the outputs we expect
assert.Equal(t, []string{"172.16.0.0/18", "172.16.64.0/18"}, privateSubnetCidrs)
// Run `terraform output` to get the value of an output variable
publicSubnetCidrs := terraform.OutputList(t, terraformOptions, "public_subnet_cidrs")
// Verify we're getting back the outputs we expect
assert.Equal(t, []string{"172.16.128.0/18", "172.16.192.0/18"}, publicSubnetCidrs)
// Run `terraform output` to get the value of an output variable
clusterHost := terraform.Output(t, terraformOptions, "cluster_host")
// Verify we're getting back the outputs we expect
assert.Equal(t, "redis-test.testing.cloudposse.co", clusterHost)
// Run `terraform output` to get the value of an output variable
clusterId := terraform.Output(t, terraformOptions, "cluster_id")
// Verify we're getting back the outputs we expect
assert.Equal(t, "eg-test-redis-test", clusterId)
}
variable "namespace" {
default = "global"
description = "Namespace"
}
variable "enabled" { variable "enabled" {
type = bool
description = "Set to false to prevent the module from creating any resources" description = "Set to false to prevent the module from creating any resources"
default = "true" default = true
}
variable "namespace" {
type = string
description = "Namespace (e.g. `eg` or `cp`)"
default = ""
} }
variable "stage" { variable "stage" {
default = "default" type = string
description = "Stage" description = "Stage (e.g. `prod`, `dev`, `staging`)"
default = ""
} }
variable "name" { variable "name" {
default = "redis" type = string
description = "Name" description = "Name of the application"
} }
variable "security_groups" { variable "security_groups" {
type = "list" type = list(string)
default = [] default = []
description = "AWS security group ids" description = "Security Group IDs"
} }
variable "vpc_id" { variable "vpc_id" {
description = "AWS VPC id" type = string
description = "VPC ID"
} }
variable "subnets" { variable "subnets" {
type = "list" type = list(string)
description = "AWS subnet IDs" description = "Subnet IDs"
default = [] default = []
} }
variable "elasticache_subnet_group_name" { variable "elasticache_subnet_group_name" {
type = "string" type = string
description = "Subnet group name for the ElastiCache instance" description = "Subnet group name for the ElastiCache instance"
default = "" default = ""
} }
variable "maintenance_window" { variable "maintenance_window" {
type = string
default = "wed:03:00-wed:04:00" default = "wed:03:00-wed:04:00"
description = "Maintenance window" description = "Maintenance window"
} }
variable "cluster_size" { variable "cluster_size" {
default = "1" type = number
default = 1
description = "Count of nodes in cluster" description = "Count of nodes in cluster"
} }
variable "port" { variable "port" {
default = "6379" type = number
default = 6379
description = "Redis port" description = "Redis port"
} }
variable "instance_type" { variable "instance_type" {
type = string
default = "cache.t2.micro" default = "cache.t2.micro"
description = "Elastic cache instance type" description = "Elastic cache instance type"
} }
variable "family" { variable "family" {
type = string
default = "redis4.0" default = "redis4.0"
description = "Redis family " description = "Redis family"
} }
variable "parameter" { variable "parameter" {
type = "list" type = list(object({
name = string
value = string
}))
default = [] default = []
description = "A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another" description = "A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another"
} }
variable "engine_version" { variable "engine_version" {
type = string
default = "4.0.10" default = "4.0.10"
description = "Redis engine version" description = "Redis engine version"
} }
variable "at_rest_encryption_enabled" { variable "at_rest_encryption_enabled" {
default = "false" type = bool
default = false
description = "Enable encryption at rest" description = "Enable encryption at rest"
} }
variable "transit_encryption_enabled" { variable "transit_encryption_enabled" {
default = "true" type = bool
default = true
description = "Enable TLS" description = "Enable TLS"
} }
variable "notification_topic_arn" { variable "notification_topic_arn" {
type = string
default = "" default = ""
description = "Notification topic arn" description = "Notification topic arn"
} }
variable "alarm_cpu_threshold_percent" { variable "alarm_cpu_threshold_percent" {
default = "75" type = number
default = 75
description = "CPU threshold alarm level" description = "CPU threshold alarm level"
} }
variable "alarm_memory_threshold_bytes" { variable "alarm_memory_threshold_bytes" {
# 10MB # 10MB
default = "10000000" type = number
default = 10000000
description = "Ram threshold alarm level" description = "Ram threshold alarm level"
} }
variable "alarm_actions" { variable "alarm_actions" {
type = "list" type = list(string)
description = "Alarm action list" description = "Alarm action list"
default = [] default = []
} }
variable "ok_actions" { variable "ok_actions" {
type = "list" type = list(string)
description = "The list of actions to execute when this alarm transitions into an OK state from any other state. Each action is specified as an Amazon Resource Number (ARN)" description = "The list of actions to execute when this alarm transitions into an OK state from any other state. Each action is specified as an Amazon Resource Number (ARN)"
default = [] default = []
} }
variable "apply_immediately" { variable "apply_immediately" {
default = "true" type = bool
default = true
description = "Apply changes immediately" description = "Apply changes immediately"
} }
variable "automatic_failover" { variable "automatic_failover" {
default = "false" type = bool
default = false
description = "Automatic failover (Not available for T1/T2 instances)" description = "Automatic failover (Not available for T1/T2 instances)"
} }
variable "availability_zones" { variable "availability_zones" {
type = "list" type = list(string)
description = "Availability zone ids" description = "Availability zone IDs"
default = [] default = []
} }
variable "zone_id" { variable "zone_id" {
type = string
default = "" default = ""
description = "Route53 DNS Zone id" description = "Route53 DNS Zone ID"
} }
variable "delimiter" { variable "delimiter" {
type = "string" type = string
default = "-" default = "-"
description = "Delimiter between `name`, `namespace`, `stage` and `attributes`" description = "Delimiter between `name`, `namespace`, `stage` and `attributes`"
} }
variable "attributes" { variable "attributes" {
type = "list" type = list(string)
description = "Additional attributes (_e.g._ \"1\")" description = "Additional attributes (_e.g._ \"1\")"
default = [] default = []
} }
variable "tags" { variable "tags" {
type = "map" type = map(string)
description = "Additional tags (_e.g._ map(\"BusinessUnit\",\"ABC\")" description = "Additional tags (_e.g._ map(\"BusinessUnit\",\"ABC\")"
default = {} default = {}
} }
variable "auth_token" { variable "auth_token" {
type = "string" type = string
description = "Auth token for password protecting redis, transit_encryption_enabled must be set to 'true'! Password must be longer than 16 chars" description = "Auth token for password protecting redis, `transit_encryption_enabled` must be set to `true`. Password must be longer than 16 chars"
default = "" default = ""
} }
variable "replication_group_id" { variable "replication_group_id" {
type = "string" type = string
description = "Replication group ID with the following constraints: \nA name must contain from 1 to 20 alphanumeric characters or hyphens. \n The first character must be a letter. \n A name cannot end with a hyphen or contain two consecutive hyphens." description = "Replication group ID with the following constraints: \nA name must contain from 1 to 20 alphanumeric characters or hyphens. \n The first character must be a letter. \n A name cannot end with a hyphen or contain two consecutive hyphens."
default = "" default = ""
} }
terraform {
required_version = "~> 0.12.0"
required_providers {
aws = "~> 2.0"
null = "~> 2.0"
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment