From 359c26c34522d6585939b9297b85ace6702470ba Mon Sep 17 00:00:00 2001 From: Guilherme Branco Date: Wed, 18 Dec 2024 09:19:11 -0300 Subject: [PATCH] OCM-12850 | feat: include shared vpc support and example --- assets/assume_role_policy.tpl | 11 + assets/bastion_connect.sh | 15 +- assets/bastion_disconnect.sh | 1 - .../rosa-hcp-private-shared-vpc/README.md | 87 ++++++++ examples/rosa-hcp-private-shared-vpc/main.tf | 211 ++++++++++++++++++ .../rosa-hcp-private-shared-vpc/outputs.tf | 90 ++++++++ .../rosa-hcp-private-shared-vpc/variables.tf | 72 ++++++ .../rosa-hcp-private-shared-vpc/versions.tf | 22 ++ examples/rosa-hcp-private/README.md | 3 +- examples/rosa-hcp-private/main.tf | 27 --- examples/rosa-hcp-private/outputs.tf | 5 + main.tf | 54 +++-- modules/account-iam-resources/README.md | 14 +- modules/account-iam-resources/main.tf | 83 ++++++- modules/account-iam-resources/outputs.tf | 5 + modules/account-iam-resources/variables.tf | 21 +- modules/account-iam-resources/versions.tf | 2 +- modules/bastion-host/README.md | 2 + modules/bastion-host/main.tf | 32 ++- modules/bastion-host/outputs.tf | 5 + modules/operator-roles/README.md | 8 + modules/operator-roles/main.tf | 74 +++++- modules/operator-roles/outputs.tf | 5 + modules/operator-roles/variables.tf | 17 +- modules/rosa-cluster-hcp/README.md | 8 +- modules/rosa-cluster-hcp/main.tf | 17 +- modules/rosa-cluster-hcp/variables.tf | 29 +++ modules/rosa-cluster-hcp/versions.tf | 2 +- modules/shared-vpc-resources/README.md | 78 +++++++ .../shared-vpc-resources/hosted-zones/main.tf | 35 +++ .../hosted-zones/outputs.tf | 19 ++ .../hosted-zones/variables.tf | 26 +++ .../hosted-zones/versions.tf | 10 + modules/shared-vpc-resources/main.tf | 67 ++++++ modules/shared-vpc-resources/outputs.tf | 44 ++++ .../shared-vpc-resources/route53-role/main.tf | 52 +++++ .../route53-role/output.tf | 14 ++ .../route53-role/variables.tf | 25 +++ .../route53-role/versions.tf | 14 ++ .../subnets-share/main.tf | 26 +++ .../subnets-share/output.tf | 9 + .../subnets-share/variables.tf | 14 ++ .../subnets-share/versions.tf | 10 + modules/shared-vpc-resources/variables.tf | 51 +++++ modules/shared-vpc-resources/versions.tf | 14 ++ .../shared-vpc-resources/vpce-role/main.tf | 52 +++++ .../shared-vpc-resources/vpce-role/output.tf | 14 ++ .../vpce-role/variables.tf | 20 ++ .../vpce-role/versions.tf | 14 ++ variables.tf | 29 ++- 50 files changed, 1464 insertions(+), 95 deletions(-) create mode 100644 assets/assume_role_policy.tpl create mode 100644 examples/rosa-hcp-private-shared-vpc/README.md create mode 100644 examples/rosa-hcp-private-shared-vpc/main.tf create mode 100644 examples/rosa-hcp-private-shared-vpc/outputs.tf create mode 100644 examples/rosa-hcp-private-shared-vpc/variables.tf create mode 100644 examples/rosa-hcp-private-shared-vpc/versions.tf create mode 100644 modules/shared-vpc-resources/README.md create mode 100644 modules/shared-vpc-resources/hosted-zones/main.tf create mode 100644 modules/shared-vpc-resources/hosted-zones/outputs.tf create mode 100644 modules/shared-vpc-resources/hosted-zones/variables.tf create mode 100644 modules/shared-vpc-resources/hosted-zones/versions.tf create mode 100644 modules/shared-vpc-resources/main.tf create mode 100644 modules/shared-vpc-resources/outputs.tf create mode 100644 modules/shared-vpc-resources/route53-role/main.tf create mode 100644 modules/shared-vpc-resources/route53-role/output.tf create mode 100644 modules/shared-vpc-resources/route53-role/variables.tf create mode 100644 modules/shared-vpc-resources/route53-role/versions.tf create mode 100644 modules/shared-vpc-resources/subnets-share/main.tf create mode 100644 modules/shared-vpc-resources/subnets-share/output.tf create mode 100644 modules/shared-vpc-resources/subnets-share/variables.tf create mode 100644 modules/shared-vpc-resources/subnets-share/versions.tf create mode 100644 modules/shared-vpc-resources/variables.tf create mode 100644 modules/shared-vpc-resources/versions.tf create mode 100644 modules/shared-vpc-resources/vpce-role/main.tf create mode 100644 modules/shared-vpc-resources/vpce-role/output.tf create mode 100644 modules/shared-vpc-resources/vpce-role/variables.tf create mode 100644 modules/shared-vpc-resources/vpce-role/versions.tf diff --git a/assets/assume_role_policy.tpl b/assets/assume_role_policy.tpl new file mode 100644 index 0000000..6f7c72f --- /dev/null +++ b/assets/assume_role_policy.tpl @@ -0,0 +1,11 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AssumeInto", + "Effect": "Allow", + "Action": "sts:AssumeRole", + "Resource": "${aws_role_arn}" + } + ] +} \ No newline at end of file diff --git a/assets/bastion_connect.sh b/assets/bastion_connect.sh index 23ab8ca..4cc0963 100644 --- a/assets/bastion_connect.sh +++ b/assets/bastion_connect.sh @@ -13,12 +13,13 @@ fi TERRAFORM_JSON=$(terraform output -json) # Assigns public IP of bastion host to variables -BASTION_HOST_PUB_IP=$(jq '.bastion_host_public_ip.value[0]' -r <<< $TERRAFORM_JSON) +BASTION_HOST_PUB_IP=$(echo $TERRAFORM_JSON | jq '.bastion_host_public_ip.value[0]' -r) # Sest bastion host ssh .pem filename to variable -ROSA_KEY=$(find . | grep '.pem') +ROSA_KEY=$(echo $TERRAFORM_JSON | jq '.bastion_host_pem_path.value' -r) # Get API url of Rosa Cluster -API=$(jq '.cluster_api_url.value' -r <<< $TERRAFORM_JSON) -PW=$(jq '.password.value.result' -r <<< $TERRAFORM_JSON) +API=$(echo $TERRAFORM_JSON | jq '.cluster_api_url.value' -r) +USERNAME=$(echo $TERRAFORM_JSON | jq '.cluster_admin_username.value' -r) +PW=$(echo $TERRAFORM_JSON | jq '.cluster_admin_password.value' -r) if [ -z "$API" ]; then echo "Could not find the API URL" @@ -32,6 +33,10 @@ if [ -z "$BASTION_HOST_PUB_IP" ]; then echo "Could not find the SSH bastion host IP address" exit 4 fi +if [ -z "$USERNAME" ]; then + echo "Could not find the cluster idp username" + exit 4 +fi if [ -z "$PW" ]; then echo "Could not find the cluster idp password" exit 4 @@ -40,4 +45,4 @@ fi # Connect to the SSH bastion # Note that the user depends on AMI and might require to be changed sshuttle --daemon --pidfile="${TF_DIR:-.}/sshuttle-pid-file" --ssh-cmd "ssh -i ${TF_DIR:-.}/${ROSA_KEY}" --dns -NHr "ec2-user@${BASTION_HOST_PUB_IP}" 10.0.0.0/16 -oc login $API --username admin --password "${PW}" \ No newline at end of file +oc login $API --username $USERNAME --password "${PW}" diff --git a/assets/bastion_disconnect.sh b/assets/bastion_disconnect.sh index 423bd3d..ac581e6 100644 --- a/assets/bastion_disconnect.sh +++ b/assets/bastion_disconnect.sh @@ -1,3 +1,2 @@ - PID=$(cat ${TF_DIR:-.}/sshuttle-pid-file) kill $PID \ No newline at end of file diff --git a/examples/rosa-hcp-private-shared-vpc/README.md b/examples/rosa-hcp-private-shared-vpc/README.md new file mode 100644 index 0000000..01e59a7 --- /dev/null +++ b/examples/rosa-hcp-private-shared-vpc/README.md @@ -0,0 +1,87 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | +| [null](#requirement\_null) | >= 3.0.0 | +| [random](#requirement\_random) | >= 2.0 | +| [rhcs](#requirement\_rhcs) | >= 1.6.8 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | +| [aws.cluster-owner](#provider\_aws.cluster-owner) | >= 4.0 | +| [aws.network-owner](#provider\_aws.network-owner) | >= 4.0 | +| [null](#provider\_null) | >= 3.0.0 | +| [random](#provider\_random) | >= 2.0 | +| [rhcs](#provider\_rhcs) | >= 1.6.8 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [account\_iam\_resources](#module\_account\_iam\_resources) | ../../modules/account-iam-resources | n/a | +| [bastion\_host](#module\_bastion\_host) | ../../modules/bastion-host | n/a | +| [oidc\_config\_and\_provider](#module\_oidc\_config\_and\_provider) | ../../modules/oidc-config-and-provider | n/a | +| [operator\_roles](#module\_operator\_roles) | ../../modules/operator-roles | n/a | +| [rosa\_cluster\_hcp](#module\_rosa\_cluster\_hcp) | ../../modules/rosa-cluster-hcp | n/a | +| [shared-vpc-resources](#module\_shared-vpc-resources) | ../../modules/shared-vpc-resources | n/a | +| [vpc](#module\_vpc) | ../../modules/vpc | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_ec2_tag.tag_private_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource | +| [null_resource.validations](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [random_password.password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [rhcs_dns_domain.dns_domain](https://registry.terraform.io/providers/terraform-redhat/rhcs/latest/docs/resources/dns_domain) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_caller_identity.shared_vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_billing\_account\_id](#input\_aws\_billing\_account\_id) | The AWS billing account identifier where all resources are billed. If no information is provided, the data will be retrieved from the currently connected account. | `string` | `null` | no | +| [cluster\_name](#input\_cluster\_name) | Name of the cluster. After the creation of the resource, it is not possible to update the attribute value. | `string` | n/a | yes | +| [cluster\_owner\_aws\_access\_key\_id](#input\_cluster\_owner\_aws\_access\_key\_id) | The access key provides access to AWS services and is associated with the shared-vpc AWS account. | `string` | `""` | no | +| [cluster\_owner\_aws\_profile](#input\_cluster\_owner\_aws\_profile) | The name of the AWS profile configured in the AWS credentials file (typically located at ~/.aws/credentials). This profile contains the access key, secret key, and optional session token associated with the shared-vpc AWS account. | `string` | `""` | no | +| [cluster\_owner\_aws\_secret\_access\_key](#input\_cluster\_owner\_aws\_secret\_access\_key) | The secret key paired with the access key. Together, they provide the necessary credentials for Terraform to authenticate with the shared-vpc AWS account and manage resources securely. | `string` | `""` | no | +| [cluster\_owner\_aws\_shared\_credentials\_files](#input\_cluster\_owner\_aws\_shared\_credentials\_files) | List of files path to the AWS shared credentials file. This file typically contains AWS access keys and secret keys and is used when authenticating with AWS using profiles (default file located at ~/.aws/credentials). | `list(string)` | `null` | no | +| [network\_owner\_aws\_access\_key\_id](#input\_network\_owner\_aws\_access\_key\_id) | The access key provides access to AWS services and is associated with the shared-vpc AWS account. | `string` | `""` | no | +| [network\_owner\_aws\_profile](#input\_network\_owner\_aws\_profile) | The name of the AWS profile configured in the AWS credentials file (typically located at ~/.aws/credentials). This profile contains the access key, secret key, and optional session token associated with the shared-vpc AWS account. | `string` | `""` | no | +| [network\_owner\_aws\_secret\_access\_key](#input\_network\_owner\_aws\_secret\_access\_key) | The secret key paired with the access key. Together, they provide the necessary credentials for Terraform to authenticate with the shared-vpc AWS account and manage resources securely. | `string` | `""` | no | +| [network\_owner\_aws\_shared\_credentials\_files](#input\_network\_owner\_aws\_shared\_credentials\_files) | List of files path to the AWS shared credentials file. This file typically contains AWS access keys and secret keys and is used when authenticating with AWS using profiles (default file located at ~/.aws/credentials). | `list(string)` | `null` | no | +| [openshift\_version](#input\_openshift\_version) | The required version of Red Hat OpenShift for the cluster, for example '4.1.0'. If version is greater than the currently running version, an upgrade will be scheduled. | `string` | `"4.17.9"` | no | +| [version\_channel\_group](#input\_version\_channel\_group) | Desired channel group of the version [stable, candidate, fast, nightly]. | `string` | `"stable"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [account\_role\_prefix](#output\_account\_role\_prefix) | The prefix used for all generated AWS resources. | +| [account\_roles\_arn](#output\_account\_roles\_arn) | A map of Amazon Resource Names (ARNs) associated with the AWS IAM roles created. The key in the map represents the name of an AWS IAM role, while the corresponding value represents the associated Amazon Resource Name (ARN) of that role. | +| [bastion\_host\_pem\_path](#output\_bastion\_host\_pem\_path) | Bastion Host key file path | +| [bastion\_host\_public\_ip](#output\_bastion\_host\_public\_ip) | Bastion Host Public IP | +| [cluster\_admin\_password](#output\_cluster\_admin\_password) | The password of the admin user. | +| [cluster\_admin\_username](#output\_cluster\_admin\_username) | The username of the admin user. | +| [cluster\_api\_url](#output\_cluster\_api\_url) | URL of the API server. | +| [cluster\_id](#output\_cluster\_id) | Unique identifier of the cluster. | +| [console\_url](#output\_console\_url) | URL of the console. | +| [current\_version](#output\_current\_version) | The currently running version of OpenShift on the cluster, for example '4.11.0'. | +| [domain](#output\_domain) | DNS domain of cluster. | +| [oidc\_config\_id](#output\_oidc\_config\_id) | The unique identifier associated with users authenticated through OpenID Connect (OIDC) generated by this OIDC config. | +| [oidc\_endpoint\_url](#output\_oidc\_endpoint\_url) | Registered OIDC configuration issuer URL, generated by this OIDC config. | +| [operator\_role\_prefix](#output\_operator\_role\_prefix) | Prefix used for generated AWS operator policies. | +| [operator\_roles\_arn](#output\_operator\_roles\_arn) | List of Amazon Resource Names (ARNs) for all operator roles created. | +| [password](#output\_password) | n/a | +| [path](#output\_path) | The arn path for the account/operator roles as well as their policies. | +| [state](#output\_state) | The state of the cluster. | + \ No newline at end of file diff --git a/examples/rosa-hcp-private-shared-vpc/main.tf b/examples/rosa-hcp-private-shared-vpc/main.tf new file mode 100644 index 0000000..e5b66a3 --- /dev/null +++ b/examples/rosa-hcp-private-shared-vpc/main.tf @@ -0,0 +1,211 @@ +data "aws_partition" "current" {} +data "aws_region" "current" {} +provider "aws" { + alias = "cluster-owner" + + access_key = var.cluster_owner_aws_access_key_id + secret_key = var.cluster_owner_aws_secret_access_key + region = data.aws_region.current.name + profile = var.cluster_owner_aws_profile + shared_credentials_files = var.cluster_owner_aws_shared_credentials_files +} + +locals { + account_role_prefix = "${var.cluster_name}-acc" + operator_role_prefix = "${var.cluster_name}-op" + shared_resources_name_prefix = var.cluster_name + shared_route53_role_name = substr("${local.shared_resources_name_prefix}-shared-route53-role", 0, 64) + shared_vpce_role_name = substr("${local.shared_resources_name_prefix}-shared-vpce-role", 0, 64) + # Required to generate the expected names for the shared vpc role arns + # There is a cyclic dependency on the shared vpc role arns and the installer,control-plane,ingress roles + # that is because AWS will not accept to include these into the trust policy without first creating it + # however, will allow to generate a permission policy with these values before the creation of the roles + shared_vpc_roles_arns = { + "route53" : "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.shared_vpc.account_id}:role/${local.shared_route53_role_name}", + "vpce" : "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.shared_vpc.account_id}:role/${local.shared_vpce_role_name}" + } +} + +data "aws_caller_identity" "current" { + provider = aws.cluster-owner +} + +############################################################## +# Account roles includes IAM roles and IAM policies +############################################################## +module "account_iam_resources" { + source = "../../modules/account-iam-resources" + providers = { + aws = aws.cluster-owner + } + + account_role_prefix = local.account_role_prefix + create_shared_vpc_policies = true + shared_vpc_roles = local.shared_vpc_roles_arns +} + +############################ +# OIDC provider +############################ +module "oidc_config_and_provider" { + source = "../../modules/oidc-config-and-provider" + providers = { + aws = aws.cluster-owner + } + + managed = true +} + +############################ +# operator roles +############################ +module "operator_roles" { + source = "../../modules/operator-roles" + providers = { + aws = aws.cluster-owner + } + + operator_role_prefix = local.operator_role_prefix + path = module.account_iam_resources.path + oidc_endpoint_url = module.oidc_config_and_provider.oidc_endpoint_url + create_shared_vpc_policies = false + shared_vpc_roles = local.shared_vpc_roles_arns +} + +resource "rhcs_dns_domain" "dns_domain" { + cluster_arch = "hcp" +} + +############################ +# shared-vpc-resources +############################ +provider "aws" { + alias = "network-owner" + + access_key = var.network_owner_aws_access_key_id + secret_key = var.network_owner_aws_secret_access_key + region = data.aws_region.current.name + profile = var.network_owner_aws_profile + shared_credentials_files = var.network_owner_aws_shared_credentials_files +} + +data "aws_caller_identity" "shared_vpc" { + provider = aws.network-owner +} + +############################ +# VPC +############################ +module "vpc" { + source = "../../modules/vpc" + + providers = { + aws = aws.network-owner + } + + name_prefix = local.shared_resources_name_prefix + availability_zones_count = 1 +} + +############################ +# Bastion instance for connection to the cluster +############################ +module "bastion_host" { + providers = { + aws = aws.network-owner + } + source = "../../modules/bastion-host" + prefix = var.cluster_name + vpc_id = module.vpc.vpc_id + subnet_ids = [module.vpc.public_subnets[0]] +} + +module "shared-vpc-resources" { + source = "../../modules/shared-vpc-resources" + + providers = { + aws = aws.network-owner + } + + cluster_name = var.cluster_name + account_roles_prefix = module.account_iam_resources.account_role_prefix + operator_roles_prefix = module.operator_roles.operator_role_prefix + ingress_private_hosted_zone_base_domain = rhcs_dns_domain.dns_domain.id + name_prefix = local.shared_resources_name_prefix + target_aws_account = data.aws_caller_identity.current.account_id + subnets = concat(module.vpc.private_subnets) + vpc_id = module.vpc.vpc_id +} + +resource "aws_ec2_tag" "tag_private_subnets" { + provider = aws.cluster-owner + count = length(module.vpc.private_subnets) + resource_id = module.vpc.private_subnets[count.index] + key = "kubernetes.io/role/internal-elb" + value = "" +} + +############################ +# ROSA STS cluster +############################ +module "rosa_cluster_hcp" { + source = "../../modules/rosa-cluster-hcp" + providers = { + aws = aws.cluster-owner + } + + cluster_name = var.cluster_name + openshift_version = var.openshift_version + version_channel_group = var.version_channel_group + machine_cidr = module.vpc.cidr_block + aws_subnet_ids = module.shared-vpc-resources.shared_subnets + replicas = 2 + private = true + create_admin_user = true + admin_credentials_username = "admin" + admin_credentials_password = random_password.password.result + ec2_metadata_http_tokens = "required" + aws_billing_account_id = var.aws_billing_account_id + + // STS configuration + oidc_config_id = module.oidc_config_and_provider.oidc_config_id + account_role_prefix = module.account_iam_resources.account_role_prefix + operator_role_prefix = module.operator_roles.operator_role_prefix + shared_vpc = { + ingress_private_hosted_zone_id = module.shared-vpc-resources.ingress_private_hosted_zone_id + internal_communication_private_hosted_zone_id = module.shared-vpc-resources.hcp_internal_communication_private_hosted_zone_id + route53_role_arn = module.shared-vpc-resources.route53_role_arn + vpce_role_arn = module.shared-vpc-resources.vpce_role_arn + } + base_dns_domain = rhcs_dns_domain.dns_domain.id + aws_additional_allowed_principals = [module.shared-vpc-resources.route53_role_arn, module.shared-vpc-resources.vpce_role_arn] +} + +resource "random_password" "password" { + length = 14 + special = true + min_lower = 1 + min_numeric = 1 + min_special = 1 + min_upper = 1 +} + +locals { + network_owner_aws_credentials_provided = length(var.network_owner_aws_access_key_id) > 0 && length(var.network_owner_aws_secret_access_key) > 0 + network_owner_aws_profile_provided = length(var.network_owner_aws_profile) > 0 + cluster_owner_aws_credentials_provided = length(var.cluster_owner_aws_access_key_id) > 0 && length(var.cluster_owner_aws_secret_access_key) > 0 + cluster_owner_aws_profile_provided = length(var.cluster_owner_aws_profile) > 0 +} + +resource "null_resource" "validations" { + lifecycle { + precondition { + condition = (local.network_owner_aws_credentials_provided == false && local.network_owner_aws_profile_provided == false) == false + error_message = "AWS credentials for the network-owner account must be provided. This can provided with \"var.network_owner_aws_access_key_id\" and \"var.network_owner_aws_secret_access_key\" or with existing profile \"var.network_owner_aws_profile\"" + } + precondition { + condition = (local.cluster_owner_aws_credentials_provided == false && local.cluster_owner_aws_profile_provided == false) == false + error_message = "AWS credentials for the cluster-owner account must be provided. This can provided with \"var.cluster_owner_aws_access_key_id\" and \"var.cluster_owner_aws_secret_access_key\" or with existing profile \"var.cluster_owner_aws_profile\"" + } + } +} diff --git a/examples/rosa-hcp-private-shared-vpc/outputs.tf b/examples/rosa-hcp-private-shared-vpc/outputs.tf new file mode 100644 index 0000000..6798a0e --- /dev/null +++ b/examples/rosa-hcp-private-shared-vpc/outputs.tf @@ -0,0 +1,90 @@ +output "bastion_host_public_ip" { + value = module.bastion_host.bastion_host_public_ip + description = "Bastion Host Public IP" +} + +output "bastion_host_pem_path" { + value = module.bastion_host.bastion_host_pem_path + description = "Bastion Host key file path" +} + +output "cluster_id" { + value = module.rosa_cluster_hcp.cluster_id + description = "Unique identifier of the cluster." +} + +output "cluster_api_url" { + value = module.rosa_cluster_hcp.cluster_api_url + description = "URL of the API server." +} + +output "console_url" { + value = module.rosa_cluster_hcp.cluster_console_url + description = "URL of the console." +} + +output "domain" { + value = module.rosa_cluster_hcp.cluster_domain + description = "DNS domain of cluster." +} + +output "current_version" { + value = module.rosa_cluster_hcp.cluster_current_version + description = "The currently running version of OpenShift on the cluster, for example '4.11.0'." +} + +output "state" { + value = module.rosa_cluster_hcp.cluster_state + description = "The state of the cluster." +} + +output "cluster_admin_username" { + value = module.rosa_cluster_hcp.cluster_admin_username + description = "The username of the admin user." +} + +output "cluster_admin_password" { + value = module.rosa_cluster_hcp.cluster_admin_password + description = "The password of the admin user." + sensitive = true +} + +output "account_role_prefix" { + value = module.account_iam_resources.account_role_prefix + description = "The prefix used for all generated AWS resources." +} + +output "account_roles_arn" { + value = module.account_iam_resources.account_roles_arn + description = "A map of Amazon Resource Names (ARNs) associated with the AWS IAM roles created. The key in the map represents the name of an AWS IAM role, while the corresponding value represents the associated Amazon Resource Name (ARN) of that role." +} + +output "path" { + value = module.account_iam_resources.path + description = "The arn path for the account/operator roles as well as their policies." +} + +output "oidc_config_id" { + value = module.oidc_config_and_provider.oidc_config_id + description = "The unique identifier associated with users authenticated through OpenID Connect (OIDC) generated by this OIDC config." +} + +output "oidc_endpoint_url" { + value = module.oidc_config_and_provider.oidc_endpoint_url + description = "Registered OIDC configuration issuer URL, generated by this OIDC config." +} + +output "operator_role_prefix" { + value = module.operator_roles.operator_role_prefix + description = "Prefix used for generated AWS operator policies." +} + +output "operator_roles_arn" { + value = module.operator_roles.operator_roles_arn + description = "List of Amazon Resource Names (ARNs) for all operator roles created." +} + +output "password" { + value = resource.random_password.password + sensitive = true +} diff --git a/examples/rosa-hcp-private-shared-vpc/variables.tf b/examples/rosa-hcp-private-shared-vpc/variables.tf new file mode 100644 index 0000000..cdc778f --- /dev/null +++ b/examples/rosa-hcp-private-shared-vpc/variables.tf @@ -0,0 +1,72 @@ +variable "cluster_name" { + type = string + description = "Name of the cluster. After the creation of the resource, it is not possible to update the attribute value." +} + +variable "openshift_version" { + type = string + default = "4.17.9" + description = "The required version of Red Hat OpenShift for the cluster, for example '4.1.0'. If version is greater than the currently running version, an upgrade will be scheduled." +} + +variable "version_channel_group" { + type = string + default = "stable" + description = "Desired channel group of the version [stable, candidate, fast, nightly]." +} + +variable "aws_billing_account_id" { + type = string + default = null + description = "The AWS billing account identifier where all resources are billed. If no information is provided, the data will be retrieved from the currently connected account." +} + +variable "network_owner_aws_access_key_id" { + type = string + default = "" + description = "The access key provides access to AWS services and is associated with the shared-vpc AWS account." +} + +variable "network_owner_aws_secret_access_key" { + type = string + default = "" + description = "The secret key paired with the access key. Together, they provide the necessary credentials for Terraform to authenticate with the shared-vpc AWS account and manage resources securely." + sensitive = true +} + +variable "network_owner_aws_profile" { + type = string + default = "" + description = "The name of the AWS profile configured in the AWS credentials file (typically located at ~/.aws/credentials). This profile contains the access key, secret key, and optional session token associated with the shared-vpc AWS account." +} + +variable "network_owner_aws_shared_credentials_files" { + type = list(string) + default = null + description = "List of files path to the AWS shared credentials file. This file typically contains AWS access keys and secret keys and is used when authenticating with AWS using profiles (default file located at ~/.aws/credentials)." +} + +variable "cluster_owner_aws_access_key_id" { + type = string + default = "" + description = "The access key provides access to AWS services and is associated with the shared-vpc AWS account." +} + +variable "cluster_owner_aws_secret_access_key" { + type = string + default = "" + description = "The secret key paired with the access key. Together, they provide the necessary credentials for Terraform to authenticate with the shared-vpc AWS account and manage resources securely." + sensitive = true +} + +variable "cluster_owner_aws_profile" { + type = string + default = "" + description = "The name of the AWS profile configured in the AWS credentials file (typically located at ~/.aws/credentials). This profile contains the access key, secret key, and optional session token associated with the shared-vpc AWS account." +} + +variable "cluster_owner_aws_shared_credentials_files" { + type = list(string) + default = null + description = "List of files path to the AWS shared credentials file. This file typically contains AWS access keys and secret keys and is used when authenticating with AWS using profiles (default file located at ~/.aws/credentials)." +} \ No newline at end of file diff --git a/examples/rosa-hcp-private-shared-vpc/versions.tf b/examples/rosa-hcp-private-shared-vpc/versions.tf new file mode 100644 index 0000000..370405f --- /dev/null +++ b/examples/rosa-hcp-private-shared-vpc/versions.tf @@ -0,0 +1,22 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + rhcs = { + version = ">= 1.6.8" + source = "terraform-redhat/rhcs" + } + random = { + source = "hashicorp/random" + version = ">= 2.0" + } + null = { + source = "hashicorp/null" + version = ">= 3.0.0" + } + } +} \ No newline at end of file diff --git a/examples/rosa-hcp-private/README.md b/examples/rosa-hcp-private/README.md index 6e345da..7d7fd92 100644 --- a/examples/rosa-hcp-private/README.md +++ b/examples/rosa-hcp-private/README.md @@ -119,7 +119,6 @@ module "bastion_host" { | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.35.0 | | [random](#provider\_random) | >= 2.0 | ## Modules @@ -135,7 +134,6 @@ module "bastion_host" { | Name | Type | |------|------| | [random_password.password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | -| [aws_ami.rhel9](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | ## Inputs @@ -150,6 +148,7 @@ module "bastion_host" { |------|-------------| | [account\_role\_prefix](#output\_account\_role\_prefix) | The prefix used for all generated AWS resources. | | [account\_roles\_arn](#output\_account\_roles\_arn) | A map of Amazon Resource Names (ARNs) associated with the AWS IAM roles created. The key in the map represents the name of an AWS IAM role, while the corresponding value represents the associated Amazon Resource Name (ARN) of that role. | +| [bastion\_host\_pem\_path](#output\_bastion\_host\_pem\_path) | Bastion Host key file path | | [bastion\_host\_public\_ip](#output\_bastion\_host\_public\_ip) | Bastion Host Public IP | | [cluster\_api\_url](#output\_cluster\_api\_url) | The URL of the API server. | | [cluster\_console\_url](#output\_cluster\_console\_url) | The URL of the console. | diff --git a/examples/rosa-hcp-private/main.tf b/examples/rosa-hcp-private/main.tf index 19ca941..d751f9f 100644 --- a/examples/rosa-hcp-private/main.tf +++ b/examples/rosa-hcp-private/main.tf @@ -50,36 +50,9 @@ module "vpc" { ############################ # Bastion instance for connection to the cluster ############################ -data "aws_ami" "rhel9" { - most_recent = true - - filter { - name = "platform-details" - values = ["Red Hat Enterprise Linux"] - } - - filter { - name = "architecture" - values = ["x86_64"] - } - - filter { - name = "root-device-type" - values = ["ebs"] - } - - filter { - name = "manifest-location" - values = ["amazon/RHEL-9.*_HVM-*-x86_64-*-Hourly2-GP2"] - } - - owners = ["309956199498"] # Amazon's "Official Red Hat" account -} module "bastion_host" { source = "../../modules/bastion-host" prefix = var.cluster_name vpc_id = module.vpc.vpc_id subnet_ids = [module.vpc.public_subnets[0]] - ami_id = data.aws_ami.rhel9.id - user_data_file = file("../../assets/bastion-host-user-data.yaml") } diff --git a/examples/rosa-hcp-private/outputs.tf b/examples/rosa-hcp-private/outputs.tf index 056e1ea..4507f02 100644 --- a/examples/rosa-hcp-private/outputs.tf +++ b/examples/rosa-hcp-private/outputs.tf @@ -3,6 +3,11 @@ output "bastion_host_public_ip" { description = "Bastion Host Public IP" } +output "bastion_host_pem_path" { + value = module.bastion_host.bastion_host_pem_path + description = "Bastion Host key file path" +} + output "cluster_id" { value = module.hcp.cluster_id description = "Unique identifier of the cluster." diff --git a/main.tf b/main.tf index 2482f29..439af1b 100644 --- a/main.tf +++ b/main.tf @@ -60,29 +60,37 @@ module "operator_roles" { ############################ # ROSA STS cluster ############################ +resource "rhcs_dns_domain" "dns_domain" { + count = create_dns_domain_reservation ? 1 : 0 + cluster_arch = "hcp" +} + module "rosa_cluster_hcp" { source = "./modules/rosa-cluster-hcp" - cluster_name = var.cluster_name - operator_role_prefix = var.create_operator_roles ? module.operator_roles[0].operator_role_prefix : local.operator_role_prefix - openshift_version = var.openshift_version - installer_role_arn = var.create_account_roles ? module.account_iam_resources[0].account_roles_arn["HCP-ROSA-Installer"] : local.sts_roles.installer_role_arn - support_role_arn = var.create_account_roles ? module.account_iam_resources[0].account_roles_arn["HCP-ROSA-Support"] : local.sts_roles.support_role_arn - worker_role_arn = var.create_account_roles ? module.account_iam_resources[0].account_roles_arn["HCP-ROSA-Worker"] : local.sts_roles.worker_role_arn - oidc_config_id = var.create_oidc ? module.oidc_config_and_provider[0].oidc_config_id : var.oidc_config_id - aws_subnet_ids = var.aws_subnet_ids - machine_cidr = var.machine_cidr - service_cidr = var.service_cidr - pod_cidr = var.pod_cidr - host_prefix = var.host_prefix - private = var.private - tags = var.tags - properties = var.properties - etcd_encryption = var.etcd_encryption - etcd_kms_key_arn = var.etcd_kms_key_arn - kms_key_arn = var.kms_key_arn - aws_billing_account_id = var.aws_billing_account_id - ec2_metadata_http_tokens = var.ec2_metadata_http_tokens + cluster_name = var.cluster_name + operator_role_prefix = var.create_operator_roles ? module.operator_roles[0].operator_role_prefix : local.operator_role_prefix + openshift_version = var.openshift_version + version_channel_group = var.version_channel_group + installer_role_arn = var.create_account_roles ? module.account_iam_resources[0].account_roles_arn["HCP-ROSA-Installer"] : local.sts_roles.installer_role_arn + support_role_arn = var.create_account_roles ? module.account_iam_resources[0].account_roles_arn["HCP-ROSA-Support"] : local.sts_roles.support_role_arn + worker_role_arn = var.create_account_roles ? module.account_iam_resources[0].account_roles_arn["HCP-ROSA-Worker"] : local.sts_roles.worker_role_arn + oidc_config_id = var.create_oidc ? module.oidc_config_and_provider[0].oidc_config_id : var.oidc_config_id + aws_subnet_ids = var.aws_subnet_ids + machine_cidr = var.machine_cidr + service_cidr = var.service_cidr + pod_cidr = var.pod_cidr + host_prefix = var.host_prefix + private = var.private + tags = var.tags + properties = var.properties + etcd_encryption = var.etcd_encryption + etcd_kms_key_arn = var.etcd_kms_key_arn + kms_key_arn = var.kms_key_arn + aws_billing_account_id = var.aws_billing_account_id + ec2_metadata_http_tokens = var.ec2_metadata_http_tokens + base_dns_domain = var.create_dns_domain_reservation ? rhcs_dns_domain.dns_domain[0].id : var.base_dns_domain + aws_additional_allowed_principals = var.aws_additional_allowed_principals ######## # Cluster Admin User @@ -104,9 +112,9 @@ module "rosa_cluster_hcp" { # Default Machine Pool ####################### - replicas = var.replicas - compute_machine_type = var.compute_machine_type - aws_availability_zones = var.aws_availability_zones + replicas = var.replicas + compute_machine_type = var.compute_machine_type + aws_availability_zones = var.aws_availability_zones aws_additional_compute_security_group_ids = var.aws_additional_compute_security_group_ids ######## diff --git a/modules/account-iam-resources/README.md b/modules/account-iam-resources/README.md index c1d749c..5774a23 100644 --- a/modules/account-iam-resources/README.md +++ b/modules/account-iam-resources/README.md @@ -27,7 +27,7 @@ module "account_iam_resources" { | [terraform](#requirement\_terraform) | >= 1.0 | | [aws](#requirement\_aws) | >= 5.38.0 | | [random](#requirement\_random) | >= 2.0 | -| [rhcs](#requirement\_rhcs) | >= 1.6.2 | +| [rhcs](#requirement\_rhcs) | >= 1.6.8 | | [time](#requirement\_time) | >= 0.9 | ## Providers @@ -36,7 +36,7 @@ module "account_iam_resources" { |------|---------| | [aws](#provider\_aws) | >= 5.38.0 | | [random](#provider\_random) | >= 2.0 | -| [rhcs](#provider\_rhcs) | >= 1.6.2 | +| [rhcs](#provider\_rhcs) | >= 1.6.8 | | [time](#provider\_time) | >= 0.9 | ## Modules @@ -47,10 +47,15 @@ No modules. | Name | Type | |------|------| +| [aws_iam_policy.route53_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.vpce_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.account_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.account_role_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.route53_policy_installer_account_role_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.vpce_policy_installer_account_role_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [random_string.default_random](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | | [time_sleep.account_iam_resources_wait](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy_document.custom_trust_policy](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 | | [rhcs_hcp_policies.all_policies](https://registry.terraform.io/providers/terraform-redhat/rhcs/latest/docs/data-sources/hcp_policies) | data source | @@ -61,9 +66,11 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [account\_role\_prefix](#input\_account\_role\_prefix) | Prefix to be used when creating the account roles | `string` | `"tf-acc"` | no | +| [create\_shared\_vpc\_policies](#input\_create\_shared\_vpc\_policies) | Signals to create the shared vpc policies, it might not be needed if created through another step | `bool` | `false` | no | | [path](#input\_path) | (Optional) The arn path for the account/operator roles as well as their policies. Must begin and end with '/'. | `string` | `"/"` | no | | [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the IAM roles in STS clusters. | `string` | `""` | no | -| [tags](#input\_tags) | List of AWS resource tags to apply. | `map(string)` | `null` | no | +| [shared\_vpc\_roles](#input\_shared\_vpc\_roles) | Mapping of shared vpc roles, available keys are [route53, vpce] | `map(string)` |
{
"route53": "",
"vpce": ""
}
| no | +| [tags](#input\_tags) | Mapping of AWS resource tags to apply. | `map(string)` | `null` | no | ## Outputs @@ -72,4 +79,5 @@ No modules. | [account\_role\_prefix](#output\_account\_role\_prefix) | The prefix used for all generated AWS resources. | | [account\_roles\_arn](#output\_account\_roles\_arn) | A map of Amazon Resource Names (ARNs) associated with the AWS IAM roles created. The key in the map represents the name of an AWS IAM role, while the corresponding value represents the associated Amazon Resource Name (ARN) of that role. | | [path](#output\_path) | The arn path for the account/operator roles as well as their policies. | +| [shared\_vpc\_policy\_attachments](#output\_shared\_vpc\_policy\_attachments) | A list of Amazon Resource Names (ARNs) related to the shared VPC | \ No newline at end of file diff --git a/modules/account-iam-resources/main.tf b/modules/account-iam-resources/main.tf index 17716a7..ad90989 100644 --- a/modules/account-iam-resources/main.tf +++ b/modules/account-iam-resources/main.tf @@ -9,10 +9,10 @@ locals { principal_identifier = "arn:${data.aws_partition.current.partition}:iam::${data.rhcs_info.current.ocm_aws_account_id}:role/RH-Managed-OpenShift-Installer" }, { - role_name = "HCP-ROSA-Support" - role_type = "support" - policy_details = "arn:aws:iam::aws:policy/service-role/ROSASRESupportPolicy" - principal_type = "AWS" + role_name = "HCP-ROSA-Support" + role_type = "support" + policy_details = "arn:aws:iam::aws:policy/service-role/ROSASRESupportPolicy" + principal_type = "AWS" // This is a SRE RH Support role which is used to assume this support role principal_identifier = data.rhcs_hcp_policies.all_policies.account_role_policies["sts_support_rh_sre_role"] }, @@ -30,8 +30,25 @@ locals { ) : ( "account-role-${random_string.default_random[0].result}" ) + route53_shared_role_arn = var.shared_vpc_roles["route53"] + route53_splits = split("/", local.route53_shared_role_arn) + route53_role_name = local.route53_splits[length(local.route53_splits) - 1] + route53_policy_name = substr("${local.route53_role_name}-assume-role", 0, 64) + vpce_shared_role_arn = var.shared_vpc_roles["vpce"] + vpce_splits = split("/", local.vpce_shared_role_arn) + vpce_role_name = local.vpce_splits[length(local.vpce_splits) - 1] + vpce_policy_name = substr("${local.vpce_role_name}-assume-role", 0, 64) + policy_arn_base = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy" } +data "aws_caller_identity" "current" {} + +data "rhcs_hcp_policies" "all_policies" {} + +data "aws_partition" "current" {} + +data "rhcs_info" "current" {} + data "aws_iam_policy_document" "custom_trust_policy" { count = local.account_roles_count @@ -75,20 +92,62 @@ resource "random_string" "default_random" { upper = false } -data "rhcs_hcp_policies" "all_policies" {} +### Shared VPC resources +resource "aws_iam_policy" "route53_policy" { + count = (local.route53_shared_role_arn != "" && var.create_shared_vpc_policies) ? 1 : 0 -data "aws_partition" "current" {} + name = local.route53_policy_name + policy = templatefile( + "${path.module}/../../assets/assume_role_policy.tpl", + { + aws_role_arn = local.route53_shared_role_arn + }, + ) + path = local.path + tags = merge(var.tags, { + red-hat-managed = true, + hcp-shared-vpc = true + }) +} -data "rhcs_info" "current" {} +resource "aws_iam_policy" "vpce_policy" { + count = (local.vpce_shared_role_arn != "" && var.create_shared_vpc_policies) ? 1 : 0 + + name = local.vpce_policy_name + policy = templatefile( + "${path.module}/../../assets/assume_role_policy.tpl", + { + aws_role_arn = local.vpce_shared_role_arn + }, + ) + path = local.path + tags = merge(var.tags, { + red-hat-managed = true, + hcp-shared-vpc = true + }) +} + +resource "aws_iam_role_policy_attachment" "route53_policy_installer_account_role_attachment" { + count = local.route53_shared_role_arn != "" ? 1 : 0 + role = aws_iam_role.account_role[0].name + policy_arn = var.create_shared_vpc_policies ? aws_iam_policy.route53_policy[0].arn : "${local.policy_arn_base}${local.path}${local.route53_policy_name}" +} + +resource "aws_iam_role_policy_attachment" "vpce_policy_installer_account_role_attachment" { + count = local.vpce_shared_role_arn != "" ? 1 : 0 + role = aws_iam_role.account_role[0].name + policy_arn = var.create_shared_vpc_policies ? aws_iam_policy.vpce_policy[0].arn : "${local.policy_arn_base}${local.path}${local.vpce_policy_name}" +} resource "time_sleep" "account_iam_resources_wait" { destroy_duration = "10s" create_duration = "10s" triggers = { - account_iam_role_name = jsonencode([ for value in aws_iam_role.account_role : value.name]) - account_roles_arn = jsonencode({ for idx, value in aws_iam_role.account_role : local.account_roles_properties[idx].role_name => value.arn }) - account_policy_arns = jsonencode([ for value in aws_iam_role_policy_attachment.account_role_policy_attachment : value.policy_arn]) - account_role_prefix = local.account_role_prefix_valid - path = local.path + account_iam_role_name = jsonencode([for value in aws_iam_role.account_role : value.name]) + account_roles_arn = jsonencode({ for idx, value in aws_iam_role.account_role : local.account_roles_properties[idx].role_name => value.arn }) + account_policy_arns = jsonencode([for value in aws_iam_role_policy_attachment.account_role_policy_attachment : value.policy_arn]) + account_role_prefix = local.account_role_prefix_valid + path = local.path + shared_vpc_policy_attachments = local.route53_shared_role_arn != "" && local.vpce_shared_role_arn != "" ? jsonencode([aws_iam_role_policy_attachment.route53_policy_installer_account_role_attachment[0].policy_arn, aws_iam_role_policy_attachment.vpce_policy_installer_account_role_attachment[0].policy_arn]) : jsonencode([]) } } diff --git a/modules/account-iam-resources/outputs.tf b/modules/account-iam-resources/outputs.tf index 3c1ab50..7e1ed13 100644 --- a/modules/account-iam-resources/outputs.tf +++ b/modules/account-iam-resources/outputs.tf @@ -11,4 +11,9 @@ output "account_roles_arn" { output "path" { value = time_sleep.account_iam_resources_wait.triggers["path"] description = "The arn path for the account/operator roles as well as their policies." +} + +output "shared_vpc_policy_attachments" { + value = jsondecode(time_sleep.account_iam_resources_wait.triggers["shared_vpc_policy_attachments"]) + description = "A list of Amazon Resource Names (ARNs) related to the shared VPC" } \ No newline at end of file diff --git a/modules/account-iam-resources/variables.tf b/modules/account-iam-resources/variables.tf index 9397f5c..a41dbe1 100644 --- a/modules/account-iam-resources/variables.tf +++ b/modules/account-iam-resources/variables.tf @@ -1,7 +1,7 @@ variable "account_role_prefix" { - type = string + type = string description = "Prefix to be used when creating the account roles" - default = "tf-acc" + default = "tf-acc" } variable "path" { @@ -17,7 +17,22 @@ variable "permissions_boundary" { } variable "tags" { - description = "List of AWS resource tags to apply." + description = "Mapping of AWS resource tags to apply." type = map(string) default = null } + +variable "shared_vpc_roles" { + description = "Mapping of shared vpc roles, available keys are [route53, vpce]" + type = map(string) + default = { + "route53" : "", + "vpce" : "" + } +} + +variable "create_shared_vpc_policies" { + description = "Signals to create the shared vpc policies, it might not be needed if created through another step" + type = bool + default = false +} diff --git a/modules/account-iam-resources/versions.tf b/modules/account-iam-resources/versions.tf index 5b9216d..dcd409e 100644 --- a/modules/account-iam-resources/versions.tf +++ b/modules/account-iam-resources/versions.tf @@ -7,7 +7,7 @@ terraform { version = ">= 5.38.0" } rhcs = { - version = ">= 1.6.2" + version = ">= 1.6.8" source = "terraform-redhat/rhcs" } time = { diff --git a/modules/bastion-host/README.md b/modules/bastion-host/README.md index ba42b98..418b283 100644 --- a/modules/bastion-host/README.md +++ b/modules/bastion-host/README.md @@ -67,6 +67,7 @@ No modules. | [local_file.bastion_private_ssh_key](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | | [time_sleep.bastion_resources_wait](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | | [tls_private_key.pk](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource | +| [aws_ami.rhel9](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | | [http_http.myip](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | data source | ## Inputs @@ -85,5 +86,6 @@ No modules. | Name | Description | |------|-------------| +| [bastion\_host\_pem\_path](#output\_bastion\_host\_pem\_path) | File path of bastion host .pem | | [bastion\_host\_public\_ip](#output\_bastion\_host\_public\_ip) | Bastion Host Public IP | \ No newline at end of file diff --git a/modules/bastion-host/main.tf b/modules/bastion-host/main.tf index 1644117..107b41e 100644 --- a/modules/bastion-host/main.tf +++ b/modules/bastion-host/main.tf @@ -38,16 +38,43 @@ resource "aws_security_group" "bastion_host_ingress" { } } +data "aws_ami" "rhel9" { + count = var.ami_id == null ? 1 : 0 + most_recent = true + + filter { + name = "platform-details" + values = ["Red Hat Enterprise Linux"] + } + + filter { + name = "architecture" + values = ["x86_64"] + } + + filter { + name = "root-device-type" + values = ["ebs"] + } + + filter { + name = "manifest-location" + values = ["amazon/RHEL-9.*_HVM-*-x86_64-*-Hourly2-GP2"] + } + + owners = ["309956199498"] # Amazon's "Official Red Hat" account +} + resource "aws_instance" "bastion_host" { count = length(var.subnet_ids) - ami = var.ami_id + ami = var.ami_id != null ? var.ami_id : data.aws_ami.rhel9[0].id instance_type = var.instance_type key_name = aws_key_pair.bastion_ssh_key.key_name vpc_security_group_ids = [aws_security_group.bastion_host_ingress.id] subnet_id = var.subnet_ids[count.index] associate_public_ip_address = true - user_data = var.user_data_file + user_data = var.user_data_file != null ? var.user_data_file : file("${path.module}/../../assets/bastion-host-user-data.yaml") user_data_replace_on_change = true tags = { Name = "${var.prefix}-bastion-host" @@ -59,5 +86,6 @@ resource "time_sleep" "bastion_resources_wait" { destroy_duration = "20s" triggers = { public_ips = jsonencode([for value in aws_instance.bastion_host : value.public_ip]) + pem_path = local_file.bastion_private_ssh_key.filename } } diff --git a/modules/bastion-host/outputs.tf b/modules/bastion-host/outputs.tf index 8f3b0ac..e732487 100644 --- a/modules/bastion-host/outputs.tf +++ b/modules/bastion-host/outputs.tf @@ -2,3 +2,8 @@ output "bastion_host_public_ip" { description = "Bastion Host Public IP" value = jsondecode(time_sleep.bastion_resources_wait.triggers["public_ips"]) } + +output "bastion_host_pem_path" { + description = "File path of bastion host .pem" + value = time_sleep.bastion_resources_wait.triggers["pem_path"] +} diff --git a/modules/operator-roles/README.md b/modules/operator-roles/README.md index 8794167..7e339f5 100644 --- a/modules/operator-roles/README.md +++ b/modules/operator-roles/README.md @@ -53,8 +53,13 @@ No modules. | Name | Type | |------|------| +| [aws_iam_policy.route53_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.vpce_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.operator_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.operator_role_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.route53_policy_control_plane_operator_role_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.route53_policy_ingress_operator_role_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.vpce_policy_control_plane_operator_role_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [time_sleep.role_resources_propagation](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy_document.custom_trust_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -63,10 +68,12 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [create\_shared\_vpc\_policies](#input\_create\_shared\_vpc\_policies) | Signals to create the shared vpc policies, it might not be needed if created through another step | `bool` | `false` | no | | [oidc\_endpoint\_url](#input\_oidc\_endpoint\_url) | oidc provider url | `string` | n/a | yes | | [operator\_role\_prefix](#input\_operator\_role\_prefix) | Prefix to be used when creating the operator roles | `string` | n/a | yes | | [path](#input\_path) | (Optional) The arn path for the account/operator roles as well as their policies. Must begin and end with '/'. | `string` | `"/"` | no | | [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the IAM roles in STS clusters. | `string` | `""` | no | +| [shared\_vpc\_roles](#input\_shared\_vpc\_roles) | Mapping of shared vpc roles, available keys are [route53, vpce] | `map(string)` |
{
"route53": "",
"vpce": ""
}
| no | | [tags](#input\_tags) | List of AWS resource tags to apply. | `map(string)` | `null` | no | ## Outputs @@ -75,4 +82,5 @@ No modules. |------|-------------| | [operator\_role\_prefix](#output\_operator\_role\_prefix) | Prefix used for generated AWS operator policies. | | [operator\_roles\_arn](#output\_operator\_roles\_arn) | List of Amazon Resource Names (ARNs) for all operator roles created. | +| [shared\_vpc\_policy\_attachments](#output\_shared\_vpc\_policy\_attachments) | A list of Amazon Resource Names (ARNs) related to the shared VPC | \ No newline at end of file diff --git a/modules/operator-roles/main.tf b/modules/operator-roles/main.tf index 2c59f18..db9c23d 100644 --- a/modules/operator-roles/main.tf +++ b/modules/operator-roles/main.tf @@ -60,8 +60,21 @@ locals { operator_roles_count = length(local.operator_roles_properties) operator_role_prefix = var.operator_role_prefix path = coalesce(var.path, "/") + + route53_shared_role_arn = var.shared_vpc_roles["route53"] + route53_splits = split("/", local.route53_shared_role_arn) + route53_role_name = local.route53_splits[length(local.route53_splits) - 1] + route53_policy_name = substr("${local.route53_role_name}-assume-role", 0, 64) + vpce_shared_role_arn = var.shared_vpc_roles["vpce"] + vpce_splits = split("/", local.vpce_shared_role_arn) + vpce_role_name = local.vpce_splits[length(local.vpce_splits) - 1] + vpce_policy_name = substr("${local.vpce_role_name}-assume-role", 0, 64) + + policy_arn_base = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy" } +data "aws_caller_identity" "current" {} + data "aws_iam_policy_document" "custom_trust_policy" { count = local.operator_roles_count @@ -102,13 +115,66 @@ resource "aws_iam_role_policy_attachment" "operator_role_policy_attachment" { policy_arn = local.operator_roles_properties[count.index].policy_details } -data "aws_caller_identity" "current" {} +### Shared VPC resources +resource "aws_iam_policy" "route53_policy" { + count = (local.route53_shared_role_arn != "" && var.create_shared_vpc_policies) ? 1 : 0 + + name = local.route53_policy_name + policy = templatefile( + "${path.module}/../../assets/assume_role_policy.tpl", + { + aws_role_arn = local.route53_shared_role_arn + }, + ) + path = local.path + tags = merge(var.tags, { + red-hat-managed = true, + hcp-shared-vpc = true + }) +} + +resource "aws_iam_policy" "vpce_policy" { + count = (local.vpce_shared_role_arn != "" && var.create_shared_vpc_policies) ? 1 : 0 + + name = local.vpce_policy_name + policy = templatefile( + "${path.module}/../../assets/assume_role_policy.tpl", + { + aws_role_arn = local.vpce_shared_role_arn + }, + ) + path = local.path + tags = merge(var.tags, { + red-hat-managed = true, + hcp-shared-vpc = true + }) +} + +resource "aws_iam_role_policy_attachment" "route53_policy_ingress_operator_role_attachment" { + count = local.route53_shared_role_arn != "" ? 1 : 0 + role = aws_iam_role.operator_role[1].name + policy_arn = var.create_shared_vpc_policies ? aws_iam_policy.route53_policy[0].arn : "${local.policy_arn_base}${local.path}${local.route53_policy_name}" +} + +resource "aws_iam_role_policy_attachment" "route53_policy_control_plane_operator_role_attachment" { + count = local.route53_shared_role_arn != "" ? 1 : 0 + role = aws_iam_role.operator_role[6].name + policy_arn = var.create_shared_vpc_policies ? aws_iam_policy.route53_policy[0].arn : "${local.policy_arn_base}${local.path}${local.route53_policy_name}" +} + +resource "aws_iam_role_policy_attachment" "vpce_policy_control_plane_operator_role_attachment" { + count = local.vpce_shared_role_arn != "" ? 1 : 0 + role = aws_iam_role.operator_role[6].name + policy_arn = var.create_shared_vpc_policies ? aws_iam_policy.vpce_policy[0].arn : "${local.policy_arn_base}${local.path}${local.vpce_policy_name}" +} +#### Outputs resource "time_sleep" "role_resources_propagation" { create_duration = "20s" triggers = { - operator_role_prefix = local.operator_role_prefix - operator_role_arns = jsonencode([for value in aws_iam_role.operator_role : value.arn]) - operator_policy_arns = jsonencode([for value in aws_iam_role_policy_attachment.operator_role_policy_attachment : value.policy_arn]) + operator_role_prefix = local.operator_role_prefix + operator_role_arns = jsonencode([for value in aws_iam_role.operator_role : value.arn]) + operator_policy_arns = jsonencode([for value in aws_iam_role_policy_attachment.operator_role_policy_attachment : value.policy_arn]) + shared_vpc_policy_attachments = local.route53_shared_role_arn != "" && local.vpce_shared_role_arn != "" ? jsonencode([aws_iam_role_policy_attachment.route53_policy_ingress_operator_role_attachment[0].policy_arn, aws_iam_role_policy_attachment.route53_policy_control_plane_operator_role_attachment[0].policy_arn, aws_iam_role_policy_attachment.vpce_policy_control_plane_operator_role_attachment[0].policy_arn]) : jsonencode([]) } } diff --git a/modules/operator-roles/outputs.tf b/modules/operator-roles/outputs.tf index 9860e27..93439ba 100644 --- a/modules/operator-roles/outputs.tf +++ b/modules/operator-roles/outputs.tf @@ -7,3 +7,8 @@ output "operator_roles_arn" { value = jsondecode(time_sleep.role_resources_propagation.triggers["operator_role_arns"]) description = "List of Amazon Resource Names (ARNs) for all operator roles created." } + +output "shared_vpc_policy_attachments" { + value = jsondecode(time_sleep.role_resources_propagation.triggers["shared_vpc_policy_attachments"]) + description = "A list of Amazon Resource Names (ARNs) related to the shared VPC" +} diff --git a/modules/operator-roles/variables.tf b/modules/operator-roles/variables.tf index e16cd09..c9e92df 100644 --- a/modules/operator-roles/variables.tf +++ b/modules/operator-roles/variables.tf @@ -1,5 +1,5 @@ variable "operator_role_prefix" { - type = string + type = string description = "Prefix to be used when creating the operator roles" } @@ -25,3 +25,18 @@ variable "oidc_endpoint_url" { description = "oidc provider url" type = string } + +variable "shared_vpc_roles" { + description = "Mapping of shared vpc roles, available keys are [route53, vpce]" + type = map(string) + default = { + "route53" : "", + "vpce" : "" + } +} + +variable "create_shared_vpc_policies" { + description = "Signals to create the shared vpc policies, it might not be needed if created through another step" + type = bool + default = false +} diff --git a/modules/rosa-cluster-hcp/README.md b/modules/rosa-cluster-hcp/README.md index b9665d2..856a097 100644 --- a/modules/rosa-cluster-hcp/README.md +++ b/modules/rosa-cluster-hcp/README.md @@ -39,14 +39,14 @@ module "rosa_cluster_hcp" { |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | | [aws](#requirement\_aws) | >= 5.38.0 | -| [rhcs](#requirement\_rhcs) | >= 1.6.2 | +| [rhcs](#requirement\_rhcs) | >= 1.6.8 | ## Providers | Name | Version | |------|---------| | [aws](#provider\_aws) | >= 5.38.0 | -| [rhcs](#provider\_rhcs) | >= 1.6.2 | +| [rhcs](#provider\_rhcs) | >= 1.6.8 | ## Modules @@ -78,11 +78,13 @@ No modules. | [autoscaler\_pod\_priority\_threshold](#input\_autoscaler\_pod\_priority\_threshold) | To allow users to schedule 'best-effort' pods, which shouldn't trigger Cluster Autoscaler actions, but only run when there are spare resources available. | `number` | `null` | no | | [aws\_account\_arn](#input\_aws\_account\_arn) | The ARN of the AWS account where all resources are created during the installation of the ROSA cluster. If no information is provided, the data will be retrieved from the currently connected account. | `string` | `null` | no | | [aws\_account\_id](#input\_aws\_account\_id) | The AWS account identifier where all resources are created during the installation of the ROSA cluster. If no information is provided, the data will be retrieved from the currently connected account. | `string` | `null` | no | +| [aws\_additional\_allowed\_principals](#input\_aws\_additional\_allowed\_principals) | The additional allowed principals to use when installing the cluster. | `list(string)` | `null` | no | | [aws\_additional\_compute\_security\_group\_ids](#input\_aws\_additional\_compute\_security\_group\_ids) | The additional security group IDs to be added to the default worker machine pool. | `list(string)` | `null` | no | | [aws\_availability\_zones](#input\_aws\_availability\_zones) | The AWS availability zones where instances of the default worker machine pool are deployed. Leave empty for the installer to pick availability zones | `list(string)` | `[]` | no | | [aws\_billing\_account\_id](#input\_aws\_billing\_account\_id) | The AWS billing account identifier where all resources are billed. If no information is provided, the data will be retrieved from the currently connected account. | `string` | `null` | no | | [aws\_region](#input\_aws\_region) | The full name of the AWS region used for the ROSA cluster installation, for example 'us-east-1'. If no information is provided, the data will be retrieved from the currently connected account. | `string` | `null` | no | | [aws\_subnet\_ids](#input\_aws\_subnet\_ids) | The Subnet IDs to use when installing the cluster. | `list(string)` | n/a | yes | +| [base\_dns\_domain](#input\_base\_dns\_domain) | Base DNS domain name previously reserved, e.g. '1vo8.p3.openshiftapps.com'. | `string` | `null` | no | | [cluster\_autoscaler\_enabled](#input\_cluster\_autoscaler\_enabled) | Enable Autoscaler for this cluster. This resource is currently unavailable and using will result in error 'Autoscaler configuration is not available' | `bool` | `false` | no | | [cluster\_name](#input\_cluster\_name) | Name of the cluster. After the creation of the resource, it is not possible to update the attribute value. | `string` | n/a | yes | | [compute\_machine\_type](#input\_compute\_machine\_type) | Identifies the Instance type used by the default worker machine pool e.g. `m5.xlarge`. Use the `rhcs_machine_types` data source to find the possible values. | `string` | `null` | no | @@ -109,9 +111,11 @@ No modules. | [properties](#input\_properties) | User defined properties. | `map(string)` | `null` | no | | [replicas](#input\_replicas) | Number of worker nodes to provision. This attribute is applicable solely when autoscaling is disabled. Single zone clusters need at least 2 nodes, multizone clusters need at least 3 nodes. Hosted clusters require that the number of worker nodes be a multiple of the number of private subnets. (default: 2) | `number` | `null` | no | | [service\_cidr](#input\_service\_cidr) | Block of IP addresses for services, for example "172.30.0.0/16". | `string` | `null` | no | +| [shared\_vpc](#input\_shared\_vpc) | Shared VPC settings |
object({
ingress_private_hosted_zone_id = string
internal_communication_private_hosted_zone_id = string
route53_role_arn = string
vpce_role_arn = string
})
| `null` | no | | [support\_role\_arn](#input\_support\_role\_arn) | The Amazon Resource Name (ARN) associated with the AWS IAM role used by Red Hat SREs to enable access to the cluster account in order to provide support. | `string` | `null` | no | | [tags](#input\_tags) | Apply user defined tags to all cluster resources created in AWS. After the creation of the cluster is completed, it is not possible to update this attribute. | `map(string)` | `null` | no | | [upgrade\_acknowledgements\_for](#input\_upgrade\_acknowledgements\_for) | Indicates acknowledgement of agreements required to upgrade the cluster version between minor versions (e.g. a value of "4.12" indicates acknowledgement of any agreements required to upgrade to OpenShift 4.12.z from 4.11 or before). | `string` | `null` | no | +| [version\_channel\_group](#input\_version\_channel\_group) | Desired channel group of the version [stable, candidate, fast, nightly]. | `string` | `"stable"` | no | | [wait\_for\_create\_complete](#input\_wait\_for\_create\_complete) | Wait until the cluster is either in a ready state or in an error state. The waiter has a timeout of 20 minutes. (default: true) | `bool` | `true` | no | | [wait\_for\_std\_compute\_nodes\_complete](#input\_wait\_for\_std\_compute\_nodes\_complete) | Wait until the cluster standard compute nodes are available. The waiter has a timeout of 60 minutes. (default: true) | `bool` | `true` | no | | [worker\_role\_arn](#input\_worker\_role\_arn) | The Amazon Resource Name (ARN) associated with the AWS IAM role that will be used by the cluster's compute instances. | `string` | `null` | no | diff --git a/modules/rosa-cluster-hcp/main.tf b/modules/rosa-cluster-hcp/main.tf index 1416d4d..ee0e592 100644 --- a/modules/rosa-cluster-hcp/main.tf +++ b/modules/rosa-cluster-hcp/main.tf @@ -22,7 +22,7 @@ locals { operator_role_prefix = var.operator_role_prefix, oidc_config_id = var.oidc_config_id } - aws_account_arn = var.aws_account_arn == null ? data.aws_caller_identity.current[0].arn : var.aws_account_arn + aws_account_arn = var.aws_account_arn == null ? data.aws_caller_identity.current[0].arn : var.aws_account_arn create_admin_user = var.create_admin_user admin_credentials = var.admin_credentials_username == null && var.admin_credentials_password == null ? ( null @@ -34,6 +34,7 @@ locals { resource "rhcs_cluster_rosa_hcp" "rosa_hcp_cluster" { name = var.cluster_name version = var.openshift_version + channel_group = var.version_channel_group upgrade_acknowledgements_for = var.upgrade_acknowledgements_for private = var.private properties = merge( @@ -80,9 +81,12 @@ resource "rhcs_cluster_rosa_hcp" "rosa_hcp_cluster" { ) : ( null ) - etcd_encryption = var.etcd_encryption - etcd_kms_key_arn = var.etcd_kms_key_arn - kms_key_arn = var.kms_key_arn + etcd_encryption = var.etcd_encryption + etcd_kms_key_arn = var.etcd_kms_key_arn + kms_key_arn = var.kms_key_arn + shared_vpc = var.shared_vpc + base_dns_domain = var.base_dns_domain + aws_additional_allowed_principals = var.aws_additional_allowed_principals wait_for_create_complete = var.wait_for_create_complete wait_for_std_compute_nodes_complete = var.wait_for_std_compute_nodes_complete @@ -121,7 +125,7 @@ resource "rhcs_cluster_rosa_hcp" "rosa_hcp_cluster" { } resource "rhcs_hcp_cluster_autoscaler" "cluster_autoscaler" { - count = var.cluster_autoscaler_enabled == true ? 1 : 0 + count = var.cluster_autoscaler_enabled ? 1 : 0 cluster = rhcs_cluster_rosa_hcp.rosa_hcp_cluster.id max_pod_grace_period = var.autoscaler_max_pod_grace_period @@ -134,7 +138,8 @@ resource "rhcs_hcp_cluster_autoscaler" "cluster_autoscaler" { } resource "rhcs_hcp_default_ingress" "default_ingress" { - cluster = rhcs_cluster_rosa_hcp.rosa_hcp_cluster.id + count = rhcs_cluster_rosa_hcp.rosa_hcp_cluster.wait_for_create_complete ? 1 : 0 + cluster = rhcs_cluster_rosa_hcp.rosa_hcp_cluster.id listening_method = var.default_ingress_listening_method != "" ? ( var.default_ingress_listening_method) : ( var.private ? "internal" : "external" diff --git a/modules/rosa-cluster-hcp/variables.tf b/modules/rosa-cluster-hcp/variables.tf index fe31928..5e4a6b1 100644 --- a/modules/rosa-cluster-hcp/variables.tf +++ b/modules/rosa-cluster-hcp/variables.tf @@ -20,6 +20,12 @@ variable "openshift_version" { description = "Desired version of OpenShift for the cluster, for example '4.1.0'. If version is greater than the currently running version, an upgrade will be scheduled." } +variable "version_channel_group" { + type = string + default = "stable" + description = "Desired channel group of the version [stable, candidate, fast, nightly]." +} + variable "aws_account_id" { type = string default = null @@ -145,6 +151,29 @@ variable "ec2_metadata_http_tokens" { description = "Should cluster nodes use both v1 and v2 endpoints or just v2 endpoint of EC2 Instance Metadata Service (IMDS). Available since OpenShift 4.11.0." } +variable "base_dns_domain" { + type = string + default = null + description = "Base DNS domain name previously reserved, e.g. '1vo8.p3.openshiftapps.com'." +} + +variable "shared_vpc" { + type = object({ + ingress_private_hosted_zone_id = string + internal_communication_private_hosted_zone_id = string + route53_role_arn = string + vpce_role_arn = string + }) + default = null + description = "Shared VPC settings" +} + +variable "aws_additional_allowed_principals" { + type = list(string) + default = null + description = "The additional allowed principals to use when installing the cluster." +} + ############################################################## # Proxy variables ############################################################## diff --git a/modules/rosa-cluster-hcp/versions.tf b/modules/rosa-cluster-hcp/versions.tf index f949eac..72781a5 100644 --- a/modules/rosa-cluster-hcp/versions.tf +++ b/modules/rosa-cluster-hcp/versions.tf @@ -7,7 +7,7 @@ terraform { version = ">= 5.38.0" } rhcs = { - version = ">= 1.6.2" + version = ">= 1.6.8" source = "terraform-redhat/rhcs" } } diff --git a/modules/shared-vpc-resources/README.md b/modules/shared-vpc-resources/README.md new file mode 100644 index 0000000..bbd7c03 --- /dev/null +++ b/modules/shared-vpc-resources/README.md @@ -0,0 +1,78 @@ +# shared-vpc-resources + +## Introduction + +This sub-module enables the creation of all essential AWS resources within the shared VPC account to support the shared VPC infrastructure. It encompasses the provisioning of IAM resources to facilitate sharing between accounts, ensuring seamless collaboration and resource access. Additionally, the module handles the configuration of two Route 53 hosted zones, enabling external access into the VPC for enhanced connectivity and service accessibility. + +The operator roles and installer account role must exist so that it is possible to create the trust policy. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | +| [time](#requirement\_time) | >= 0.9 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | +| [http](#provider\_http) | n/a | +| [time](#provider\_time) | >= 0.9 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.route53_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.vpce_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.route53_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.vpce_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.route_53_role_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.vpce_role_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_ram_principal_association.shared_vpc_resource_share](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ram_principal_association) | resource | +| [aws_ram_resource_association.shared_vpc_resource_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ram_resource_association) | resource | +| [aws_ram_resource_share.shared_vpc_resource_share](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ram_resource_share) | resource | +| [aws_route53_zone.hcp_internal_communication_hosted_zone](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource | +| [aws_route53_zone.ingress_private_hosted_zone](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource | +| [time_sleep.shared_resources_propagation](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | +| [http_http.route53_perms](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | data source | +| [http_http.vpce_perms](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [account\_roles\_prefix](#input\_account\_roles\_prefix) | Prefix used to compute installer account role | `string` | n/a | yes | +| [cluster\_name](#input\_cluster\_name) | The cluster's name for which shared resources are created. It is used for the hosted zone domain. | `string` | n/a | yes | +| [ingress\_private\_hosted\_zone\_base\_domain](#input\_ingress\_private\_hosted\_zone\_base\_domain) | The base domain that must be used for hosted zone creation. | `string` | n/a | yes | +| [name\_prefix](#input\_name\_prefix) | The prefix applied to all AWS creations. | `string` | n/a | yes | +| [operator\_roles\_prefix](#input\_operator\_roles\_prefix) | Prefix used to compute ingress and control plane operator roles | `string` | n/a | yes | +| [subnets](#input\_subnets) | The list of the subnets that must be shared between the accounts. | `list(string)` | n/a | yes | +| [target\_aws\_account](#input\_target\_aws\_account) | The AWS account number where the cluster is created. | `string` | n/a | yes | +| [vpc\_id](#input\_vpc\_id) | The Shared VPC ID. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [hcp\_internal\_communication\_private\_hosted\_zone\_arn](#output\_hcp\_internal\_communication\_private\_hosted\_zone\_arn) | HCP Internal Communication Private Hosted Zone ARN | +| [hcp\_internal\_communication\_private\_hosted\_zone\_id](#output\_hcp\_internal\_communication\_private\_hosted\_zone\_id) | HCP Internal Communication Private Hosted Zone ID | +| [ingress\_private\_hosted\_zone\_arn](#output\_ingress\_private\_hosted\_zone\_arn) | Ingress Private Hosted Zone ARN | +| [ingress\_private\_hosted\_zone\_id](#output\_ingress\_private\_hosted\_zone\_id) | Ingress Private Hosted Zone ID | +| [route53\_role\_arn](#output\_route53\_role\_arn) | Route53 Role ARN | +| [route53\_role\_name](#output\_route53\_role\_name) | Route53 Role name | +| [shared\_subnets](#output\_shared\_subnets) | The Amazon Resource Names (ARN) of the resource share | +| [vpce\_role\_arn](#output\_vpce\_role\_arn) | VPCE Role ARN | +| [vpce\_role\_name](#output\_vpce\_role\_name) | VPCE Role name | + \ No newline at end of file diff --git a/modules/shared-vpc-resources/hosted-zones/main.tf b/modules/shared-vpc-resources/hosted-zones/main.tf new file mode 100644 index 0000000..d5d168a --- /dev/null +++ b/modules/shared-vpc-resources/hosted-zones/main.tf @@ -0,0 +1,35 @@ +#### Ingress hosted zone +resource "aws_route53_zone" "ingress_private_hosted_zone" { + name = "rosa.${var.cluster_name}.${var.ingress_private_hosted_zone_base_domain}" + + vpc { + vpc_id = var.vpc_id + } + lifecycle { + ignore_changes = [tags] + } +} + +#### HCP Internal Communication hosted zone +resource "aws_route53_zone" "hcp_internal_communication_hosted_zone" { + name = "${var.cluster_name}.hypershift.local" + + vpc { + vpc_id = var.vpc_id + } + lifecycle { + ignore_changes = [tags] + } +} + +resource "aws_route53_vpc_association_authorization" "ingress_hz_association_auth" { + count = var.create_association_authorization ? 1 : 0 + vpc_id = var.secondary_vpc_id + zone_id = aws_route53_zone.ingress_private_hosted_zone.id +} + +resource "aws_route53_vpc_association_authorization" "hcp_internal_comm_hz_association_auth" { + count = var.create_association_authorization ? 1 : 0 + vpc_id = var.secondary_vpc_id + zone_id = aws_route53_zone.hcp_internal_communication_hosted_zone.id +} diff --git a/modules/shared-vpc-resources/hosted-zones/outputs.tf b/modules/shared-vpc-resources/hosted-zones/outputs.tf new file mode 100644 index 0000000..41e2925 --- /dev/null +++ b/modules/shared-vpc-resources/hosted-zones/outputs.tf @@ -0,0 +1,19 @@ +output "ingress_private_hosted_zone_arn" { + description = "Ingress Private Hosted Zone ARN" + value = aws_route53_zone.ingress_private_hosted_zone.arn +} + +output "ingress_private_hosted_zone_id" { + description = "Ingress Private Hosted Zone ID" + value = var.create_association_authorization ? aws_route53_vpc_association_authorization.ingress_hz_association_auth[0].zone_id : aws_route53_zone.ingress_private_hosted_zone.id +} + +output "hcp_internal_communication_private_hosted_zone_arn" { + description = "HCP Internal Communication Private Hosted Zone ARN" + value = aws_route53_zone.hcp_internal_communication_hosted_zone.arn +} + +output "hcp_internal_communication_private_hosted_zone_id" { + description = "HCP Internal Communication Private Hosted Zone ID" + value = var.create_association_authorization ? aws_route53_vpc_association_authorization.hcp_internal_comm_hz_association_auth[0].zone_id : aws_route53_zone.hcp_internal_communication_hosted_zone.id +} diff --git a/modules/shared-vpc-resources/hosted-zones/variables.tf b/modules/shared-vpc-resources/hosted-zones/variables.tf new file mode 100644 index 0000000..ebd7f96 --- /dev/null +++ b/modules/shared-vpc-resources/hosted-zones/variables.tf @@ -0,0 +1,26 @@ +variable "cluster_name" { + type = string + description = "The cluster's name for which shared resources are created. It is used for the hosted zone domain." +} + +variable "ingress_private_hosted_zone_base_domain" { + type = string + description = "The base domain that must be used for hosted zone creation." +} + +variable "vpc_id" { + type = string + description = "The main VPC ID." +} + +variable "secondary_vpc_id" { + type = string + default = null + description = "The secondary VPC ID. To be used when association authorization is required" +} + +variable "create_association_authorization" { + type = bool + default = false + description = "Indicates need to create association authorization" +} diff --git a/modules/shared-vpc-resources/hosted-zones/versions.tf b/modules/shared-vpc-resources/hosted-zones/versions.tf new file mode 100644 index 0000000..ce68e92 --- /dev/null +++ b/modules/shared-vpc-resources/hosted-zones/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + } +} \ No newline at end of file diff --git a/modules/shared-vpc-resources/main.tf b/modules/shared-vpc-resources/main.tf new file mode 100644 index 0000000..b683171 --- /dev/null +++ b/modules/shared-vpc-resources/main.tf @@ -0,0 +1,67 @@ +locals { + account_roles_prefix = var.account_roles_prefix + installer_role_name = substr("${local.account_roles_prefix}-HCP-ROSA-Installer-Role", 0, 64) + installer_role_arn = "arn:aws:iam::${var.target_aws_account}:role/${local.installer_role_name}" + operator_roles_prefix = var.operator_roles_prefix + ingress_role_name = substr("${local.operator_roles_prefix}-openshift-ingress-operator-cloud-credentials", 0, 64) + ingress_role_arn = "arn:aws:iam::${var.target_aws_account}:role/${local.ingress_role_name}" + control_plane_role_name = substr("${local.operator_roles_prefix}-kube-system-control-plane-operator", 0, 64) + control_plane_role_arn = "arn:aws:iam::${var.target_aws_account}:role/${local.control_plane_role_name}" +} + +### Route53 role +module "route53-role" { + source = "./route53-role" + name_prefix = var.name_prefix + installer_role_arn = local.installer_role_arn + ingress_role_arn = local.ingress_role_arn + control_plane_role_arn = local.control_plane_role_arn + permission_boundary = var.route53_permission_boundary +} + +### VPCE role +module "vpce-role" { + source = "./vpce-role" + name_prefix = var.name_prefix + installer_role_arn = local.installer_role_arn + control_plane_role_arn = local.control_plane_role_arn + permission_boundary = var.vpce_permission_boundary +} + +### Subnets share +module "subnets-share" { + source = "./subnets-share" + name_prefix = var.name_prefix + subnets = var.subnets + target_aws_account = var.target_aws_account +} + +### Hosted zones +module "hosted-zones" { + source = "./hosted-zones" + cluster_name = var.cluster_name + ingress_private_hosted_zone_base_domain = var.ingress_private_hosted_zone_base_domain + vpc_id = var.vpc_id +} + +# This resource is utilized to establish dependencies on all resources. +# Some resources, such as aws_ram_principle_associasion, may not have their output utilized, but they are crucial for the cluster. +# Hence, the creation of these resources must be finalized prior to the initiation of cluster creation, and they should not be removed before the cluster is destroyed. +resource "time_sleep" "shared_resources_propagation" { + destroy_duration = "20s" + create_duration = "20s" + + triggers = { + route53_role_name = module.route53-role.role_name + route53_role_arn = module.route53-role.role_arn + route53_policy_arn = module.route53-role.policy_arn + vpce_role_name = module.vpce-role.role_name + vpce_role_arn = module.vpce-role.role_arn + vpce_policy_arn = module.vpce-role.policy_arn + ingress_private_hosted_zone_id = module.hosted-zones.ingress_private_hosted_zone_id + hcp_internal_communication_private_hosted_zone_id = module.hosted-zones.hcp_internal_communication_private_hosted_zone_id + ingress_private_hosted_zone_arn = module.hosted-zones.ingress_private_hosted_zone_arn + hcp_internal_communication_private_hosted_zone_arn = module.hosted-zones.hcp_internal_communication_private_hosted_zone_arn + resource_share_arn = module.subnets-share.resource_share_arn + } +} diff --git a/modules/shared-vpc-resources/outputs.tf b/modules/shared-vpc-resources/outputs.tf new file mode 100644 index 0000000..9e85cf7 --- /dev/null +++ b/modules/shared-vpc-resources/outputs.tf @@ -0,0 +1,44 @@ +output "route53_role_name" { + description = "Route53 Role name" + value = time_sleep.shared_resources_propagation.triggers["route53_role_name"] +} + +output "route53_role_arn" { + description = "Route53 Role ARN" + value = time_sleep.shared_resources_propagation.triggers["route53_role_arn"] +} + +output "vpce_role_name" { + description = "VPCE Role name" + value = time_sleep.shared_resources_propagation.triggers["vpce_role_name"] +} + +output "vpce_role_arn" { + description = "VPCE Role ARN" + value = time_sleep.shared_resources_propagation.triggers["vpce_role_arn"] +} + +output "ingress_private_hosted_zone_arn" { + description = "Ingress Private Hosted Zone ARN" + value = time_sleep.shared_resources_propagation.triggers["ingress_private_hosted_zone_arn"] +} + +output "hcp_internal_communication_private_hosted_zone_arn" { + description = "HCP Internal Communication Private Hosted Zone ARN" + value = time_sleep.shared_resources_propagation.triggers["hcp_internal_communication_private_hosted_zone_arn"] +} + +output "ingress_private_hosted_zone_id" { + description = "Ingress Private Hosted Zone ID" + value = time_sleep.shared_resources_propagation.triggers["ingress_private_hosted_zone_id"] +} + +output "hcp_internal_communication_private_hosted_zone_id" { + description = "HCP Internal Communication Private Hosted Zone ID" + value = time_sleep.shared_resources_propagation.triggers["hcp_internal_communication_private_hosted_zone_id"] +} + +output "shared_subnets" { + description = "The Amazon Resource Names (ARN) of the resource share" + value = module.subnets-share.shared_subnets +} diff --git a/modules/shared-vpc-resources/route53-role/main.tf b/modules/shared-vpc-resources/route53-role/main.tf new file mode 100644 index 0000000..85a0bb0 --- /dev/null +++ b/modules/shared-vpc-resources/route53-role/main.tf @@ -0,0 +1,52 @@ +resource "aws_iam_role" "route53_role" { + name = substr("${var.name_prefix}-shared-route53-role", 0, 64) + permissions_boundary = var.permission_boundary + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "BeAssumableFrom" + Principal = { + AWS = [ + var.installer_role_arn, + var.control_plane_role_arn, + var.ingress_role_arn, + ] + } + } + ] + }) + description = "Role that managed Route 53 and will be assumed from the Target AWS account where the cluster resides" +} + +## Policy to be migrated to aws managed policy +data "http" "route53_perms" { + url = "https://raw.githubusercontent.com/openshift/managed-cluster-config/refs/heads/master/resources/sts/4.17/hypershift/openshift_hcp_shared_vpc_route_53_credentials_policy.json" + + request_headers = { + Accept = "application/json" + } +} + +resource "aws_iam_policy" "route53_policy" { + name = substr("${var.name_prefix}-shared-route53-policy", 0, 64) + policy = data.http.route53_perms.response_body +} + +resource "aws_iam_role_policy_attachment" "route53_role_policy_attachment" { + role = aws_iam_role.route53_role.name + policy_arn = aws_iam_policy.route53_policy.arn +} + +resource "time_sleep" "route53_role_propagation" { + destroy_duration = "20s" + create_duration = "20s" + + triggers = { + role_name = aws_iam_role_policy_attachment.route53_role_policy_attachment.role + role_arn = aws_iam_role.route53_role.arn + policy_arn = aws_iam_role_policy_attachment.route53_role_policy_attachment.policy_arn + } +} diff --git a/modules/shared-vpc-resources/route53-role/output.tf b/modules/shared-vpc-resources/route53-role/output.tf new file mode 100644 index 0000000..b31ec4f --- /dev/null +++ b/modules/shared-vpc-resources/route53-role/output.tf @@ -0,0 +1,14 @@ +output "role_name" { + description = "Route53 Role name" + value = time_sleep.route53_role_propagation.triggers["role_name"] +} + +output "role_arn" { + description = "Route53 Role ARN" + value = time_sleep.route53_role_propagation.triggers["role_arn"] +} + +output "policy_arn" { + description = "Route53 Policy ARN" + value = time_sleep.route53_role_propagation.triggers["policy_arn"] +} diff --git a/modules/shared-vpc-resources/route53-role/variables.tf b/modules/shared-vpc-resources/route53-role/variables.tf new file mode 100644 index 0000000..660624c --- /dev/null +++ b/modules/shared-vpc-resources/route53-role/variables.tf @@ -0,0 +1,25 @@ +variable "name_prefix" { + type = string + description = "The prefix applied to all AWS creations." +} + +variable "installer_role_arn" { + type = string + description = "The installer account role arn." +} + +variable "ingress_role_arn" { + type = string + description = "The ingress operator role arn." +} + +variable "control_plane_role_arn" { + type = string + description = "The control plane role arn." +} + +variable "permission_boundary" { + type = string + default = null + description = "Permission boundary arn" +} diff --git a/modules/shared-vpc-resources/route53-role/versions.tf b/modules/shared-vpc-resources/route53-role/versions.tf new file mode 100644 index 0000000..9d59d7a --- /dev/null +++ b/modules/shared-vpc-resources/route53-role/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + time = { + source = "hashicorp/time" + version = ">= 0.9" + } + } +} \ No newline at end of file diff --git a/modules/shared-vpc-resources/subnets-share/main.tf b/modules/shared-vpc-resources/subnets-share/main.tf new file mode 100644 index 0000000..0486236 --- /dev/null +++ b/modules/shared-vpc-resources/subnets-share/main.tf @@ -0,0 +1,26 @@ +locals { + resource_arn_prefix = "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:subnet/" +} + +data "aws_caller_identity" "current" {} + +data "aws_region" "current" {} + +data "aws_partition" "current" {} + +resource "aws_ram_resource_share" "shared_vpc_resource_share" { + name = "${var.name_prefix}-shared-vpc-resource-share" + allow_external_principals = true +} + +resource "aws_ram_principal_association" "shared_vpc_resource_share" { + principal = var.target_aws_account + resource_share_arn = aws_ram_resource_share.shared_vpc_resource_share.arn +} + +resource "aws_ram_resource_association" "shared_vpc_resource_association" { + count = length(var.subnets) + + resource_arn = "${local.resource_arn_prefix}${var.subnets[count.index]}" + resource_share_arn = aws_ram_principal_association.shared_vpc_resource_share.resource_share_arn +} diff --git a/modules/shared-vpc-resources/subnets-share/output.tf b/modules/shared-vpc-resources/subnets-share/output.tf new file mode 100644 index 0000000..ea620e5 --- /dev/null +++ b/modules/shared-vpc-resources/subnets-share/output.tf @@ -0,0 +1,9 @@ +output "shared_subnets" { + description = "The Amazon Resource Names (ARN) of the resource share" + value = [for resource_arn in aws_ram_resource_association.shared_vpc_resource_association[*].resource_arn : trimprefix(resource_arn, local.resource_arn_prefix)] +} + +output "resource_share_arn" { + description = "Resource Share ARN" + value = aws_ram_principal_association.shared_vpc_resource_share.resource_share_arn +} diff --git a/modules/shared-vpc-resources/subnets-share/variables.tf b/modules/shared-vpc-resources/subnets-share/variables.tf new file mode 100644 index 0000000..ddb752f --- /dev/null +++ b/modules/shared-vpc-resources/subnets-share/variables.tf @@ -0,0 +1,14 @@ +variable "name_prefix" { + type = string + description = "The prefix applied to all AWS creations." +} + +variable "target_aws_account" { + type = string + description = "The AWS account number where the cluster is created." +} + +variable "subnets" { + type = list(string) + description = "The list of the subnets that must be shared between the accounts." +} \ No newline at end of file diff --git a/modules/shared-vpc-resources/subnets-share/versions.tf b/modules/shared-vpc-resources/subnets-share/versions.tf new file mode 100644 index 0000000..ce68e92 --- /dev/null +++ b/modules/shared-vpc-resources/subnets-share/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + } +} \ No newline at end of file diff --git a/modules/shared-vpc-resources/variables.tf b/modules/shared-vpc-resources/variables.tf new file mode 100644 index 0000000..4d740d4 --- /dev/null +++ b/modules/shared-vpc-resources/variables.tf @@ -0,0 +1,51 @@ +variable "cluster_name" { + type = string + description = "The cluster's name for which shared resources are created. It is used for the hosted zone domain." +} + +variable "name_prefix" { + type = string + description = "The prefix applied to all AWS creations." +} + +variable "target_aws_account" { + type = string + description = "The AWS account number where the cluster is created." +} + +variable "operator_roles_prefix" { + type = string + description = "Prefix used to compute ingress and control plane operator roles" +} + +variable "account_roles_prefix" { + type = string + description = "Prefix used to compute installer account role" +} + +variable "subnets" { + type = list(string) + description = "The list of the subnets that must be shared between the accounts." +} + +variable "ingress_private_hosted_zone_base_domain" { + type = string + description = "The base domain that must be used for hosted zone creation." +} + +variable "vpc_id" { + type = string + description = "The Shared VPC ID." +} + +variable "route53_permission_boundary" { + type = string + default = null + description = "Route53 role permission boundary arn" +} + +variable "vpce_permission_boundary" { + type = string + default = null + description = "VPCE role permission boundary arn" +} diff --git a/modules/shared-vpc-resources/versions.tf b/modules/shared-vpc-resources/versions.tf new file mode 100644 index 0000000..9d59d7a --- /dev/null +++ b/modules/shared-vpc-resources/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + time = { + source = "hashicorp/time" + version = ">= 0.9" + } + } +} \ No newline at end of file diff --git a/modules/shared-vpc-resources/vpce-role/main.tf b/modules/shared-vpc-resources/vpce-role/main.tf new file mode 100644 index 0000000..e687183 --- /dev/null +++ b/modules/shared-vpc-resources/vpce-role/main.tf @@ -0,0 +1,52 @@ +resource "aws_iam_role" "vpce_role" { + name = substr("${var.name_prefix}-shared-vpce-role", 0, 64) + permissions_boundary = var.permission_boundary + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "BeAssumableFrom" + Principal = { + AWS = [ + var.installer_role_arn, + var.control_plane_role_arn + ] + } + } + ] + }) + description = "Role that manages VPC Endpoint and will be assumed from the Target AWS account where the cluster resides" +} + +## Policy to be migrated to aws managed policy +data "http" "vpce_perms" { + url = "https://raw.githubusercontent.com/openshift/managed-cluster-config/refs/heads/master/resources/sts/4.17/hypershift/openshift_hcp_shared_vpc_vpc_endpoint_credentials_policy.json" + + request_headers = { + Accept = "application/json" + } +} + +resource "aws_iam_policy" "vpce_policy" { + name = substr("${var.name_prefix}-shared-vpce-policy", 0, 64) + policy = data.http.vpce_perms.response_body +} + +resource "aws_iam_role_policy_attachment" "vpce_role_policy_attachment" { + role = aws_iam_role.vpce_role.name + policy_arn = aws_iam_policy.vpce_policy.arn +} + + +resource "time_sleep" "vpce_role_propagation" { + destroy_duration = "20s" + create_duration = "20s" + + triggers = { + role_name = aws_iam_role_policy_attachment.vpce_role_policy_attachment.role + role_arn = aws_iam_role.vpce_role.arn + policy_arn = aws_iam_role_policy_attachment.vpce_role_policy_attachment.policy_arn + } +} diff --git a/modules/shared-vpc-resources/vpce-role/output.tf b/modules/shared-vpc-resources/vpce-role/output.tf new file mode 100644 index 0000000..b349059 --- /dev/null +++ b/modules/shared-vpc-resources/vpce-role/output.tf @@ -0,0 +1,14 @@ +output "role_name" { + description = "VPCE Role name" + value = time_sleep.vpce_role_propagation.triggers["role_name"] +} + +output "role_arn" { + description = "VPCE Role ARN" + value = time_sleep.vpce_role_propagation.triggers["role_arn"] +} + +output "policy_arn" { + description = "VPCE Policy ARN" + value = time_sleep.vpce_role_propagation.triggers["policy_arn"] +} diff --git a/modules/shared-vpc-resources/vpce-role/variables.tf b/modules/shared-vpc-resources/vpce-role/variables.tf new file mode 100644 index 0000000..00574f5 --- /dev/null +++ b/modules/shared-vpc-resources/vpce-role/variables.tf @@ -0,0 +1,20 @@ +variable "name_prefix" { + type = string + description = "The prefix applied to all AWS creations." +} + +variable "installer_role_arn" { + type = string + description = "The installer account role arn." +} + +variable "control_plane_role_arn" { + type = string + description = "The control plane role arn." +} + +variable "permission_boundary" { + type = string + default = null + description = "Permission boundary arn" +} diff --git a/modules/shared-vpc-resources/vpce-role/versions.tf b/modules/shared-vpc-resources/vpce-role/versions.tf new file mode 100644 index 0000000..0613e43 --- /dev/null +++ b/modules/shared-vpc-resources/vpce-role/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + time = { + source = "hashicorp/time" + version = ">= 0.9" + } + } +} diff --git a/variables.tf b/variables.tf index a164692..1c9ff70 100644 --- a/variables.tf +++ b/variables.tf @@ -14,6 +14,12 @@ variable "openshift_version" { description = "Desired version of OpenShift for the cluster, for example '4.1.0'. If version is greater than the currently running version, an upgrade will be scheduled." } +variable "version_channel_group" { + type = string + default = "stable" + description = "Desired channel group of the version [stable, candidate, fast, nightly]." +} + variable "oidc_config_id" { type = string default = null @@ -94,6 +100,25 @@ variable "ec2_metadata_http_tokens" { description = "Should cluster nodes use both v1 and v2 endpoints or just v2 endpoint of EC2 Instance Metadata Service (IMDS). Available since OpenShift 4.11.0." } + +variable "create_dns_domain_reservation" { + description = "Creates reserves a dns domain domain for the cluster. This value will be created by the install step if not pre created via this configuration." + type = bool + default = false +} + +variable "base_dns_domain" { + type = string + default = null + description = "Base DNS domain name previously reserved, e.g. '1vo8.p3.openshiftapps.com'." +} + +variable "aws_additional_allowed_principals" { + type = list(string) + default = null + description = "The additional allowed principals to use when installing the cluster." +} + ############################################################## # Proxy variables ############################################################## @@ -322,7 +347,7 @@ variable "oidc_endpoint_url" { } variable "machine_pools" { - type = map(any) + type = map(any) default = {} description = "Provides a generic approach to add multiple machine pools after the creation of the cluster. This variable allows users to specify configurations for multiple machine pools in a flexible and customizable manner, facilitating the management of resources post-cluster deployment. For additional details regarding the variables utilized, refer to the [machine-pool sub-module](./modules/machine-pool). For non-primitive variables (such as maps, lists, and objects), supply the JSON-encoded string." } @@ -336,7 +361,7 @@ variable "identity_providers" { variable "kubelet_configs" { type = map(any) default = {} - description = "Provides a generic approach to add multiple kubelet configs after the creation of the cluster. This variable allows users to specify configurations for multiple kubelet configs in a flexible and customizable manner, facilitating the management of resources post-cluster deployment. For additional details regarding the variables utilized, refer to the [idp sub-module](./modules/kubelet-configs). For non-primitive variables (such as maps, lists, and objects), supply the JSON-encoded string." + description = "Provides a generic approach to add multiple kubelet configs after the creation of the cluster. This variable allows users to specify configurations for multiple kubelet configs in a flexible and customizable manner, facilitating the management of resources post-cluster deployment. For additional details regarding the variables utilized, refer to the [idp sub-module](./modules/kubelet-configs). For non-primitive variables (such as maps, lists, and objects), supply the JSON-encoded string." } variable "ignore_machine_pools_deletion_error" {