From 6bc81476645098fcee292ecf714b7e454f373fcc Mon Sep 17 00:00:00 2001 From: lufreita Date: Fri, 29 May 2026 14:34:59 -0300 Subject: [PATCH] ROSAENG-6808 | feat: add OCM role submodule with required ROSA CLI tags Add modules/ocm-role/ that creates the AWS IAM OCM role with the tags the ROSA CLI applies (red-hat-managed, rosa_role_prefix, rosa_role_type, rosa_environment, and conditional rosa_admin_role). Permission policies are read from the rhcs_hcp_policies data source using the policy IDs confirmed in terraform-provider-rhcs PR #1156. The submodule outputs role_arn for composition with the provider's rhcs_rosa_ocm_role_link resource. An example and tests are included. Signed-off-by: lufreita --- examples/ocm-role/README.md | 42 +++ examples/ocm-role/main.tf | 12 + examples/ocm-role/outputs.tf | 17 + examples/ocm-role/variables.tf | 14 + examples/ocm-role/versions.tf | 17 + modules/ocm-role/README.md | 76 +++++ modules/ocm-role/main.tf | 113 +++++++ modules/ocm-role/outputs.tf | 17 + modules/ocm-role/tests/ocm_role.tftest.hcl | 348 +++++++++++++++++++++ modules/ocm-role/variables.tf | 57 ++++ modules/ocm-role/versions.tf | 17 + 11 files changed, 730 insertions(+) create mode 100644 examples/ocm-role/README.md create mode 100644 examples/ocm-role/main.tf create mode 100644 examples/ocm-role/outputs.tf create mode 100644 examples/ocm-role/variables.tf create mode 100644 examples/ocm-role/versions.tf create mode 100644 modules/ocm-role/README.md create mode 100644 modules/ocm-role/main.tf create mode 100644 modules/ocm-role/outputs.tf create mode 100644 modules/ocm-role/tests/ocm_role.tftest.hcl create mode 100644 modules/ocm-role/variables.tf create mode 100644 modules/ocm-role/versions.tf diff --git a/examples/ocm-role/README.md b/examples/ocm-role/README.md new file mode 100644 index 0000000..b277e14 --- /dev/null +++ b/examples/ocm-role/README.md @@ -0,0 +1,42 @@ +# ocm-role example + +Creates and links an OCM IAM role with the required ROSA CLI-parity tags using the `ocm-role` module. + + +## Requirements + +| Name | Version | +| ---- | ------- | +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 6.0 | +| [rhcs](#requirement\_rhcs) | >= 1.7.7 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +| ---- | ------ | ------- | +| [ocm\_role](#module\_ocm\_role) | ../../modules/ocm-role | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +| ---- | ----------- | ---- | ------- | :------: | +| [ocm\_role\_prefix](#input\_ocm\_role\_prefix) | User-defined prefix for the OCM IAM role name. | `string` | `"ManagedOpenShift"` | no | +| [profile](#input\_profile) | Profile of the OCM role to create. Allowed values are `standard`, `admin`, and `no-console`. | `string` | `"standard"` | no | + +## Outputs + +| Name | Description | +| ---- | ----------- | +| [role\_arn](#output\_role\_arn) | The ARN of the created OCM IAM role. | +| [role\_link\_id](#output\_role\_link\_id) | The identifier of the OCM-side role link. | +| [role\_name](#output\_role\_name) | The name of the created OCM IAM role. | + diff --git a/examples/ocm-role/main.tf b/examples/ocm-role/main.tf new file mode 100644 index 0000000..dc1a03c --- /dev/null +++ b/examples/ocm-role/main.tf @@ -0,0 +1,12 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +############################ +# OCM Role +############################ +module "ocm_role" { + source = "../../modules/ocm-role" + + ocm_role_prefix = var.ocm_role_prefix + profile = var.profile +} diff --git a/examples/ocm-role/outputs.tf b/examples/ocm-role/outputs.tf new file mode 100644 index 0000000..41cd39e --- /dev/null +++ b/examples/ocm-role/outputs.tf @@ -0,0 +1,17 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +output "role_arn" { + value = module.ocm_role.role_arn + description = "The ARN of the created OCM IAM role." +} + +output "role_name" { + value = module.ocm_role.role_name + description = "The name of the created OCM IAM role." +} + +output "role_link_id" { + value = module.ocm_role.role_link_id + description = "The identifier of the OCM-side role link." +} diff --git a/examples/ocm-role/variables.tf b/examples/ocm-role/variables.tf new file mode 100644 index 0000000..8e769f9 --- /dev/null +++ b/examples/ocm-role/variables.tf @@ -0,0 +1,14 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +variable "ocm_role_prefix" { + type = string + description = "User-defined prefix for the OCM IAM role name." + default = "ManagedOpenShift" +} + +variable "profile" { + type = string + description = "Profile of the OCM role to create. Allowed values are `standard`, `admin`, and `no-console`." + default = "standard" +} diff --git a/examples/ocm-role/versions.tf b/examples/ocm-role/versions.tf new file mode 100644 index 0000000..3bef79e --- /dev/null +++ b/examples/ocm-role/versions.tf @@ -0,0 +1,17 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0" + } + rhcs = { + source = "terraform-redhat/rhcs" + version = ">= 1.7.7" + } + } +} diff --git a/modules/ocm-role/README.md b/modules/ocm-role/README.md new file mode 100644 index 0000000..5a6339d --- /dev/null +++ b/modules/ocm-role/README.md @@ -0,0 +1,76 @@ +# ocm-role + +## Introduction + +This Terraform sub-module creates the AWS IAM OCM role with the tags required by the ROSA CLI and OCM. The role is used to grant OpenShift Cluster Manager permissions in the customer's AWS account. + +The module creates the IAM role, attaches the appropriate permission policies for the selected profile, applies the required tags, and links the role to the current OCM organization via `rhcs_rosa_ocm_role_link`. + +For more information, see [Understanding OCM role and User role for ROSA](https://access.redhat.com/articles/6961686). + +## Example Usage + +``` +module "ocm_role" { + source = "terraform-redhat/rosa-hcp/rhcs//modules/ocm-role" + version = "1.7.7" + + ocm_role_prefix = "ManagedOpenShift" + profile = "standard" +} +``` + + +## Requirements + +| Name | Version | +| ---- | ------- | +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 6.0 | +| [rhcs](#requirement\_rhcs) | >= 1.7.7 | + +## Providers + +| Name | Version | +| ---- | ------- | +| [aws](#provider\_aws) | >= 6.0 | +| [rhcs](#provider\_rhcs) | >= 1.7.7 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +| ---- | ---- | +| [aws_iam_policy.ocm_admin_permission_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.ocm_no_console_permission_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.standard_permission_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.ocm_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.ocm_admin_permission_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.ocm_no_console_permission_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.standard_permission_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [rhcs_rosa_ocm_role_link.this](https://registry.terraform.io/providers/terraform-redhat/rhcs/latest/docs/resources/rosa_ocm_role_link) | resource | +| [rhcs_hcp_policies.all_policies](https://registry.terraform.io/providers/terraform-redhat/rhcs/latest/docs/data-sources/hcp_policies) | data source | +| [rhcs_info.current](https://registry.terraform.io/providers/terraform-redhat/rhcs/latest/docs/data-sources/info) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +| ---- | ----------- | ---- | ------- | :------: | +| [create\_link](#input\_create\_link) | (Optional) Whether to link the created role to the OCM organization via rhcs\_rosa\_ocm\_role\_link. Set to false when importing an already-linked role. | `bool` | `true` | no | +| [ocm\_role\_prefix](#input\_ocm\_role\_prefix) | User-defined prefix for the OCM IAM role name. The final role name is `-OCM-Role-`. | `string` | n/a | yes | +| [path](#input\_path) | (Optional) The IAM path for the OCM role and its policies. Must begin and end with '/'. | `string` | `"/"` | no | +| [permissions\_boundary](#input\_permissions\_boundary) | (Optional) The ARN of the policy used to set the permissions boundary for the OCM IAM role. | `string` | `""` | no | +| [profile](#input\_profile) | Profile of the OCM role to create. Allowed values are `standard`, `admin`, and `no-console`. | `string` | `"standard"` | no | +| [tags](#input\_tags) | (Optional) Additional AWS resource tags to merge into the OCM role and its policies. | `map(string)` | `null` | no | + +## Outputs + +| Name | Description | +| ---- | ----------- | +| [role\_arn](#output\_role\_arn) | The ARN of the created OCM IAM role. | +| [role\_link\_id](#output\_role\_link\_id) | The identifier of the OCM-side role link created for the IAM role, or null when create\_link is false. | +| [role\_name](#output\_role\_name) | The name of the created OCM IAM role. | + diff --git a/modules/ocm-role/main.tf b/modules/ocm-role/main.tf new file mode 100644 index 0000000..1bc655f --- /dev/null +++ b/modules/ocm-role/main.tf @@ -0,0 +1,113 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +locals { + path = coalesce(var.path, "/") + + role_type = "OCM" + role_suffix = "-${local.role_type}-Role-${data.rhcs_info.current.organization_external_id}" + max_prefix_length = 64 - length(local.role_suffix) + truncated_role_prefix = local.max_prefix_length > 0 ? substr(var.ocm_role_prefix, 0, local.max_prefix_length) : "" + role_name = "${local.truncated_role_prefix}${local.role_suffix}" + max_policy_name_length = 128 + standard_policy_enabled = contains(["standard", "admin"], var.profile) + admin_policy_enabled = var.profile == "admin" + no_console_enabled = var.profile == "no-console" + + ocm_environment = ( + strcontains(data.rhcs_info.current.ocm_api, "api.stage.") ? "staging" : + ( + strcontains(data.rhcs_info.current.ocm_api, "integration") || strcontains(data.rhcs_info.current.ocm_api, ".int.") ? "integration" : "production" + ) + ) + + base_tags = merge(var.tags, { + red-hat-managed = true + rosa_role_prefix = var.ocm_role_prefix + rosa_role_type = local.role_type + rosa_environment = local.ocm_environment + }) + + role_tags = local.admin_policy_enabled ? merge(local.base_tags, { + rosa_admin_role = true + }) : ( + local.no_console_enabled ? merge(local.base_tags, { + rosa_no_console_role = true + }) : local.base_tags + ) +} + +data "rhcs_hcp_policies" "all_policies" {} + +data "rhcs_info" "current" {} + +resource "aws_iam_role" "ocm_role" { + name = local.role_name + permissions_boundary = var.permissions_boundary + path = local.path + assume_role_policy = data.rhcs_hcp_policies.all_policies.ocm_role_policies.sts_ocm_trust_policy + force_detach_policies = true + + tags = local.role_tags +} + +resource "aws_iam_policy" "standard_permission_policy" { + count = local.standard_policy_enabled ? 1 : 0 + + name = substr("${local.role_name}-Policy", 0, local.max_policy_name_length) + path = local.path + policy = data.rhcs_hcp_policies.all_policies.ocm_role_policies.sts_ocm_permission_policy + + tags = local.base_tags +} + +resource "aws_iam_role_policy_attachment" "standard_permission_policy_attachment" { + count = local.standard_policy_enabled ? 1 : 0 + + role = aws_iam_role.ocm_role.name + policy_arn = aws_iam_policy.standard_permission_policy[0].arn +} + +resource "aws_iam_policy" "ocm_admin_permission_policy" { + count = local.admin_policy_enabled ? 1 : 0 + + name = substr("${local.role_name}-Admin-Policy", 0, local.max_policy_name_length) + path = local.path + policy = data.rhcs_hcp_policies.all_policies.ocm_role_policies.sts_ocm_admin_permission_policy + + tags = merge(local.base_tags, { + rosa_admin_role = true + }) +} + +resource "aws_iam_role_policy_attachment" "ocm_admin_permission_policy_attachment" { + count = local.admin_policy_enabled ? 1 : 0 + + role = aws_iam_role.ocm_role.name + policy_arn = aws_iam_policy.ocm_admin_permission_policy[0].arn +} + +resource "aws_iam_policy" "ocm_no_console_permission_policy" { + count = local.no_console_enabled ? 1 : 0 + + name = substr("${local.role_name}-NoConsole-Policy", 0, local.max_policy_name_length) + path = local.path + policy = data.rhcs_hcp_policies.all_policies.ocm_role_policies.sts_ocm_no_console_permission_policy + + tags = merge(local.base_tags, { + rosa_no_console_role = true + }) +} + +resource "aws_iam_role_policy_attachment" "ocm_no_console_permission_policy_attachment" { + count = local.no_console_enabled ? 1 : 0 + + role = aws_iam_role.ocm_role.name + policy_arn = aws_iam_policy.ocm_no_console_permission_policy[0].arn +} + +resource "rhcs_rosa_ocm_role_link" "this" { + count = var.create_link ? 1 : 0 + + role_arn = aws_iam_role.ocm_role.arn +} diff --git a/modules/ocm-role/outputs.tf b/modules/ocm-role/outputs.tf new file mode 100644 index 0000000..dc1af90 --- /dev/null +++ b/modules/ocm-role/outputs.tf @@ -0,0 +1,17 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +output "role_arn" { + value = aws_iam_role.ocm_role.arn + description = "The ARN of the created OCM IAM role." +} + +output "role_name" { + value = aws_iam_role.ocm_role.name + description = "The name of the created OCM IAM role." +} + +output "role_link_id" { + value = try(rhcs_rosa_ocm_role_link.this[0].id, null) + description = "The identifier of the OCM-side role link created for the IAM role, or null when create_link is false." +} diff --git a/modules/ocm-role/tests/ocm_role.tftest.hcl b/modules/ocm-role/tests/ocm_role.tftest.hcl new file mode 100644 index 0000000..44dc066 --- /dev/null +++ b/modules/ocm-role/tests/ocm_role.tftest.hcl @@ -0,0 +1,348 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +mock_provider "aws" { + alias = "default" +} + +mock_provider "rhcs" { + alias = "prod" + override_during = plan + + mock_data "rhcs_info" { + defaults = { + organization_external_id = "orgext123" + ocm_api = "https://api.openshift.com" + ocm_aws_account_id = "000000000000" + } + } + + mock_data "rhcs_hcp_policies" { + defaults = { + ocm_role_policies = { + sts_ocm_trust_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"sts:AssumeRole\",\"Principal\":{\"AWS\":\"arn:aws:iam::000000000000:root\"}}]}" + sts_ocm_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"iam:GetRole\"],\"Resource\":\"*\"}]}" + sts_ocm_admin_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"iam:*\"],\"Resource\":\"*\"}]}" + sts_ocm_no_console_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"iam:GetRole\",\"Resource\":\"*\"}]}" + } + } + } + + mock_resource "rhcs_rosa_ocm_role_link" { + defaults = { + id = "ocm-link-id" + } + } +} + +mock_provider "rhcs" { + alias = "stage" + override_during = plan + + mock_data "rhcs_info" { + defaults = { + organization_external_id = "orgext123" + ocm_api = "https://api.stage.openshift.com" + ocm_aws_account_id = "000000000000" + } + } + + mock_data "rhcs_hcp_policies" { + defaults = { + ocm_role_policies = { + sts_ocm_trust_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"sts:AssumeRole\",\"Principal\":{\"AWS\":\"arn:aws:iam::000000000000:root\"}}]}" + sts_ocm_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"iam:GetRole\"],\"Resource\":\"*\"}]}" + sts_ocm_admin_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"iam:*\"],\"Resource\":\"*\"}]}" + sts_ocm_no_console_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"iam:GetRole\",\"Resource\":\"*\"}]}" + } + } + } + + mock_resource "rhcs_rosa_ocm_role_link" { + defaults = { + id = "ocm-link-id" + } + } +} + +mock_provider "rhcs" { + alias = "int" + override_during = plan + + mock_data "rhcs_info" { + defaults = { + organization_external_id = "orgext123" + ocm_api = "https://api.integration.openshift.com" + ocm_aws_account_id = "000000000000" + } + } + + mock_data "rhcs_hcp_policies" { + defaults = { + ocm_role_policies = { + sts_ocm_trust_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"sts:AssumeRole\",\"Principal\":{\"AWS\":\"arn:aws:iam::000000000000:root\"}}]}" + sts_ocm_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"iam:GetRole\"],\"Resource\":\"*\"}]}" + sts_ocm_admin_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"iam:*\"],\"Resource\":\"*\"}]}" + sts_ocm_no_console_permission_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"iam:GetRole\",\"Resource\":\"*\"}]}" + } + } + } + + mock_resource "rhcs_rosa_ocm_role_link" { + defaults = { + id = "ocm-link-id" + } + } +} + +# Standard OCM role (default profile) -- plans successfully with correct tags, naming, and link. +run "standard_ocm_role_plan" { + command = plan + + providers = { + rhcs = rhcs.prod + aws = aws.default + } + + variables { + ocm_role_prefix = "ManagedOpenShift" + } + + assert { + condition = aws_iam_role.ocm_role.name == "ManagedOpenShift-OCM-Role-orgext123" + error_message = "Expected OCM role name to follow the ROSA CLI naming pattern." + } + + assert { + condition = aws_iam_role.ocm_role.tags["red-hat-managed"] == "true" + error_message = "Expected red-hat-managed tag to be true." + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_role_prefix"] == "ManagedOpenShift" + error_message = "Expected rosa_role_prefix tag to match the prefix variable." + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_role_type"] == "OCM" + error_message = "Expected rosa_role_type tag to be OCM." + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_environment"] == "production" + error_message = "Expected rosa_environment tag to default to production." + } + + assert { + condition = !contains(keys(aws_iam_role.ocm_role.tags), "rosa_admin_role") + error_message = "Standard role must not have rosa_admin_role tag." + } + + assert { + condition = !contains(keys(aws_iam_role.ocm_role.tags), "rosa_no_console_role") + error_message = "Standard role must not have rosa_no_console_role tag." + } + + assert { + condition = length(aws_iam_policy.standard_permission_policy) == 1 + error_message = "Standard role must create the standard permission policy." + } + + assert { + condition = length(aws_iam_policy.ocm_admin_permission_policy) == 0 + error_message = "Standard role must not create the admin permission policy." + } + + assert { + condition = length(aws_iam_policy.ocm_no_console_permission_policy) == 0 + error_message = "Standard role must not create the no-console permission policy." + } + + assert { + condition = rhcs_rosa_ocm_role_link.this[0].id == "ocm-link-id" + error_message = "Expected the module to create the OCM-side link resource." + } +} + +# Admin OCM role -- plans with standard + admin policies. +run "admin_ocm_role_plan" { + command = plan + + providers = { + rhcs = rhcs.prod + aws = aws.default + } + + variables { + ocm_role_prefix = "ManagedOpenShift" + profile = "admin" + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_admin_role"] == "true" + error_message = "Admin role must have rosa_admin_role=true tag." + } + + assert { + condition = !contains(keys(aws_iam_role.ocm_role.tags), "rosa_no_console_role") + error_message = "Admin role must not have rosa_no_console_role tag." + } + + assert { + condition = length(aws_iam_policy.standard_permission_policy) == 1 + error_message = "Admin role must keep the standard permission policy." + } + + assert { + condition = length(aws_iam_policy.ocm_admin_permission_policy) == 1 + error_message = "Admin role must create exactly one admin permission policy." + } + + assert { + condition = length(aws_iam_role_policy_attachment.standard_permission_policy_attachment) == 1 + error_message = "Admin role must attach the standard permission policy." + } + + assert { + condition = length(aws_iam_role_policy_attachment.ocm_admin_permission_policy_attachment) == 1 + error_message = "Admin role must attach the admin permission policy." + } +} + +# No-console OCM role -- plans with no-console tag and policy only. +run "no_console_ocm_role_plan" { + command = plan + + providers = { + rhcs = rhcs.prod + aws = aws.default + } + + variables { + ocm_role_prefix = "ManagedOpenShift" + profile = "no-console" + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_no_console_role"] == "true" + error_message = "No-console role must have rosa_no_console_role=true tag." + } + + assert { + condition = !contains(keys(aws_iam_role.ocm_role.tags), "rosa_admin_role") + error_message = "No-console role must not have rosa_admin_role tag." + } + + assert { + condition = length(aws_iam_policy.standard_permission_policy) == 0 + error_message = "No-console role must not create the standard permission policy." + } + + assert { + condition = length(aws_iam_policy.ocm_admin_permission_policy) == 0 + error_message = "No-console role must not create the admin permission policy." + } + + assert { + condition = length(aws_iam_policy.ocm_no_console_permission_policy) == 1 + error_message = "No-console role must create the no-console permission policy." + } +} + +# Derived environment tag from OCM API. +run "staging_environment_plan" { + command = plan + + providers = { + rhcs = rhcs.stage + aws = aws.default + } + + variables { + ocm_role_prefix = "MyPrefix" + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_environment"] == "staging" + error_message = "Expected rosa_environment tag to be derived as staging." + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_role_prefix"] == "MyPrefix" + error_message = "Expected rosa_role_prefix tag to match the provided prefix." + } +} + +# Derived environment tag from OCM API (integration). +run "integration_environment_plan" { + command = plan + + providers = { + rhcs = rhcs.int + aws = aws.default + } + + variables { + ocm_role_prefix = "MyPrefix" + } + + assert { + condition = aws_iam_role.ocm_role.tags["rosa_environment"] == "integration" + error_message = "Expected rosa_environment tag to be derived as integration." + } +} + +# Invalid profile value fails validation. +run "invalid_profile_fails" { + command = plan + + providers = { + rhcs = rhcs.prod + aws = aws.default + } + + variables { + ocm_role_prefix = "ManagedOpenShift" + profile = "invalid" + } + + expect_failures = [ + var.profile, + ] +} + +# Prefix exceeding 32 characters fails validation. +run "prefix_too_long_fails" { + command = plan + + providers = { + rhcs = rhcs.prod + aws = aws.default + } + + variables { + ocm_role_prefix = "abcdefghijklmnopqrstuvwxyz1234567" + } + + expect_failures = [ + var.ocm_role_prefix, + ] +} + +# Prefix with invalid characters fails validation. +run "prefix_invalid_chars_fails" { + command = plan + + providers = { + rhcs = rhcs.prod + aws = aws.default + } + + variables { + ocm_role_prefix = "invalid prefix!" + } + + expect_failures = [ + var.ocm_role_prefix, + ] +} diff --git a/modules/ocm-role/variables.tf b/modules/ocm-role/variables.tf new file mode 100644 index 0000000..cdbb3ab --- /dev/null +++ b/modules/ocm-role/variables.tf @@ -0,0 +1,57 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +variable "ocm_role_prefix" { + type = string + description = "User-defined prefix for the OCM IAM role name. The final role name is `-OCM-Role-`." + + validation { + condition = can(regex("^[\\w+=,.@-]+$", var.ocm_role_prefix)) + error_message = "ocm_role_prefix must match [\\w+=,.@-]+ (alphanumeric, plus, equals, comma, period, at, hyphen)." + } + + validation { + condition = length(var.ocm_role_prefix) <= 32 + error_message = "ocm_role_prefix must be 32 characters or fewer." + } +} + +variable "profile" { + type = string + description = "Profile of the OCM role to create. Allowed values are `standard`, `admin`, and `no-console`." + default = "standard" + + validation { + condition = contains(["standard", "admin", "no-console"], var.profile) + error_message = "profile must be one of: standard, admin, no-console." + } +} + +variable "path" { + type = string + description = "(Optional) The IAM path for the OCM role and its policies. Must begin and end with '/'." + default = "/" + + validation { + condition = var.path == "/" || can(regex("^/.+/$", var.path)) + error_message = "path must be '/' or a value that begins and ends with '/'." + } +} + +variable "permissions_boundary" { + type = string + description = "(Optional) The ARN of the policy used to set the permissions boundary for the OCM IAM role." + default = "" +} + +variable "tags" { + type = map(string) + description = "(Optional) Additional AWS resource tags to merge into the OCM role and its policies." + default = null +} + +variable "create_link" { + type = bool + description = "(Optional) Whether to link the created role to the OCM organization via rhcs_rosa_ocm_role_link. Set to false when importing an already-linked role." + default = true +} diff --git a/modules/ocm-role/versions.tf b/modules/ocm-role/versions.tf new file mode 100644 index 0000000..3bef79e --- /dev/null +++ b/modules/ocm-role/versions.tf @@ -0,0 +1,17 @@ +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 + +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0" + } + rhcs = { + source = "terraform-redhat/rhcs" + version = ">= 1.7.7" + } + } +}