diff --git a/README.md b/README.md index 1977913..911b6cb 100644 --- a/README.md +++ b/README.md @@ -15,18 +15,18 @@ module "redshift" { node_type = "ra3.xlplus" number_of_nodes = 3 - database_name = "mydb" - master_username = "mydbuser" - create_random_password = false - master_password = "MySecretPassw0rd1!" # Do better! + database_name = "mydb" + master_username = "mydbuser" + + manage_master_password = true + manage_master_password_rotation = true + master_password_rotation_schedule_expression = "rate(90 days)" encrypted = true kms_key_arn = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" - enhanced_vpc_routing = true - vpc_security_group_ids = ["sg-12345678"] - subnet_ids = ["subnet-123456", "subnet-654321"] - + vpc_id = "vpc-1234556abcdef" + enhanced_vpc_routing = true availability_zone_relocation_enabled = true snapshot_copy = { @@ -79,17 +79,19 @@ module "redshift" { # Subnet group subnet_group_name = "example-custom" subnet_group_description = "Custom subnet group for example cluster" + subnet_ids = ["subnet-123456", "subnet-654321"] subnet_group_tags = { Additional = "CustomSubnetGroup" } # Snapshot schedule - create_snapshot_schedule = true - snapshot_schedule_identifier = local.name - use_snapshot_identifier_prefix = true - snapshot_schedule_description = "Example snapshot schedule" - snapshot_schedule_definitions = ["rate(12 hours)"] - snapshot_schedule_force_destroy = true + snapshot_schedule = { + identifier = "example" + use_prefix = true + description = "Example snapshot schedule" + definitions = ["rate(12 hours)"] + force_destroy = true + } # Scheduled actions create_scheduled_action_iam_role = true @@ -98,30 +100,39 @@ module "redshift" { name = "example-pause" description = "Pause cluster every night" schedule = "cron(0 22 * * ? *)" - pause_cluster = true + target_action = { + pause_cluster = true + } } resize = { name = "example-resize" description = "Resize cluster (demo only)" schedule = "cron(00 13 * * ? *)" - resize_cluster = { - node_type = "ds2.xlarge" - number_of_nodes = 5 + target_action = { + resize_cluster = { + node_type = "ds2.xlarge" + number_of_nodes = 5 + } } } resume = { name = "example-resume" description = "Resume cluster every morning" schedule = "cron(0 12 * * ? *)" - resume_cluster = true + target_action = { + resume_cluster = true + } } } # Endpoint access - create_endpoint_access = true - endpoint_name = "example-example" - endpoint_subnet_group_name = "example-subnet-group" - endpoint_vpc_security_group_ids = ["sg-12345678"] + endpoint_access = { + example = { + name = "example-example" + subnet_group_name = "example-subnet-group" + vpc_security_group_ids = ["sg-12345678"] + } + } # Usage limits usage_limits = { @@ -177,16 +188,14 @@ module "redshift" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3 | -| [aws](#requirement\_aws) | >= 5.45 | -| [random](#requirement\_random) | >= 3.0 | +| [terraform](#requirement\_terraform) | >= 1.11 | +| [aws](#requirement\_aws) | >= 6.21 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.45 | -| [random](#provider\_random) | >= 3.0 | +| [aws](#provider\_aws) | >= 6.21 | ## Modules @@ -212,10 +221,12 @@ No modules. | [aws_redshift_subnet_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/redshift_subnet_group) | resource | | [aws_redshift_usage_limit.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/redshift_usage_limit) | resource | | [aws_secretsmanager_secret_rotation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_rotation) | resource | -| [random_password.master_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_vpc_security_group_egress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | +| [aws_vpc_security_group_ingress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | | [aws_iam_policy_document.scheduled_action](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.scheduled_action_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | +| [aws_service_principal.scheduler_redshift](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/service_principal) | data source | ## Inputs @@ -223,8 +234,7 @@ No modules. |------|-------------|------|---------|:--------:| | [allow\_version\_upgrade](#input\_allow\_version\_upgrade) | If `true`, major version upgrades can be applied during the maintenance window to the Amazon Redshift engine that is running on the cluster. Default is `true` | `bool` | `null` | no | | [apply\_immediately](#input\_apply\_immediately) | Specifies whether any cluster modifications are applied immediately, or during the next maintenance window. Default is `false` | `bool` | `null` | no | -| [aqua\_configuration\_status](#input\_aqua\_configuration\_status) | The value represents how the cluster is configured to use AQUA (Advanced Query Accelerator) after the cluster is restored. Possible values are `enabled`, `disabled`, and `auto`. Requires Cluster reboot | `string` | `null` | no | -| [authentication\_profiles](#input\_authentication\_profiles) | Map of authentication profiles to create | `any` | `{}` | no | +| [authentication\_profiles](#input\_authentication\_profiles) | Map of authentication profiles to create |
map(object({
name = optional(string) # Will fall back to key if not set
content = any
}))
| `{}` | no | | [automated\_snapshot\_retention\_period](#input\_automated\_snapshot\_retention\_period) | The number of days that automated snapshots are retained. If the value is 0, automated snapshots are disabled. Even if automated snapshots are disabled, you can still create manual snapshots when you want with create-cluster-snapshot. Default is 1 | `number` | `null` | no | | [availability\_zone](#input\_availability\_zone) | The EC2 Availability Zone (AZ) in which you want Amazon Redshift to provision the cluster. Can only be changed if `availability_zone_relocation_enabled` is `true` | `string` | `null` | no | | [availability\_zone\_relocation\_enabled](#input\_availability\_zone\_relocation\_enabled) | If `true`, the cluster can be relocated to another availability zone, either automatically by AWS or when requested. Default is `false`. Available for use on clusters from the RA3 instance family | `bool` | `null` | no | @@ -233,24 +243,19 @@ No modules. | [cloudwatch\_log\_group\_skip\_destroy](#input\_cloudwatch\_log\_group\_skip\_destroy) | Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state | `bool` | `null` | no | | [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | Additional tags to add to cloudwatch log groups created | `map(string)` | `{}` | no | | [cluster\_identifier](#input\_cluster\_identifier) | The Cluster Identifier. Must be a lower case string | `string` | `""` | no | -| [cluster\_timeouts](#input\_cluster\_timeouts) | Create, update, and delete timeout configurations for the cluster | `map(string)` | `{}` | no | +| [cluster\_timeouts](#input\_cluster\_timeouts) | Create, update, and delete timeout configurations for the cluster |
object({
create = optional(string)
update = optional(string)
delete = optional(string)
})
| `null` | no | | [cluster\_version](#input\_cluster\_version) | The version of the Amazon Redshift engine software that you want to deploy on the cluster. The version selected runs on all the nodes in the cluster | `string` | `null` | no | | [create](#input\_create) | Determines whether to create Redshift cluster and resources (affects all resources) | `bool` | `true` | no | | [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Determines whether a CloudWatch log group is created for each `var.logging.log_exports` | `bool` | `false` | no | -| [create\_endpoint\_access](#input\_create\_endpoint\_access) | Determines whether to create an endpoint access (managed VPC endpoint) | `bool` | `false` | no | | [create\_parameter\_group](#input\_create\_parameter\_group) | Determines whether to create a parameter group or use existing | `bool` | `true` | no | -| [create\_random\_password](#input\_create\_random\_password) | Determines whether to create random password for cluster `master_password` | `bool` | `true` | no | | [create\_scheduled\_action\_iam\_role](#input\_create\_scheduled\_action\_iam\_role) | Determines whether a scheduled action IAM role is created | `bool` | `false` | no | -| [create\_snapshot\_schedule](#input\_create\_snapshot\_schedule) | Determines whether to create a snapshot schedule | `bool` | `false` | no | +| [create\_security\_group](#input\_create\_security\_group) | Determines whether to create security group for Redshift cluster | `bool` | `true` | no | | [create\_subnet\_group](#input\_create\_subnet\_group) | Determines whether to create a subnet group or use existing | `bool` | `true` | no | | [database\_name](#input\_database\_name) | The name of the first database to be created when the cluster is created. If you do not provide a name, Amazon Redshift will create a default database called `dev` | `string` | `null` | no | | [default\_iam\_role\_arn](#input\_default\_iam\_role\_arn) | The Amazon Resource Name (ARN) for the IAM role that was set as default for the cluster when the cluster was created | `string` | `null` | no | | [elastic\_ip](#input\_elastic\_ip) | The Elastic IP (EIP) address for the cluster | `string` | `null` | no | -| [encrypted](#input\_encrypted) | If `true`, the data in the cluster is encrypted at rest | `bool` | `true` | no | -| [endpoint\_name](#input\_endpoint\_name) | The Redshift-managed VPC endpoint name | `string` | `""` | no | -| [endpoint\_resource\_owner](#input\_endpoint\_resource\_owner) | The Amazon Web Services account ID of the owner of the cluster. This is only required if the cluster is in another Amazon Web Services account | `string` | `null` | no | -| [endpoint\_subnet\_group\_name](#input\_endpoint\_subnet\_group\_name) | The subnet group from which Amazon Redshift chooses the subnet to deploy the endpoint | `string` | `""` | no | -| [endpoint\_vpc\_security\_group\_ids](#input\_endpoint\_vpc\_security\_group\_ids) | The security group IDs to use for the endpoint access (managed VPC endpoint) | `list(string)` | `[]` | no | +| [encrypted](#input\_encrypted) | If `true`, the data in the cluster is encrypted at rest | `bool` | `null` | no | +| [endpoint\_access](#input\_endpoint\_access) | Map of endpoint access (managed VPC endpoint) definitions to create |
map(object({
name = optional(string) # Will fall back to key if not set
resource_owner = optional(string)
subnet_group_name = string
vpc_security_group_ids = optional(list(string))
}))
| `{}` | no | | [enhanced\_vpc\_routing](#input\_enhanced\_vpc\_routing) | If `true`, enhanced VPC routing is enabled | `bool` | `null` | no | | [final\_snapshot\_identifier](#input\_final\_snapshot\_identifier) | The identifier of the final snapshot that is to be created immediately before deleting the cluster. If this parameter is provided, `skip_final_snapshot` must be `false` | `string` | `null` | no | | [iam\_role\_arns](#input\_iam\_role\_arns) | A list of IAM Role ARNs to associate with the cluster. A Maximum of 10 can be associated to the cluster at any time | `list(string)` | `[]` | no | @@ -261,47 +266,52 @@ No modules. | [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the scheduled action IAM role created | `map(string)` | `{}` | no | | [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether scheduled action the IAM role name (`iam_role_name`) is used as a prefix | `string` | `true` | no | | [kms\_key\_arn](#input\_kms\_key\_arn) | The ARN for the KMS encryption key. When specifying `kms_key_arn`, `encrypted` needs to be set to `true` | `string` | `null` | no | -| [logging](#input\_logging) | Logging configuration for the cluster | `any` | `{}` | no | +| [logging](#input\_logging) | Logging configuration for the cluster |
object({
bucket_name = optional(string)
log_destination_type = optional(string)
log_exports = optional(list(string))
s3_key_prefix = optional(string)
})
| `null` | no | | [maintenance\_track\_name](#input\_maintenance\_track\_name) | The name of the maintenance track for the restored cluster. When you take a snapshot, the snapshot inherits the MaintenanceTrack value from the cluster. The snapshot might be on a different track than the cluster that was the source for the snapshot. Default value is `current` | `string` | `null` | no | -| [manage\_master\_password](#input\_manage\_master\_password) | Whether to use AWS SecretsManager to manage the cluster admin credentials. Conflicts with `master_password`. One of `master_password` or `manage_master_password` is required unless `snapshot_identifier` is provided | `bool` | `false` | no | -| [manage\_master\_password\_rotation](#input\_manage\_master\_password\_rotation) | Whether to manage the master user password rotation. Setting this value to false after previously having been set to true will disable automatic rotation. | `bool` | `false` | no | +| [manage\_master\_password](#input\_manage\_master\_password) | Whether to use AWS SecretsManager to manage the cluster admin credentials. Conflicts with `master_password_wo`. One of `master_password_wo` or `manage_master_password` is required unless `snapshot_identifier` is provided | `bool` | `true` | no | +| [manage\_master\_password\_rotation](#input\_manage\_master\_password\_rotation) | Whether to manage the master user password rotation. Setting this value to false after previously having been set to true will disable automatic rotation | `bool` | `false` | no | | [manual\_snapshot\_retention\_period](#input\_manual\_snapshot\_retention\_period) | The default number of days to retain a manual snapshot. If the value is -1, the snapshot is retained indefinitely. This setting doesn't change the retention period of existing snapshots. Valid values are between `-1` and `3653`. Default value is `-1` | `number` | `null` | no | -| [master\_password](#input\_master\_password) | Password for the master DB user. (Required unless a `snapshot_identifier` is provided). Must contain at least 8 chars, one uppercase letter, one lowercase letter, and one number | `string` | `null` | no | -| [master\_password\_rotate\_immediately](#input\_master\_password\_rotate\_immediately) | Specifies whether to rotate the secret immediately or wait until the next scheduled rotation window. | `bool` | `null` | no | -| [master\_password\_rotation\_automatically\_after\_days](#input\_master\_password\_rotation\_automatically\_after\_days) | Specifies the number of days between automatic scheduled rotations of the secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified. | `number` | `null` | no | -| [master\_password\_rotation\_duration](#input\_master\_password\_rotation\_duration) | The length of the rotation window in hours. For example, 3h for a three hour window. | `string` | `null` | no | -| [master\_password\_rotation\_schedule\_expression](#input\_master\_password\_rotation\_schedule\_expression) | A cron() or rate() expression that defines the schedule for rotating your secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified. | `string` | `null` | no | +| [master\_password\_rotate\_immediately](#input\_master\_password\_rotate\_immediately) | Specifies whether to rotate the secret immediately or wait until the next scheduled rotation window | `bool` | `null` | no | +| [master\_password\_rotation\_automatically\_after\_days](#input\_master\_password\_rotation\_automatically\_after\_days) | Specifies the number of days between automatic scheduled rotations of the secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified | `number` | `null` | no | +| [master\_password\_rotation\_duration](#input\_master\_password\_rotation\_duration) | The length of the rotation window in hours. For example, 3h for a three hour window | `string` | `null` | no | +| [master\_password\_rotation\_schedule\_expression](#input\_master\_password\_rotation\_schedule\_expression) | A cron() or rate() expression that defines the schedule for rotating your secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified | `string` | `null` | no | | [master\_password\_secret\_kms\_key\_id](#input\_master\_password\_secret\_kms\_key\_id) | ID of the KMS key used to encrypt the cluster admin credentials secret | `string` | `null` | no | -| [master\_username](#input\_master\_username) | Username for the master DB user (Required unless a `snapshot_identifier` is provided). Defaults to `awsuser` | `string` | `"awsuser"` | no | +| [master\_password\_wo](#input\_master\_password\_wo) | Password for the master DB user. Must contain at least 8 chars, one uppercase letter, one lowercase letter, and one number | `string` | `null` | no | +| [master\_password\_wo\_version](#input\_master\_password\_wo\_version) | Used together with `master_password_wo` to trigger an update. Increment this value when an update to the `master_password_wo` is required | `string` | `null` | no | +| [master\_username](#input\_master\_username) | Username for the master DB user. Defaults to `awsuser` | `string` | `"awsuser"` | no | | [multi\_az](#input\_multi\_az) | Specifies if the Redshift cluster is multi-AZ | `bool` | `null` | no | | [node\_type](#input\_node\_type) | The node type to be provisioned for the cluster | `string` | `""` | no | | [number\_of\_nodes](#input\_number\_of\_nodes) | Number of nodes in the cluster. Defaults to 1. Note: values greater than 1 will trigger `cluster_type` to switch to `multi-node` | `number` | `1` | no | | [owner\_account](#input\_owner\_account) | The AWS customer account used to create or copy the snapshot. Required if you are restoring a snapshot you do not own, optional if you own the snapshot | `string` | `null` | no | | [parameter\_group\_description](#input\_parameter\_group\_description) | The description of the Redshift parameter group. Defaults to `Managed by Terraform` | `string` | `null` | no | -| [parameter\_group\_family](#input\_parameter\_group\_family) | The family of the Redshift parameter group | `string` | `"redshift-1.0"` | no | +| [parameter\_group\_family](#input\_parameter\_group\_family) | The family of the Redshift parameter group | `string` | `"redshift-2.0"` | no | | [parameter\_group\_name](#input\_parameter\_group\_name) | The name of the Redshift parameter group, existing or to be created | `string` | `null` | no | -| [parameter\_group\_parameters](#input\_parameter\_group\_parameters) | value | `map(any)` | `{}` | no | +| [parameter\_group\_parameters](#input\_parameter\_group\_parameters) | A list of Redshift parameters to apply |
list(object({
name = string
value = string
}))
| `null` | no | | [parameter\_group\_tags](#input\_parameter\_group\_tags) | Additional tags to add to the parameter group | `map(string)` | `{}` | no | -| [port](#input\_port) | The port number on which the cluster accepts incoming connections. Default port is 5439 | `number` | `null` | no | +| [port](#input\_port) | The port number on which the cluster accepts incoming connections. Default port is `5439` | `number` | `5439` | no | | [preferred\_maintenance\_window](#input\_preferred\_maintenance\_window) | The weekly time range (in UTC) during which automated cluster maintenance can occur. Format: `ddd:hh24:mi-ddd:hh24:mi` | `string` | `"sat:10:00-sat:10:30"` | no | -| [publicly\_accessible](#input\_publicly\_accessible) | If true, the cluster can be accessed from a public network | `bool` | `false` | no | -| [random\_password\_length](#input\_random\_password\_length) | Length of random password to create. Defaults to `16` | `number` | `16` | no | -| [scheduled\_actions](#input\_scheduled\_actions) | Map of maps containing scheduled action definitions | `any` | `{}` | no | +| [publicly\_accessible](#input\_publicly\_accessible) | If true, the cluster can be accessed from a public network | `bool` | `null` | no | +| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no | +| [scheduled\_actions](#input\_scheduled\_actions) | Map of scheduled action definitions to create |
map(object({
name = optional(string) # Will fall back to key if not set
description = optional(string)
enable = optional(bool)
start_time = optional(string)
end_time = optional(string)
schedule = string
iam_role = optional(string)
target_action = object({
pause_cluster = optional(bool, false)
resize_cluster = optional(object({
classic = optional(bool)
cluster_type = optional(string)
node_type = optional(string)
number_of_nodes = optional(number)
}))
resume_cluster = optional(bool, false)
})
}))
| `{}` | no | +| [security\_group\_description](#input\_security\_group\_description) | The description of the security group. If value is set to empty string it will contain cluster name in the description | `string` | `null` | no | +| [security\_group\_egress\_rules](#input\_security\_group\_egress\_rules) | Map of security group egress rules to add to the security group created |
map(object({
name = optional(string)

cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(number)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
region = optional(string)
tags = optional(map(string), {})
to_port = optional(number)
}))
| `{}` | no | +| [security\_group\_ingress\_rules](#input\_security\_group\_ingress\_rules) | Map of security group ingress rules to add to the security group created |
map(object({
name = optional(string)

cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(number)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
region = optional(string)
tags = optional(map(string), {})
to_port = optional(number)
}))
| `{}` | no | +| [security\_group\_name](#input\_security\_group\_name) | The security group name | `string` | `""` | no | +| [security\_group\_tags](#input\_security\_group\_tags) | Additional tags for the security group | `map(string)` | `{}` | no | +| [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no | | [skip\_final\_snapshot](#input\_skip\_final\_snapshot) | Determines whether a final snapshot of the cluster is created before Redshift deletes the cluster. If true, a final cluster snapshot is not created. If false , a final cluster snapshot is created before the cluster is deleted | `bool` | `true` | no | +| [snapshot\_arn](#input\_snapshot\_arn) | The ARN of the snapshot from which to create the new cluster. Conflicts with `snapshot_identifier` | `string` | `null` | no | | [snapshot\_cluster\_identifier](#input\_snapshot\_cluster\_identifier) | The name of the cluster the source snapshot was created from | `string` | `null` | no | -| [snapshot\_copy](#input\_snapshot\_copy) | Configuration of automatic copy of snapshots from one region to another | `any` | `{}` | no | -| [snapshot\_identifier](#input\_snapshot\_identifier) | The name of the snapshot from which to create the new cluster | `string` | `null` | no | -| [snapshot\_schedule\_definitions](#input\_snapshot\_schedule\_definitions) | The definition of the snapshot schedule. The definition is made up of schedule expressions, for example `cron(30 12 *)` or `rate(12 hours)` | `list(string)` | `[]` | no | -| [snapshot\_schedule\_description](#input\_snapshot\_schedule\_description) | The description of the snapshot schedule | `string` | `null` | no | -| [snapshot\_schedule\_force\_destroy](#input\_snapshot\_schedule\_force\_destroy) | Whether to destroy all associated clusters with this snapshot schedule on deletion. Must be enabled and applied before attempting deletion | `bool` | `null` | no | -| [snapshot\_schedule\_identifier](#input\_snapshot\_schedule\_identifier) | The snapshot schedule identifier | `string` | `null` | no | +| [snapshot\_copy](#input\_snapshot\_copy) | Configuration of automatic copy of snapshots from one region to another |
object({
destination_region = string
manual_snapshot_retention_period = optional(number)
retention_period = optional(number)
grant_name = optional(string)
})
| `null` | no | +| [snapshot\_identifier](#input\_snapshot\_identifier) | The name of the snapshot from which to create the new cluster. Conflicts with `snapshot_arn` | `string` | `null` | no | +| [snapshot\_schedule](#input\_snapshot\_schedule) | Configuration for creating a snapshot schedule and associating it with the cluster |
object({
definitions = list(string)
description = optional(string)
force_destroy = optional(bool)
use_prefix = optional(bool, false)
identifier = optional(string)
tags = optional(map(string), {})
})
| `null` | no | | [subnet\_group\_description](#input\_subnet\_group\_description) | The description of the Redshift Subnet group. Defaults to `Managed by Terraform` | `string` | `null` | no | | [subnet\_group\_name](#input\_subnet\_group\_name) | The name of the Redshift subnet group, existing or to be created | `string` | `null` | no | | [subnet\_group\_tags](#input\_subnet\_group\_tags) | Additional tags to add to the subnet group | `map(string)` | `{}` | no | | [subnet\_ids](#input\_subnet\_ids) | An array of VPC subnet IDs to use in the subnet group | `list(string)` | `[]` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | -| [usage\_limits](#input\_usage\_limits) | Map of usage limit definitions to create | `any` | `{}` | no | -| [use\_snapshot\_identifier\_prefix](#input\_use\_snapshot\_identifier\_prefix) | Determines whether the identifier (`snapshot_schedule_identifier`) is used as a prefix | `bool` | `true` | no | +| [usage\_limits](#input\_usage\_limits) | Map of usage limit definitions to create |
map(object({
amount = number
breach_action = optional(string)
feature_type = string
limit_type = optional(string) # Will fall back to key if not set
period = optional(string)
tags = optional(map(string), {})
}))
| `{}` | no | +| [vpc\_id](#input\_vpc\_id) | ID of the VPC where to create security group | `string` | `""` | no | | [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | A list of Virtual Private Cloud (VPC) security groups to be associated with the cluster | `list(string)` | `[]` | no | ## Outputs @@ -334,10 +344,7 @@ No modules. | [cluster\_type](#output\_cluster\_type) | The Redshift cluster type | | [cluster\_version](#output\_cluster\_version) | The version of Redshift engine software | | [cluster\_vpc\_security\_group\_ids](#output\_cluster\_vpc\_security\_group\_ids) | The VPC security group ids associated with the cluster | -| [endpoint\_access\_address](#output\_endpoint\_access\_address) | The DNS address of the endpoint | -| [endpoint\_access\_id](#output\_endpoint\_access\_id) | The Redshift-managed VPC endpoint name | -| [endpoint\_access\_port](#output\_endpoint\_access\_port) | The port number on which the cluster accepts incoming connections | -| [endpoint\_access\_vpc\_endpoint](#output\_endpoint\_access\_vpc\_endpoint) | The connection endpoint for connecting to an Amazon Redshift cluster through the proxy. See details below | +| [endpoint\_access](#output\_endpoint\_access) | A map of access endpoints created and their attributes | | [master\_password\_secret\_arn](#output\_master\_password\_secret\_arn) | ARN of managed master password secret | | [parameter\_group\_arn](#output\_parameter\_group\_arn) | Amazon Resource Name (ARN) of the parameter group created | | [parameter\_group\_id](#output\_parameter\_group\_id) | The name of the Redshift parameter group created | diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index fc9408c..201bf77 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -115,6 +115,7 @@ module "redshift" { + snapshot_copy_grant_name = "ex-complete-us-east-1" } ``` + The `aws_redshift_logging` can be applied or imported. If setting the `log_destination_type`, an apply following an import will be required to clear the remaining diff. The `aws_redshift_snapshot_copy` resource requires importing if an existing snapshot_copy configuration exists. diff --git a/docs/UPGRADE-7.0.md b/docs/UPGRADE-7.0.md new file mode 100644 index 0000000..0e96b55 --- /dev/null +++ b/docs/UPGRADE-7.0.md @@ -0,0 +1,207 @@ +# Upgrade from v6.x to v7.x + +Please consult the `examples` directory for reference example configurations. If you find a bug, please open an issue with supporting configuration to reproduce. + +## List of backwards incompatible changes + +- Terraform `v1.11` is now minimum supported version to support write-only (`wo_*`) attributes. +- AWS provider `v6.18` is now minimum supported version +- The ability for the module to create a random password has been removed in order to ensure passwords are not stored in plain text within the state file. Users must now provide their own password via the `master_password_wo` variable. + - `master_password` is no longer supported and only the write-only equivalent is supported (`master_password_wo` and `master_password_wo_version`) + - `manage_master_password` default changed from `false` to `true` to ensure password rotation is managed by default. +- The variable(s) used to create access endpoints has changed from creating a single endpoint to n-number of endpoints + +## Additional changes + +### Added + +- Support for `region` argument to specify the AWS region for the resources created if different from the provider region. +- Support for creating a security group used by the cluster + +### Modified + +- Variable definitions now contain detailed `object` types in place of the previously used any type. +- Default value for `parameter_group_family` changed from `redshift-1.0` to `redshift-2.0` +- `manage_master_password` default changed from `false` to `true` + +### Removed + +- Support for generating random passwords has been removed. + +### Variable and output changes + +1. Removed variables: + + - `create_random_password` removed along with support for generating a random password + - `random_password_length` removed along with support for generating a random password + - `aqua_configuration_status` argument was deprecated + - The variables for endpoint access have been nested under a single, top-level `endpoint_access` variable: + - `create_endpoint_access` removed - set `endpoint_access` to `null` or omit to disable + - `endpoint_name` -> `endpoint_access.name` + - `endpoint_resource_owner` -> `endpoint_access.resource_owner` + - `endpoint_subnet_group_name` -> `endpoint_access.subnet_group_name` + - `endpoint_vpc_security_group_ids` -> `endpoint_access.vpc_security_group_ids` + - The variables for snapshot schedule have been nested under a single, top-level `snapshot_schedule` variable: + - `create_snapshot_schedule` removed - set `snapshot_schedule` to `null` or omit to disable + - `snapshot_schedule_identifier` -> `snapshot_schedule.identifier` + - `use_snapshot_identifier_prefix` -> `snapshot_schedule.use_prefix` + - `snapshot_schedule_description` -> `snapshot_schedule.description` + - `snapshot_schedule_definitions` -> `snapshot_schedule.definitions` + - `snapshot_schedule_force_destroy` -> `snapshot_schedule.force_destroy` + +2. Renamed variables: + + - `master_password` -> `master_password_wo` + +3. Added variables: + + - `region` + - `create_security_group` + - `security_group_name` + - `security_group_use_name_prefix` + - `security_group_description` + - `vpc_id` + - `security_group_ingress_rules` + - `security_group_egress_rules` + - `master_password_wo_version` + +4. Removed outputs: + + - `endpoint_access_address` -> see `endpoint_access` output + - `endpoint_access_port` -> see `endpoint_access` output + - `endpoint_access_id` -> see `endpoint_access` output + - `endpoint_access_vpc_endpoint` -> see `endpoint_access` output + +5. Renamed outputs: + + - None + +6. Added outputs: + + - None + +## Upgrade Migration + +### Before v6.x Example + +```hcl +module "redshift" { + source = "terraform-aws-modules/redshift/aws" + version = "~> 6.0" + + # Only the affected attributes are shown + + # Snapshot schedule + create_snapshot_schedule = true + snapshot_schedule_identifier = "example" + use_snapshot_identifier_prefix = true + snapshot_schedule_description = "Example snapshot schedule" + snapshot_schedule_definitions = ["rate(12 hours)"] + snapshot_schedule_force_destroy = true + + # Scheduled actions + create_scheduled_action_iam_role = true + scheduled_actions = { + pause = { + name = "example-pause" + description = "Pause cluster every night" + schedule = "cron(0 22 * * ? *)" + pause_cluster = true + } + resize = { + name = "example-resize" + description = "Resize cluster (demo only)" + schedule = "cron(00 13 * * ? *)" + resize_cluster = { + node_type = "ds2.xlarge" + number_of_nodes = 5 + } + } + resume = { + name = "example-resume" + description = "Resume cluster every morning" + schedule = "cron(0 12 * * ? *)" + resume_cluster = true + } + } + + # Endpoint access - only available when using the ra3.x type + create_endpoint_access = true + endpoint_name = "example" + endpoint_subnet_group_name = "example" + endpoint_vpc_security_group_ids = ["sg-12345678"] +} +``` + +### After v7.x Example + +```hcl +module "redshift" { + source = "terraform-aws-modules/redshift/aws" + version = "~> 7.0" + + # Only the affected attributes are shown + + # Security group + vpc_id = "vpc-1234556abcdef" + + # Snapshot schedule + snapshot_schedule = { + identifier = "example" + use_prefix = true + description = "Example snapshot schedule" + definitions = ["rate(12 hours)"] + force_destroy = true + } + + # Scheduled actions + create_scheduled_action_iam_role = true + scheduled_actions = { + pause = { + name = "example-pause" + description = "Pause cluster every night" + schedule = "cron(0 22 * * ? *)" + target_action = { + pause_cluster = true + } + } + resize = { + name = "example-resize" + description = "Resize cluster (demo only)" + schedule = "cron(00 13 * * ? *)" + target_action = { + resize_cluster = { + node_type = "ds2.xlarge" + number_of_nodes = 5 + } + } + } + resume = { + name = "example-resume" + description = "Resume cluster every morning" + schedule = "cron(0 12 * * ? *)" + target_action = { + resume_cluster = true + } + } + } + + # Endpoint access - only available when using the ra3.x type + endpoint_access = { + example = { + name = "example" + subnet_group_name = "example" + vpc_security_group_ids = ["sg-12345678"] + } + } + + # Maintains backward compatibility, as needed + parameter_group_family = "redshift-1.0" +} +``` + +### State Move Commands + +```sh +terraform state mv 'module.redshift.aws_redshift_endpoint_access.this[0]' 'module.redshift.aws_redshift_endpoint_access.this["example"]' +``` diff --git a/examples/complete/README.md b/examples/complete/README.md index f084b40..4a4ad91 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -23,15 +23,14 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3 | -| [aws](#requirement\_aws) | >= 5.45 | +| [terraform](#requirement\_terraform) | >= 1.11 | +| [aws](#requirement\_aws) | >= 6.21 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.45 | -| [aws.us\_east\_1](#provider\_aws.us\_east\_1) | >= 5.45 | +| [aws](#provider\_aws) | >= 6.21 | ## Modules @@ -40,9 +39,9 @@ Note that this example may create resources which cost money. Run `terraform des | [default](#module\_default) | ../../ | n/a | | [disabled](#module\_disabled) | ../../ | n/a | | [redshift](#module\_redshift) | ../../ | n/a | -| [s3\_logs](#module\_s3\_logs) | terraform-aws-modules/s3-bucket/aws | ~> 3.0 | +| [s3\_logs](#module\_s3\_logs) | terraform-aws-modules/s3-bucket/aws | ~> 5.0 | | [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws//modules/redshift | ~> 5.0 | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | | [with\_cloudwatch\_logging](#module\_with\_cloudwatch\_logging) | ../../ | n/a | ## Resources @@ -89,10 +88,7 @@ No inputs. | [cluster\_type](#output\_cluster\_type) | The Redshift cluster type | | [cluster\_version](#output\_cluster\_version) | The version of Redshift engine software | | [cluster\_vpc\_security\_group\_ids](#output\_cluster\_vpc\_security\_group\_ids) | The VPC security group ids associated with the cluster | -| [endpoint\_access\_address](#output\_endpoint\_access\_address) | The DNS address of the endpoint | -| [endpoint\_access\_id](#output\_endpoint\_access\_id) | The Redshift-managed VPC endpoint name | -| [endpoint\_access\_port](#output\_endpoint\_access\_port) | The port number on which the cluster accepts incoming connections | -| [endpoint\_access\_vpc\_endpoint](#output\_endpoint\_access\_vpc\_endpoint) | The connection endpoint for connecting to an Amazon Redshift cluster through the proxy. See details below | +| [endpoint\_access](#output\_endpoint\_access) | A map of access endpoints created and their attributes | | [master\_password\_secret\_arn](#output\_master\_password\_secret\_arn) | ARN of managed master password secret | | [master\_password\_secretsmanager\_secret\_rotation\_enabled](#output\_master\_password\_secretsmanager\_secret\_rotation\_enabled) | Specifies whether automatic rotation is enabled for the secret | | [parameter\_group\_arn](#output\_parameter\_group\_arn) | Amazon Resource Name (ARN) of the parameter group created | diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 6f7ce18..9b50812 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -2,13 +2,14 @@ provider "aws" { region = local.region } -provider "aws" { - alias = "us_east_1" - region = "us-east-1" +data "aws_availability_zones" "available" { + # Exclude local zones + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } } -data "aws_availability_zones" "available" {} - locals { name = "ex-${basename(path.cwd)}" region = "eu-west-1" @@ -39,24 +40,18 @@ module "redshift" { database_name = "mydb" master_username = "mydbuser" - # Either provide a good master password - # create_random_password = false - # master_password = "MySecretPassw0rd1!" # Do better! - # Or make Redshift manage it in secrets manager - manage_master_password = true + manage_master_password = true manage_master_password_rotation = true master_password_rotation_schedule_expression = "rate(90 days)" encrypted = true kms_key_arn = aws_kms_key.redshift.arn - enhanced_vpc_routing = true - vpc_security_group_ids = [module.security_group.security_group_id] - subnet_ids = module.vpc.redshift_subnets - # Only available when using the ra3.x type availability_zone_relocation_enabled = true + enhanced_vpc_routing = true + vpc_id = module.vpc.vpc_id snapshot_copy = { destination_region = "us-east-1" @@ -71,36 +66,36 @@ module "redshift" { # Parameter group parameter_group_name = "${local.name}-custom" parameter_group_description = "Custom parameter group for ${local.name} cluster" - parameter_group_parameters = { - wlm_json_configuration = { + parameter_group_parameters = [ + { name = "wlm_json_configuration" value = jsonencode([ { query_concurrency = 15 } ]) - } - require_ssl = { + }, + { name = "require_ssl" value = true - } - use_fips_ssl = { + }, + { name = "use_fips_ssl" value = false - } - enable_user_activity_logging = { + }, + { name = "enable_user_activity_logging" value = true - } - max_concurrency_scaling_clusters = { + }, + { name = "max_concurrency_scaling_clusters" value = 3 - } - enable_case_sensitive_identifier = { + }, + { name = "enable_case_sensitive_identifier" value = true } - } + ] parameter_group_tags = { Additional = "CustomParameterGroup" } @@ -108,49 +103,60 @@ module "redshift" { # Subnet group subnet_group_name = "${local.name}-custom" subnet_group_description = "Custom subnet group for ${local.name} cluster" + subnet_ids = module.vpc.redshift_subnets subnet_group_tags = { Additional = "CustomSubnetGroup" } # Snapshot schedule - create_snapshot_schedule = true - snapshot_schedule_identifier = local.name - use_snapshot_identifier_prefix = true - snapshot_schedule_description = "Example snapshot schedule" - snapshot_schedule_definitions = ["rate(12 hours)"] - snapshot_schedule_force_destroy = true + snapshot_schedule = { + identifier = local.name + use_prefix = true + description = "Example snapshot schedule" + definitions = ["rate(12 hours)"] + force_destroy = true + } # Scheduled actions create_scheduled_action_iam_role = true scheduled_actions = { pause = { - name = "${local.name}-pause" - description = "Pause cluster every night" - schedule = "cron(0 22 * * ? *)" - pause_cluster = true + name = "${local.name}-pause" + description = "Pause cluster every night" + schedule = "cron(0 22 * * ? *)" + target_action = { + pause_cluster = true + } } resize = { name = "${local.name}-resize" description = "Resize cluster (demo only)" schedule = "cron(00 13 * * ? *)" - resize_cluster = { - node_type = "ds2.xlarge" - number_of_nodes = 5 + target_action = { + resize_cluster = { + node_type = "ds2.xlarge" + number_of_nodes = 5 + } } } resume = { - name = "${local.name}-resume" - description = "Resume cluster every morning" - schedule = "cron(0 12 * * ? *)" - resume_cluster = true + name = "${local.name}-resume" + description = "Resume cluster every morning" + schedule = "cron(0 12 * * ? *)" + target_action = { + resume_cluster = true + } } } # Endpoint access - only available when using the ra3.x type - create_endpoint_access = true - endpoint_name = "${local.name}-example" - endpoint_subnet_group_name = aws_redshift_subnet_group.endpoint.id - endpoint_vpc_security_group_ids = [module.security_group.security_group_id] + endpoint_access = { + example = { + name = "${local.name}-example" + subnet_group_name = aws_redshift_subnet_group.endpoint.id + vpc_security_group_ids = [module.security_group.security_group_id] + } + } # Usage limits usage_limits = { @@ -196,7 +202,7 @@ module "redshift" { resource "aws_redshift_snapshot_copy_grant" "useast1" { # Grants are declared outside of module because they are generally performed # in the destination region and we do not embed multiple providers in the root module - provider = aws.us_east_1 + region = "us-east-1" snapshot_copy_grant_name = "${local.name}-us-east-1" kms_key_id = aws_kms_key.redshift_us_east_1.arn @@ -212,10 +218,10 @@ module "with_cloudwatch_logging" { source = "../../" cluster_identifier = "${local.name}-with-cloudwatch-logging" - node_type = "dc2.large" + node_type = "ra3.large" - vpc_security_group_ids = [module.security_group.security_group_id] - subnet_ids = module.vpc.redshift_subnets + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.redshift_subnets create_cloudwatch_log_group = true cloudwatch_log_group_retention_in_days = 7 @@ -235,10 +241,10 @@ module "default" { source = "../../" cluster_identifier = "${local.name}-default" - node_type = "dc2.large" + node_type = "ra3.large" - vpc_security_group_ids = [module.security_group.security_group_id] - subnet_ids = module.vpc.redshift_subnets + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.redshift_subnets tags = local.tags } @@ -259,7 +265,7 @@ module "disabled" { module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "~> 5.0" + version = "~> 6.0" name = local.name cidr = local.vpc_cidr @@ -301,7 +307,7 @@ resource "aws_kms_key" "redshift" { } resource "aws_kms_key" "redshift_us_east_1" { - provider = aws.us_east_1 + region = "us-east-1" description = "Customer managed key for encrypting Redshift snapshot cross-region" deletion_window_in_days = 7 @@ -341,7 +347,7 @@ data "aws_iam_policy_document" "s3_redshift" { module "s3_logs" { source = "terraform-aws-modules/s3-bucket/aws" - version = "~> 3.0" + version = "~> 5.0" bucket_prefix = local.name acl = "log-delivery-write" diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index a8ea7ec..f378b9c 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -115,6 +115,7 @@ output "cluster_namespace_arn" { output "cluster_master_password" { description = "The Redshift cluster master password" value = module.redshift.cluster_master_password + sensitive = true } output "cluster_master_username" { @@ -187,24 +188,9 @@ output "scheduled_action_iam_role_unique_id" { # Endpoint Access ################################################################################ -output "endpoint_access_address" { - description = "The DNS address of the endpoint" - value = module.redshift.endpoint_access_address -} - -output "endpoint_access_id" { - description = "The Redshift-managed VPC endpoint name" - value = module.redshift.endpoint_access_id -} - -output "endpoint_access_port" { - description = "The port number on which the cluster accepts incoming connections" - value = module.redshift.endpoint_access_port -} - -output "endpoint_access_vpc_endpoint" { - description = "The connection endpoint for connecting to an Amazon Redshift cluster through the proxy. See details below" - value = module.redshift.endpoint_access_vpc_endpoint +output "endpoint_access" { + description = "A map of access endpoints created and their attributes" + value = module.redshift.endpoint_access } ################################################################################ diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf index 02f721b..49d3842 100644 --- a/examples/complete/versions.tf +++ b/examples/complete/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.3" + required_version = ">= 1.11" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.45" + version = ">= 6.21" } } } diff --git a/main.tf b/main.tf index 9ef436e..9dc4493 100644 --- a/main.tf +++ b/main.tf @@ -1,23 +1,3 @@ -data "aws_partition" "current" { - count = var.create && var.create_scheduled_action_iam_role ? 1 : 0 -} - -locals { - dns_suffix = try(data.aws_partition.current[0].dns_suffix, "") -} - -resource "random_password" "master_password" { - count = var.create && var.create_random_password ? 1 : 0 - - length = var.random_password_length - min_lower = 1 - min_numeric = 1 - min_special = 1 - min_upper = 1 - special = true - override_special = "!#$%&*()-_=+[]{}<>:?" -} - ################################################################################ # Cluster ################################################################################ @@ -25,16 +5,15 @@ resource "random_password" "master_password" { locals { subnet_group_name = var.create && var.create_subnet_group ? aws_redshift_subnet_group.this[0].name : var.subnet_group_name parameter_group_name = var.create && var.create_parameter_group ? aws_redshift_parameter_group.this[0].id : var.parameter_group_name - - master_password = var.create && var.create_random_password ? random_password.master_password[0].result : var.master_password } resource "aws_redshift_cluster" "this" { count = var.create ? 1 : 0 + region = var.region + allow_version_upgrade = var.allow_version_upgrade apply_immediately = var.apply_immediately - aqua_configuration_status = var.aqua_configuration_status automated_snapshot_retention_period = var.automated_snapshot_retention_period availability_zone = var.availability_zone availability_zone_relocation_enabled = var.availability_zone_relocation_enabled @@ -49,34 +28,35 @@ resource "aws_redshift_cluster" "this" { enhanced_vpc_routing = var.enhanced_vpc_routing final_snapshot_identifier = var.skip_final_snapshot ? null : var.final_snapshot_identifier kms_key_id = var.kms_key_arn + maintenance_track_name = var.maintenance_track_name + manage_master_password = var.manage_master_password ? var.manage_master_password : null + manual_snapshot_retention_period = var.manual_snapshot_retention_period + master_password_wo = var.snapshot_identifier == null && !var.manage_master_password ? var.master_password_wo : null + master_password_wo_version = var.snapshot_identifier == null && !var.manage_master_password ? var.master_password_wo_version : null + master_password_secret_kms_key_id = var.master_password_secret_kms_key_id + master_username = var.master_username + multi_az = var.multi_az + node_type = var.node_type + number_of_nodes = var.number_of_nodes + owner_account = var.owner_account + port = var.port + preferred_maintenance_window = var.preferred_maintenance_window + publicly_accessible = var.publicly_accessible + skip_final_snapshot = var.skip_final_snapshot + snapshot_arn = var.snapshot_arn + snapshot_cluster_identifier = var.snapshot_cluster_identifier + snapshot_identifier = var.snapshot_identifier + tags = var.tags + vpc_security_group_ids = compact(concat(aws_security_group.this[*].id, var.vpc_security_group_ids)) + + dynamic "timeouts" { + for_each = var.cluster_timeouts != null ? [var.cluster_timeouts] : [] - # iam_roles and default_iam_roles are managed in the aws_redshift_cluster_iam_roles resource below - - maintenance_track_name = var.maintenance_track_name - manual_snapshot_retention_period = var.manual_snapshot_retention_period - manage_master_password = var.manage_master_password ? var.manage_master_password : null - master_password = var.snapshot_identifier == null && !var.manage_master_password ? local.master_password : null - master_password_secret_kms_key_id = var.master_password_secret_kms_key_id - master_username = var.master_username - multi_az = var.multi_az - node_type = var.node_type - number_of_nodes = var.number_of_nodes - owner_account = var.owner_account - port = var.port - preferred_maintenance_window = var.preferred_maintenance_window - publicly_accessible = var.publicly_accessible - skip_final_snapshot = var.skip_final_snapshot - snapshot_cluster_identifier = var.snapshot_cluster_identifier - - snapshot_identifier = var.snapshot_identifier - vpc_security_group_ids = var.vpc_security_group_ids - - tags = var.tags - - timeouts { - create = try(var.cluster_timeouts.create, null) - update = try(var.cluster_timeouts.update, null) - delete = try(var.cluster_timeouts.delete, null) + content { + create = timeouts.value.create + update = timeouts.value.update + delete = timeouts.value.delete + } } lifecycle { @@ -93,6 +73,8 @@ resource "aws_redshift_cluster" "this" { resource "aws_redshift_cluster_iam_roles" "this" { count = var.create && length(var.iam_role_arns) > 0 ? 1 : 0 + region = var.region + cluster_identifier = aws_redshift_cluster.this[0].id iam_role_arns = var.iam_role_arns default_iam_role_arn = var.default_iam_role_arn @@ -105,15 +87,17 @@ resource "aws_redshift_cluster_iam_roles" "this" { resource "aws_redshift_parameter_group" "this" { count = var.create && var.create_parameter_group ? 1 : 0 + region = var.region + name = coalesce(var.parameter_group_name, replace(var.cluster_identifier, ".", "-")) description = var.parameter_group_description family = var.parameter_group_family dynamic "parameter" { - for_each = var.parameter_group_parameters + for_each = var.parameter_group_parameters != null ? var.parameter_group_parameters : [] content { - name = try(parameter.value.name, parameter.key) + name = parameter.value.name value = parameter.value.value } } @@ -128,6 +112,8 @@ resource "aws_redshift_parameter_group" "this" { resource "aws_redshift_subnet_group" "this" { count = var.create && var.create_subnet_group ? 1 : 0 + region = var.region + name = coalesce(var.subnet_group_name, var.cluster_identifier) description = var.subnet_group_description subnet_ids = var.subnet_ids @@ -140,19 +126,23 @@ resource "aws_redshift_subnet_group" "this" { ################################################################################ resource "aws_redshift_snapshot_schedule" "this" { - count = var.create && var.create_snapshot_schedule ? 1 : 0 + count = var.create && var.snapshot_schedule != null ? 1 : 0 + + region = var.region - identifier = var.use_snapshot_identifier_prefix ? null : var.snapshot_schedule_identifier - identifier_prefix = var.use_snapshot_identifier_prefix ? "${var.snapshot_schedule_identifier}-" : null - description = var.snapshot_schedule_description - definitions = var.snapshot_schedule_definitions - force_destroy = var.snapshot_schedule_force_destroy + definitions = var.snapshot_schedule.definitions + description = var.snapshot_schedule.description + force_destroy = var.snapshot_schedule.force_destroy + identifier = var.snapshot_schedule.use_prefix ? null : try(coalesce(var.snapshot_schedule.identifier, var.cluster_identifier), "") + identifier_prefix = var.snapshot_schedule.use_prefix ? "${try(coalesce(var.snapshot_schedule.identifier, var.cluster_identifier), "")}-" : null - tags = var.tags + tags = merge(var.tags, var.snapshot_schedule.tags) } resource "aws_redshift_snapshot_schedule_association" "this" { - count = var.create && var.create_snapshot_schedule ? 1 : 0 + count = var.create && var.snapshot_schedule != null ? 1 : 0 + + region = var.region cluster_identifier = aws_redshift_cluster.this[0].id schedule_identifier = aws_redshift_snapshot_schedule.this[0].id @@ -169,45 +159,61 @@ locals { resource "aws_redshift_scheduled_action" "this" { for_each = { for k, v in var.scheduled_actions : k => v if var.create } - name = each.value.name - description = try(each.value.description, null) - enable = try(each.value.enable, null) - start_time = try(each.value.start_time, null) - end_time = try(each.value.end_time, null) + region = var.region + + name = try(coalesce(each.value.name, each.key)) + description = each.value.description + enable = each.value.enable + start_time = each.value.start_time + end_time = each.value.end_time schedule = each.value.schedule iam_role = var.create_scheduled_action_iam_role ? aws_iam_role.scheduled_action[0].arn : each.value.iam_role - target_action { - dynamic "pause_cluster" { - for_each = try([each.value.pause_cluster], []) + dynamic "target_action" { + for_each = [each.value.target_action] - content { - cluster_identifier = aws_redshift_cluster.this[0].id + content { + dynamic "pause_cluster" { + for_each = target_action.value.pause_cluster != null && target_action.value.pause_cluster ? [1] : [] + + content { + cluster_identifier = aws_redshift_cluster.this[0].id + } } - } - dynamic "resize_cluster" { - for_each = try([each.value.resize_cluster], []) + dynamic "resize_cluster" { + for_each = target_action.value.resize_cluster != null ? [target_action.value.resize_cluster] : [] - content { - classic = try(resize_cluster.value.classic, null) - cluster_identifier = aws_redshift_cluster.this[0].id - cluster_type = try(resize_cluster.value.cluster_type, null) - node_type = try(resize_cluster.value.node_type, null) - number_of_nodes = try(resize_cluster.value.number_of_nodes, null) + content { + classic = resize_cluster.value.classic + cluster_identifier = aws_redshift_cluster.this[0].id + cluster_type = resize_cluster.value.cluster_type + node_type = resize_cluster.value.node_type + number_of_nodes = resize_cluster.value.number_of_nodes + } } - } - dynamic "resume_cluster" { - for_each = try([each.value.resume_cluster], []) + dynamic "resume_cluster" { + for_each = target_action.value.resume_cluster != null && target_action.value.resume_cluster ? [target_action.value.resume_cluster] : [] - content { - cluster_identifier = aws_redshift_cluster.this[0].id + content { + cluster_identifier = aws_redshift_cluster.this[0].id + } } } } } +################################################################################ +# Scheduled Action IAM Role +################################################################################ + +data "aws_service_principal" "scheduler_redshift" { + count = var.create && var.create_scheduled_action_iam_role ? 1 : 0 + + service_name = "scheduler.redshift" +} + data "aws_iam_policy_document" "scheduled_action_assume" { count = var.create && var.create_scheduled_action_iam_role ? 1 : 0 @@ -217,7 +223,7 @@ data "aws_iam_policy_document" "scheduled_action_assume" { principals { type = "Service" - identifiers = ["scheduler.redshift.${local.dns_suffix}"] + identifiers = [data.aws_service_principal.scheduler_redshift[0].name] } } } @@ -268,14 +274,15 @@ resource "aws_iam_role_policy" "scheduled_action" { ################################################################################ resource "aws_redshift_endpoint_access" "this" { - count = var.create && var.create_endpoint_access ? 1 : 0 + for_each = var.create && var.endpoint_access != null ? var.endpoint_access : {} - cluster_identifier = aws_redshift_cluster.this[0].id + region = var.region - endpoint_name = var.endpoint_name - resource_owner = var.endpoint_resource_owner - subnet_group_name = coalesce(var.endpoint_subnet_group_name, local.subnet_group_name) - vpc_security_group_ids = var.endpoint_vpc_security_group_ids + cluster_identifier = aws_redshift_cluster.this[0].id + endpoint_name = try(coalesce(each.value.name, each.key)) + resource_owner = each.value.resource_owner + subnet_group_name = each.value.subnet_group_name + vpc_security_group_ids = each.value.vpc_security_group_ids } ################################################################################ @@ -285,15 +292,16 @@ resource "aws_redshift_endpoint_access" "this" { resource "aws_redshift_usage_limit" "this" { for_each = { for k, v in var.usage_limits : k => v if var.create } - cluster_identifier = aws_redshift_cluster.this[0].id + region = var.region - amount = each.value.amount - breach_action = try(each.value.breach_action, null) - feature_type = each.value.feature_type - limit_type = each.value.limit_type - period = try(each.value.period, null) + amount = each.value.amount + breach_action = each.value.breach_action + cluster_identifier = aws_redshift_cluster.this[0].id + feature_type = each.value.feature_type + limit_type = try(coalesce(each.value.limit_type)) + period = each.value.period - tags = merge(var.tags, try(each.value.tags, {})) + tags = merge(var.tags, each.value.tags) } ################################################################################ @@ -303,7 +311,9 @@ resource "aws_redshift_usage_limit" "this" { resource "aws_redshift_authentication_profile" "this" { for_each = { for k, v in var.authentication_profiles : k => v if var.create } - authentication_profile_name = try(each.value.name, each.key) + region = var.region + + authentication_profile_name = try(coalesce(each.value.name, each.key)) authentication_profile_content = jsonencode(each.value.content) } @@ -312,27 +322,15 @@ resource "aws_redshift_authentication_profile" "this" { ################################################################################ resource "aws_redshift_logging" "this" { - count = var.create && length(var.logging) > 0 ? 1 : 0 - - cluster_identifier = aws_redshift_cluster.this[0].id - bucket_name = try(var.logging.bucket_name, null) - log_destination_type = try(var.logging.log_destination_type, null) - log_exports = try(var.logging.log_exports, null) - s3_key_prefix = try(var.logging.s3_key_prefix, null) -} - -################################################################################ -# Snapshot Copy -################################################################################ + count = var.create && var.logging != null ? 1 : 0 -resource "aws_redshift_snapshot_copy" "this" { - count = var.create && length(var.snapshot_copy) > 0 ? 1 : 0 + region = var.region - cluster_identifier = aws_redshift_cluster.this[0].id - destination_region = var.snapshot_copy.destination_region - manual_snapshot_retention_period = try(var.snapshot_copy.manual_snapshot_retention_period, null) - retention_period = try(var.snapshot_copy.retention_period, null) - snapshot_copy_grant_name = try(var.snapshot_copy.grant_name, null) + cluster_identifier = aws_redshift_cluster.this[0].id + bucket_name = var.logging.bucket_name + log_destination_type = var.logging.log_destination_type + log_exports = var.logging.log_exports + s3_key_prefix = var.logging.s3_key_prefix } ################################################################################ @@ -340,7 +338,9 @@ resource "aws_redshift_snapshot_copy" "this" { ################################################################################ resource "aws_cloudwatch_log_group" "this" { - for_each = toset([for log in try(var.logging.log_exports, []) : log if var.create && var.create_cloudwatch_log_group]) + for_each = var.create && var.create_cloudwatch_log_group && var.logging != null ? toset([for log in try(var.logging.log_exports, []) : log]) : toset([]) + + region = var.region name = "/aws/redshift/cluster/${var.cluster_identifier}/${each.value}" retention_in_days = var.cloudwatch_log_group_retention_in_days @@ -350,6 +350,22 @@ resource "aws_cloudwatch_log_group" "this" { tags = merge(var.tags, var.cloudwatch_log_group_tags) } +################################################################################ +# Snapshot Copy +################################################################################ + +resource "aws_redshift_snapshot_copy" "this" { + count = var.create && var.snapshot_copy != null ? 1 : 0 + + region = var.region + + cluster_identifier = aws_redshift_cluster.this[0].id + destination_region = var.snapshot_copy.destination_region + manual_snapshot_retention_period = var.snapshot_copy.manual_snapshot_retention_period + retention_period = var.snapshot_copy.retention_period + snapshot_copy_grant_name = var.snapshot_copy.grant_name +} + ################################################################################ # Managed Secret Rotation ################################################################################ @@ -357,6 +373,8 @@ resource "aws_cloudwatch_log_group" "this" { resource "aws_secretsmanager_secret_rotation" "this" { count = var.create && var.manage_master_password && var.manage_master_password_rotation ? 1 : 0 + region = var.region + secret_id = aws_redshift_cluster.this[0].master_password_secret_arn rotate_immediately = var.master_password_rotate_immediately @@ -366,3 +384,75 @@ resource "aws_secretsmanager_secret_rotation" "this" { schedule_expression = var.master_password_rotation_schedule_expression } } + +################################################################################ +# Security Group +################################################################################ + +locals { + create_security_group = var.create && var.create_security_group + security_group_name = try(coalesce(var.security_group_name, var.cluster_identifier), "") +} + +resource "aws_security_group" "this" { + count = local.create_security_group ? 1 : 0 + + region = var.region + + name = var.security_group_use_name_prefix ? null : local.security_group_name + name_prefix = var.security_group_use_name_prefix ? "${local.security_group_name}-" : null + vpc_id = var.vpc_id + description = coalesce(var.security_group_description, "Control traffic to/from Redshift cluster ${var.security_group_name}") + + tags = merge( + var.tags, + var.security_group_tags, + { "Name" = local.security_group_name } + ) + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_vpc_security_group_ingress_rule" "this" { + for_each = { for k, v in var.security_group_ingress_rules : k => v if var.security_group_ingress_rules != null && local.create_security_group } + + region = var.region + + cidr_ipv4 = each.value.cidr_ipv4 + cidr_ipv6 = each.value.cidr_ipv6 + description = each.value.description + from_port = try(coalesce(each.value.from_port, var.port), null) + ip_protocol = each.value.ip_protocol + prefix_list_id = each.value.prefix_list_id + referenced_security_group_id = each.value.referenced_security_group_id == "self" ? aws_security_group.this[0].id : each.value.referenced_security_group_id + security_group_id = aws_security_group.this[0].id + tags = merge( + var.tags, + { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") }, + each.value.tags + ) + to_port = try(coalesce(each.value.to_port, each.value.from_port, var.port), null) +} + +resource "aws_vpc_security_group_egress_rule" "this" { + for_each = { for k, v in var.security_group_egress_rules : k => v if var.security_group_egress_rules != null && local.create_security_group } + + region = var.region + + cidr_ipv4 = each.value.cidr_ipv4 + cidr_ipv6 = each.value.cidr_ipv6 + description = each.value.description + from_port = try(coalesce(each.value.from_port, each.value.to_port, var.port), null) + ip_protocol = each.value.ip_protocol + prefix_list_id = each.value.prefix_list_id + referenced_security_group_id = each.value.referenced_security_group_id == "self" ? aws_security_group.this[0].id : each.value.referenced_security_group_id + security_group_id = aws_security_group.this[0].id + tags = merge( + var.tags, + { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") }, + each.value.tags + ) + to_port = try(coalesce(each.value.to_port, var.port), null) +} diff --git a/outputs.tf b/outputs.tf index 0c60927..53cf63f 100644 --- a/outputs.tf +++ b/outputs.tf @@ -193,24 +193,9 @@ output "scheduled_action_iam_role_unique_id" { # Endpoint Access ################################################################################ -output "endpoint_access_address" { - description = "The DNS address of the endpoint" - value = try(aws_redshift_endpoint_access.this[0].address, null) -} - -output "endpoint_access_id" { - description = "The Redshift-managed VPC endpoint name" - value = try(aws_redshift_endpoint_access.this[0].id, null) -} - -output "endpoint_access_port" { - description = "The port number on which the cluster accepts incoming connections" - value = try(aws_redshift_endpoint_access.this[0].port, null) -} - -output "endpoint_access_vpc_endpoint" { - description = "The connection endpoint for connecting to an Amazon Redshift cluster through the proxy. See details below" - value = try(aws_redshift_endpoint_access.this[0].vpc_endpoint, null) +output "endpoint_access" { + description = "A map of access endpoints created and their attributes" + value = aws_redshift_endpoint_access.this } ################################################################################ diff --git a/variables.tf b/variables.tf index f9cfc55..b3f1c54 100644 --- a/variables.tf +++ b/variables.tf @@ -4,6 +4,12 @@ variable "create" { default = true } +variable "region" { + description = "Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration" + type = string + default = null +} + variable "tags" { description = "A map of tags to add to all resources" type = map(string) @@ -26,12 +32,6 @@ variable "apply_immediately" { default = null } -variable "aqua_configuration_status" { - description = "The value represents how the cluster is configured to use AQUA (Advanced Query Accelerator) after the cluster is restored. Possible values are `enabled`, `disabled`, and `auto`. Requires Cluster reboot" - type = string - default = null -} - variable "automated_snapshot_retention_period" { description = "The number of days that automated snapshots are retained. If the value is 0, automated snapshots are disabled. Even if automated snapshots are disabled, you can still create manual snapshots when you want with create-cluster-snapshot. Default is 1" type = number @@ -56,9 +56,6 @@ variable "cluster_identifier" { default = "" } -# cluster_parameter_group_name -> see parameter group section -# cluster_subnet_group_name -> see subnet group section - variable "cluster_version" { description = "The version of the Amazon Redshift engine software that you want to deploy on the cluster. The version selected runs on all the nodes in the cluster" type = string @@ -71,8 +68,6 @@ variable "database_name" { default = null } -# default_iam_role_arn -> see iam roles section - variable "elastic_ip" { description = "The Elastic IP (EIP) address for the cluster" type = string @@ -82,7 +77,7 @@ variable "elastic_ip" { variable "encrypted" { description = "If `true`, the data in the cluster is encrypted at rest" type = bool - default = true + default = null } variable "enhanced_vpc_routing" { @@ -97,50 +92,47 @@ variable "final_snapshot_identifier" { default = null } -# iam_roles -> see iam roles section - variable "kms_key_arn" { description = "The ARN for the KMS encryption key. When specifying `kms_key_arn`, `encrypted` needs to be set to `true`" type = string default = null } -variable "logging" { - description = "Logging configuration for the cluster" - type = any - default = {} -} - variable "maintenance_track_name" { description = "The name of the maintenance track for the restored cluster. When you take a snapshot, the snapshot inherits the MaintenanceTrack value from the cluster. The snapshot might be on a different track than the cluster that was the source for the snapshot. Default value is `current`" type = string default = null } +variable "manage_master_password" { + description = "Whether to use AWS SecretsManager to manage the cluster admin credentials. Conflicts with `master_password_wo`. One of `master_password_wo` or `manage_master_password` is required unless `snapshot_identifier` is provided" + type = bool + default = true +} + variable "manual_snapshot_retention_period" { description = "The default number of days to retain a manual snapshot. If the value is -1, the snapshot is retained indefinitely. This setting doesn't change the retention period of existing snapshots. Valid values are between `-1` and `3653`. Default value is `-1`" type = number default = null } - -variable "manage_master_password" { - description = "Whether to use AWS SecretsManager to manage the cluster admin credentials. Conflicts with `master_password`. One of `master_password` or `manage_master_password` is required unless `snapshot_identifier` is provided" - type = bool - default = false +variable "master_password_wo" { + description = "Password for the master DB user. Must contain at least 8 chars, one uppercase letter, one lowercase letter, and one number" + type = string + default = null + sensitive = true } -variable "master_password_secret_kms_key_id" { - description = "ID of the KMS key used to encrypt the cluster admin credentials secret" +variable "master_password_wo_version" { + description = "Used together with `master_password_wo` to trigger an update. Increment this value when an update to the `master_password_wo` is required" type = string default = null } -variable "master_password" { - description = "Password for the master DB user. (Required unless a `snapshot_identifier` is provided). Must contain at least 8 chars, one uppercase letter, one lowercase letter, and one number" +variable "master_password_secret_kms_key_id" { + description = "ID of the KMS key used to encrypt the cluster admin credentials secret" type = string default = null - sensitive = true } variable "multi_az" { @@ -149,20 +141,8 @@ variable "multi_az" { default = null } -variable "create_random_password" { - description = "Determines whether to create random password for cluster `master_password`" - type = bool - default = true -} - -variable "random_password_length" { - description = "Length of random password to create. Defaults to `16`" - type = number - default = 16 -} - variable "master_username" { - description = "Username for the master DB user (Required unless a `snapshot_identifier` is provided). Defaults to `awsuser`" + description = "Username for the master DB user. Defaults to `awsuser`" type = string default = "awsuser" } @@ -186,9 +166,9 @@ variable "owner_account" { } variable "port" { - description = "The port number on which the cluster accepts incoming connections. Default port is 5439" + description = "The port number on which the cluster accepts incoming connections. Default port is `5439`" type = number - default = null + default = 5439 } variable "preferred_maintenance_window" { @@ -200,7 +180,7 @@ variable "preferred_maintenance_window" { variable "publicly_accessible" { description = "If true, the cluster can be accessed from a public network" type = bool - default = false + default = null } variable "skip_final_snapshot" { @@ -209,20 +189,20 @@ variable "skip_final_snapshot" { default = true } -variable "snapshot_cluster_identifier" { - description = "The name of the cluster the source snapshot was created from" +variable "snapshot_arn" { + description = "The ARN of the snapshot from which to create the new cluster. Conflicts with `snapshot_identifier`" type = string default = null } -variable "snapshot_copy" { - description = "Configuration of automatic copy of snapshots from one region to another" - type = any - default = {} +variable "snapshot_cluster_identifier" { + description = "The name of the cluster the source snapshot was created from" + type = string + default = null } variable "snapshot_identifier" { - description = "The name of the snapshot from which to create the new cluster" + description = "The name of the snapshot from which to create the new cluster. Conflicts with `snapshot_arn`" type = string default = null } @@ -235,8 +215,12 @@ variable "vpc_security_group_ids" { variable "cluster_timeouts" { description = "Create, update, and delete timeout configurations for the cluster" - type = map(string) - default = {} + type = object({ + create = optional(string) + update = optional(string) + delete = optional(string) + }) + default = null } ################################################################################ @@ -280,13 +264,16 @@ variable "parameter_group_description" { variable "parameter_group_family" { description = "The family of the Redshift parameter group" type = string - default = "redshift-1.0" + default = "redshift-2.0" } variable "parameter_group_parameters" { - description = "value" - type = map(any) - default = {} + description = "A list of Redshift parameters to apply" + type = list(object({ + name = string + value = string + })) + default = null } variable "parameter_group_tags" { @@ -333,40 +320,17 @@ variable "subnet_group_tags" { # Snapshot Schedule ################################################################################ -variable "create_snapshot_schedule" { - description = "Determines whether to create a snapshot schedule" - type = bool - default = false -} - -variable "snapshot_schedule_identifier" { - description = "The snapshot schedule identifier" - type = string - default = null -} - -variable "use_snapshot_identifier_prefix" { - description = "Determines whether the identifier (`snapshot_schedule_identifier`) is used as a prefix" - type = bool - default = true -} - -variable "snapshot_schedule_description" { - description = "The description of the snapshot schedule" - type = string - default = null -} - -variable "snapshot_schedule_definitions" { - description = "The definition of the snapshot schedule. The definition is made up of schedule expressions, for example `cron(30 12 *)` or `rate(12 hours)`" - type = list(string) - default = [] -} - -variable "snapshot_schedule_force_destroy" { - description = "Whether to destroy all associated clusters with this snapshot schedule on deletion. Must be enabled and applied before attempting deletion" - type = bool - default = null +variable "snapshot_schedule" { + description = "Configuration for creating a snapshot schedule and associating it with the cluster" + type = object({ + definitions = list(string) + description = optional(string) + force_destroy = optional(bool) + use_prefix = optional(bool, false) + identifier = optional(string) + tags = optional(map(string), {}) + }) + default = null } ################################################################################ @@ -374,11 +338,34 @@ variable "snapshot_schedule_force_destroy" { ################################################################################ variable "scheduled_actions" { - description = "Map of maps containing scheduled action definitions" - type = any - default = {} + description = "Map of scheduled action definitions to create" + type = map(object({ + name = optional(string) # Will fall back to key if not set + description = optional(string) + enable = optional(bool) + start_time = optional(string) + end_time = optional(string) + schedule = string + iam_role = optional(string) + target_action = object({ + pause_cluster = optional(bool, false) + resize_cluster = optional(object({ + classic = optional(bool) + cluster_type = optional(string) + node_type = optional(string) + number_of_nodes = optional(number) + })) + resume_cluster = optional(bool, false) + }) + })) + default = {} + nullable = false } +################################################################################ +# Scheduled Action IAM Role +################################################################################ + variable "create_scheduled_action_iam_role" { description = "Determines whether a scheduled action IAM role is created" type = bool @@ -425,34 +412,16 @@ variable "iam_role_tags" { # Endpoint Access ################################################################################ -variable "create_endpoint_access" { - description = "Determines whether to create an endpoint access (managed VPC endpoint)" - type = bool - default = false -} - -variable "endpoint_name" { - description = "The Redshift-managed VPC endpoint name" - type = string - default = "" -} - -variable "endpoint_resource_owner" { - description = "The Amazon Web Services account ID of the owner of the cluster. This is only required if the cluster is in another Amazon Web Services account" - type = string - default = null -} - -variable "endpoint_subnet_group_name" { - description = "The subnet group from which Amazon Redshift chooses the subnet to deploy the endpoint" - type = string - default = "" -} - -variable "endpoint_vpc_security_group_ids" { - description = "The security group IDs to use for the endpoint access (managed VPC endpoint)" - type = list(string) - default = [] +variable "endpoint_access" { + description = "Map of endpoint access (managed VPC endpoint) definitions to create" + type = map(object({ + name = optional(string) # Will fall back to key if not set + resource_owner = optional(string) + subnet_group_name = string + vpc_security_group_ids = optional(list(string)) + })) + default = {} + nullable = false } ################################################################################ @@ -461,8 +430,16 @@ variable "endpoint_vpc_security_group_ids" { variable "usage_limits" { description = "Map of usage limit definitions to create" - type = any - default = {} + type = map(object({ + amount = number + breach_action = optional(string) + feature_type = string + limit_type = optional(string) # Will fall back to key if not set + period = optional(string) + tags = optional(map(string), {}) + })) + default = {} + nullable = false } ################################################################################ @@ -471,8 +448,27 @@ variable "usage_limits" { variable "authentication_profiles" { description = "Map of authentication profiles to create" - type = any - default = {} + type = map(object({ + name = optional(string) # Will fall back to key if not set + content = any + })) + default = {} + nullable = false +} + +################################################################################ +# Logging +################################################################################ + +variable "logging" { + description = "Logging configuration for the cluster" + type = object({ + bucket_name = optional(string) + log_destination_type = optional(string) + log_exports = optional(list(string)) + s3_key_prefix = optional(string) + }) + default = null } ################################################################################ @@ -509,36 +505,131 @@ variable "cloudwatch_log_group_tags" { default = {} } +################################################################################ +# Snapshot Copy +################################################################################ + +variable "snapshot_copy" { + description = "Configuration of automatic copy of snapshots from one region to another" + type = object({ + destination_region = string + manual_snapshot_retention_period = optional(number) + retention_period = optional(number) + grant_name = optional(string) + }) + default = null +} + ################################################################################ # Managed Secret Rotation ################################################################################ variable "manage_master_password_rotation" { - description = "Whether to manage the master user password rotation. Setting this value to false after previously having been set to true will disable automatic rotation." + description = "Whether to manage the master user password rotation. Setting this value to false after previously having been set to true will disable automatic rotation" type = bool default = false } variable "master_password_rotate_immediately" { - description = "Specifies whether to rotate the secret immediately or wait until the next scheduled rotation window." + description = "Specifies whether to rotate the secret immediately or wait until the next scheduled rotation window" type = bool default = null } variable "master_password_rotation_automatically_after_days" { - description = "Specifies the number of days between automatic scheduled rotations of the secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified." + description = "Specifies the number of days between automatic scheduled rotations of the secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified" type = number default = null } variable "master_password_rotation_duration" { - description = "The length of the rotation window in hours. For example, 3h for a three hour window." + description = "The length of the rotation window in hours. For example, 3h for a three hour window" type = string default = null } variable "master_password_rotation_schedule_expression" { - description = "A cron() or rate() expression that defines the schedule for rotating your secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified." + description = "A cron() or rate() expression that defines the schedule for rotating your secret. Either `master_user_password_rotation_automatically_after_days` or `master_user_password_rotation_schedule_expression` must be specified" + type = string + default = null +} + +################################################################################ +# Security Group +################################################################################ + +variable "create_security_group" { + description = "Determines whether to create security group for Redshift cluster" + type = bool + default = true +} + +variable "security_group_name" { + description = "The security group name" + type = string + default = "" +} + +variable "security_group_use_name_prefix" { + description = "Determines whether the security group name (`security_group_name`) is used as a prefix" + type = bool + default = true +} + +variable "security_group_description" { + description = "The description of the security group. If value is set to empty string it will contain cluster name in the description" type = string default = null } + +variable "vpc_id" { + description = "ID of the VPC where to create security group" + type = string + default = "" +} + +variable "security_group_ingress_rules" { + description = "Map of security group ingress rules to add to the security group created" + type = map(object({ + name = optional(string) + + cidr_ipv4 = optional(string) + cidr_ipv6 = optional(string) + description = optional(string) + from_port = optional(number) + ip_protocol = optional(string, "tcp") + prefix_list_id = optional(string) + referenced_security_group_id = optional(string) + region = optional(string) + tags = optional(map(string), {}) + to_port = optional(number) + })) + default = {} + nullable = false +} + +variable "security_group_egress_rules" { + description = "Map of security group egress rules to add to the security group created" + type = map(object({ + name = optional(string) + + cidr_ipv4 = optional(string) + cidr_ipv6 = optional(string) + description = optional(string) + from_port = optional(number) + ip_protocol = optional(string, "tcp") + prefix_list_id = optional(string) + referenced_security_group_id = optional(string) + region = optional(string) + tags = optional(map(string), {}) + to_port = optional(number) + })) + default = {} + nullable = false +} + +variable "security_group_tags" { + description = "Additional tags for the security group" + type = map(string) + default = {} +} diff --git a/versions.tf b/versions.tf index 512dd2c..49d3842 100644 --- a/versions.tf +++ b/versions.tf @@ -1,15 +1,10 @@ terraform { - required_version = ">= 1.3" + required_version = ">= 1.11" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.45" - } - - random = { - source = "hashicorp/random" - version = ">= 3.0" + version = ">= 6.21" } } } diff --git a/wrappers/main.tf b/wrappers/main.tf index d04f14b..907995f 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -5,7 +5,6 @@ module "wrapper" { allow_version_upgrade = try(each.value.allow_version_upgrade, var.defaults.allow_version_upgrade, null) apply_immediately = try(each.value.apply_immediately, var.defaults.apply_immediately, null) - aqua_configuration_status = try(each.value.aqua_configuration_status, var.defaults.aqua_configuration_status, null) authentication_profiles = try(each.value.authentication_profiles, var.defaults.authentication_profiles, {}) automated_snapshot_retention_period = try(each.value.automated_snapshot_retention_period, var.defaults.automated_snapshot_retention_period, null) availability_zone = try(each.value.availability_zone, var.defaults.availability_zone, null) @@ -15,24 +14,19 @@ module "wrapper" { cloudwatch_log_group_skip_destroy = try(each.value.cloudwatch_log_group_skip_destroy, var.defaults.cloudwatch_log_group_skip_destroy, null) cloudwatch_log_group_tags = try(each.value.cloudwatch_log_group_tags, var.defaults.cloudwatch_log_group_tags, {}) cluster_identifier = try(each.value.cluster_identifier, var.defaults.cluster_identifier, "") - cluster_timeouts = try(each.value.cluster_timeouts, var.defaults.cluster_timeouts, {}) + cluster_timeouts = try(each.value.cluster_timeouts, var.defaults.cluster_timeouts, null) cluster_version = try(each.value.cluster_version, var.defaults.cluster_version, null) create = try(each.value.create, var.defaults.create, true) create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, false) - create_endpoint_access = try(each.value.create_endpoint_access, var.defaults.create_endpoint_access, false) create_parameter_group = try(each.value.create_parameter_group, var.defaults.create_parameter_group, true) - create_random_password = try(each.value.create_random_password, var.defaults.create_random_password, true) create_scheduled_action_iam_role = try(each.value.create_scheduled_action_iam_role, var.defaults.create_scheduled_action_iam_role, false) - create_snapshot_schedule = try(each.value.create_snapshot_schedule, var.defaults.create_snapshot_schedule, false) + create_security_group = try(each.value.create_security_group, var.defaults.create_security_group, true) create_subnet_group = try(each.value.create_subnet_group, var.defaults.create_subnet_group, true) database_name = try(each.value.database_name, var.defaults.database_name, null) default_iam_role_arn = try(each.value.default_iam_role_arn, var.defaults.default_iam_role_arn, null) elastic_ip = try(each.value.elastic_ip, var.defaults.elastic_ip, null) - encrypted = try(each.value.encrypted, var.defaults.encrypted, true) - endpoint_name = try(each.value.endpoint_name, var.defaults.endpoint_name, "") - endpoint_resource_owner = try(each.value.endpoint_resource_owner, var.defaults.endpoint_resource_owner, null) - endpoint_subnet_group_name = try(each.value.endpoint_subnet_group_name, var.defaults.endpoint_subnet_group_name, "") - endpoint_vpc_security_group_ids = try(each.value.endpoint_vpc_security_group_ids, var.defaults.endpoint_vpc_security_group_ids, []) + encrypted = try(each.value.encrypted, var.defaults.encrypted, null) + endpoint_access = try(each.value.endpoint_access, var.defaults.endpoint_access, {}) enhanced_vpc_routing = try(each.value.enhanced_vpc_routing, var.defaults.enhanced_vpc_routing, null) final_snapshot_identifier = try(each.value.final_snapshot_identifier, var.defaults.final_snapshot_identifier, null) iam_role_arns = try(each.value.iam_role_arns, var.defaults.iam_role_arns, []) @@ -43,46 +37,51 @@ module "wrapper" { iam_role_tags = try(each.value.iam_role_tags, var.defaults.iam_role_tags, {}) iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.defaults.iam_role_use_name_prefix, true) kms_key_arn = try(each.value.kms_key_arn, var.defaults.kms_key_arn, null) - logging = try(each.value.logging, var.defaults.logging, {}) + logging = try(each.value.logging, var.defaults.logging, null) maintenance_track_name = try(each.value.maintenance_track_name, var.defaults.maintenance_track_name, null) - manage_master_password = try(each.value.manage_master_password, var.defaults.manage_master_password, false) + manage_master_password = try(each.value.manage_master_password, var.defaults.manage_master_password, true) manage_master_password_rotation = try(each.value.manage_master_password_rotation, var.defaults.manage_master_password_rotation, false) manual_snapshot_retention_period = try(each.value.manual_snapshot_retention_period, var.defaults.manual_snapshot_retention_period, null) - master_password = try(each.value.master_password, var.defaults.master_password, null) master_password_rotate_immediately = try(each.value.master_password_rotate_immediately, var.defaults.master_password_rotate_immediately, null) master_password_rotation_automatically_after_days = try(each.value.master_password_rotation_automatically_after_days, var.defaults.master_password_rotation_automatically_after_days, null) master_password_rotation_duration = try(each.value.master_password_rotation_duration, var.defaults.master_password_rotation_duration, null) master_password_rotation_schedule_expression = try(each.value.master_password_rotation_schedule_expression, var.defaults.master_password_rotation_schedule_expression, null) master_password_secret_kms_key_id = try(each.value.master_password_secret_kms_key_id, var.defaults.master_password_secret_kms_key_id, null) + master_password_wo = try(each.value.master_password_wo, var.defaults.master_password_wo, null) + master_password_wo_version = try(each.value.master_password_wo_version, var.defaults.master_password_wo_version, null) master_username = try(each.value.master_username, var.defaults.master_username, "awsuser") multi_az = try(each.value.multi_az, var.defaults.multi_az, null) node_type = try(each.value.node_type, var.defaults.node_type, "") number_of_nodes = try(each.value.number_of_nodes, var.defaults.number_of_nodes, 1) owner_account = try(each.value.owner_account, var.defaults.owner_account, null) parameter_group_description = try(each.value.parameter_group_description, var.defaults.parameter_group_description, null) - parameter_group_family = try(each.value.parameter_group_family, var.defaults.parameter_group_family, "redshift-1.0") + parameter_group_family = try(each.value.parameter_group_family, var.defaults.parameter_group_family, "redshift-2.0") parameter_group_name = try(each.value.parameter_group_name, var.defaults.parameter_group_name, null) - parameter_group_parameters = try(each.value.parameter_group_parameters, var.defaults.parameter_group_parameters, {}) + parameter_group_parameters = try(each.value.parameter_group_parameters, var.defaults.parameter_group_parameters, null) parameter_group_tags = try(each.value.parameter_group_tags, var.defaults.parameter_group_tags, {}) - port = try(each.value.port, var.defaults.port, null) + port = try(each.value.port, var.defaults.port, 5439) preferred_maintenance_window = try(each.value.preferred_maintenance_window, var.defaults.preferred_maintenance_window, "sat:10:00-sat:10:30") - publicly_accessible = try(each.value.publicly_accessible, var.defaults.publicly_accessible, false) - random_password_length = try(each.value.random_password_length, var.defaults.random_password_length, 16) + publicly_accessible = try(each.value.publicly_accessible, var.defaults.publicly_accessible, null) + region = try(each.value.region, var.defaults.region, null) scheduled_actions = try(each.value.scheduled_actions, var.defaults.scheduled_actions, {}) + security_group_description = try(each.value.security_group_description, var.defaults.security_group_description, null) + security_group_egress_rules = try(each.value.security_group_egress_rules, var.defaults.security_group_egress_rules, {}) + security_group_ingress_rules = try(each.value.security_group_ingress_rules, var.defaults.security_group_ingress_rules, {}) + security_group_name = try(each.value.security_group_name, var.defaults.security_group_name, "") + security_group_tags = try(each.value.security_group_tags, var.defaults.security_group_tags, {}) + security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, var.defaults.security_group_use_name_prefix, true) skip_final_snapshot = try(each.value.skip_final_snapshot, var.defaults.skip_final_snapshot, true) + snapshot_arn = try(each.value.snapshot_arn, var.defaults.snapshot_arn, null) snapshot_cluster_identifier = try(each.value.snapshot_cluster_identifier, var.defaults.snapshot_cluster_identifier, null) - snapshot_copy = try(each.value.snapshot_copy, var.defaults.snapshot_copy, {}) + snapshot_copy = try(each.value.snapshot_copy, var.defaults.snapshot_copy, null) snapshot_identifier = try(each.value.snapshot_identifier, var.defaults.snapshot_identifier, null) - snapshot_schedule_definitions = try(each.value.snapshot_schedule_definitions, var.defaults.snapshot_schedule_definitions, []) - snapshot_schedule_description = try(each.value.snapshot_schedule_description, var.defaults.snapshot_schedule_description, null) - snapshot_schedule_force_destroy = try(each.value.snapshot_schedule_force_destroy, var.defaults.snapshot_schedule_force_destroy, null) - snapshot_schedule_identifier = try(each.value.snapshot_schedule_identifier, var.defaults.snapshot_schedule_identifier, null) + snapshot_schedule = try(each.value.snapshot_schedule, var.defaults.snapshot_schedule, null) subnet_group_description = try(each.value.subnet_group_description, var.defaults.subnet_group_description, null) subnet_group_name = try(each.value.subnet_group_name, var.defaults.subnet_group_name, null) subnet_group_tags = try(each.value.subnet_group_tags, var.defaults.subnet_group_tags, {}) subnet_ids = try(each.value.subnet_ids, var.defaults.subnet_ids, []) tags = try(each.value.tags, var.defaults.tags, {}) usage_limits = try(each.value.usage_limits, var.defaults.usage_limits, {}) - use_snapshot_identifier_prefix = try(each.value.use_snapshot_identifier_prefix, var.defaults.use_snapshot_identifier_prefix, true) + vpc_id = try(each.value.vpc_id, var.defaults.vpc_id, "") vpc_security_group_ids = try(each.value.vpc_security_group_ids, var.defaults.vpc_security_group_ids, []) } diff --git a/wrappers/versions.tf b/wrappers/versions.tf index 512dd2c..49d3842 100644 --- a/wrappers/versions.tf +++ b/wrappers/versions.tf @@ -1,15 +1,10 @@ terraform { - required_version = ">= 1.3" + required_version = ">= 1.11" required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.45" - } - - random = { - source = "hashicorp/random" - version = ">= 3.0" + version = ">= 6.21" } } }