Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Secrets Manager Service Credentials #372

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ You need the following permissions to run this module.

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.9.0 |
| <a name="requirement_ibm"></a> [ibm](#requirement\_ibm) | >= 1.71.0, <2.0.0 |
| <a name="requirement_time"></a> [time](#requirement\_time) | >= 0.9.1 |

Expand Down
2 changes: 1 addition & 1 deletion examples/basic/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.3.0"
required_version = ">= 1.9.0"
required_providers {
ibm = {
source = "IBM-Cloud/ibm"
Expand Down
2 changes: 1 addition & 1 deletion examples/complete/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.3.0"
required_version = ">= 1.9.0"
required_providers {
# Use latest version of provider in non-basic examples to verify latest version works with module
ibm = {
Expand Down
2 changes: 1 addition & 1 deletion examples/fscloud/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.3.0"
required_version = ">= 1.9.0"
required_providers {
# Use latest version of provider in non-basic examples to verify latest version works with module
ibm = {
Expand Down
24 changes: 24 additions & 0 deletions ibm_catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@
},
{
"key": "service_credential_names"
},
{
"key": "existing_secrets_manager_endpoint_type"
},
{
"key": "existing_secrets_manager_instance_crn"
},
{
"key": "service_credential_secrets"
},
{
"key": "skip_event_streams_secrets_manager_auth_policy"
}
],
"iam_permissions": [
Expand Down Expand Up @@ -336,6 +348,18 @@
},
{
"key": "existing_kms_key_crn"
},
{
"key": "existing_secrets_manager_endpoint_type"
},
{
"key": "existing_secrets_manager_instance_crn"
},
{
"key": "service_credential_secrets"
},
{
"key": "skip_event_streams_secrets_manager_auth_policy"
}
],
"iam_permissions": [
Expand Down
37 changes: 5 additions & 32 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,11 @@
#######################################################################################

locals {
# Validation (approach based on https://github.com/hashicorp/terraform/issues/25609#issuecomment-1057614400)

# tflint-ignore: terraform_unused_declarations
validate_kms_plan = var.plan != "enterprise-3nodes-2tb" && var.kms_key_crn != null ? tobool("KMS encryption is only supported for enterprise plan.") : true
# tflint-ignore: terraform_unused_declarations
validate_metrics = var.plan != "enterprise-3nodes-2tb" && length(var.metrics) > 0 ? tobool("Metrics are only supported for enterprise plan.") : true
# tflint-ignore: terraform_unused_declarations
validate_quotas = var.plan != "enterprise-3nodes-2tb" && length(var.quotas) > 0 ? tobool("Quotas are only supported for enterprise plan.") : true
# tflint-ignore: terraform_unused_declarations
validate_schema_global_rule = var.plan != "enterprise-3nodes-2tb" && var.schema_global_rule != null ? tobool("Schema global rule is only supported for enterprise plan.") : true

# tflint-ignore: terraform_unused_declarations
validate_kms_values = !var.kms_encryption_enabled && var.kms_key_crn != null ? tobool("When passing values for var.kms_key_crn, you must set var.kms_encryption_enabled to true. Otherwise unset them to use default encryption.") : true
# tflint-ignore: terraform_unused_declarations
validate_kms_vars = var.kms_encryption_enabled && var.kms_key_crn == null ? tobool("When setting var.kms_encryption_enabled to true, a value must be passed for var.kms_key_crn and/or var.backup_encryption_key_crn.") : true
# tflint-ignore: terraform_unused_declarations
validate_auth_policy = var.kms_encryption_enabled && var.skip_kms_iam_authorization_policy == false && var.kms_key_crn == null ? tobool("When var.skip_kms_iam_authorization_policy is set to false, and var.kms_encryption_enabled to true, a value must be passed for var.kms_key_crn in order to create the auth policy.") : true
# tflint-ignore: terraform_unused_declarations
validate_throughput_lite_standard = ((var.plan == "lite" || var.plan == "standard") && var.throughput != 150) ? tobool("Throughput value cannot be changed in lite and standard plan. Default value is 150.") : true
# tflint-ignore: terraform_unused_declarations
validate_storage_size_lite_standard = ((var.plan == "lite" || var.plan == "standard") && var.storage_size != 2048) ? tobool("Storage size value cannot be changed in lite and standard plan. Default value is 2048.") : true
# tflint-ignore: terraform_unused_declarations
validate_service_end_points_lite_standard = ((var.plan == "lite" || var.plan == "standard") && var.service_endpoints != "public") ? tobool("Service endpoint cannot be changed in lite and standard plan. Default is public.") : true
# tflint-ignore: terraform_unused_declarations
validate_mirroring_topics = var.mirroring == null && var.mirroring_topic_patterns != null ? tobool("When passing values for var.mirroring_topic_patterns, values must also be passed for var.mirroring.") : true
# tflint-ignore: terraform_unused_declarations
validate_mirroring_config = var.mirroring != null && var.mirroring_topic_patterns == null ? tobool("When passing values for var.mirroring, values must also be passed for var.mirroring_topic_patterns.") : true
parsed_kms_key_crn = var.kms_key_crn != null ? split(":", var.kms_key_crn) : []
kms_service = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[4] : null
kms_scope = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[6] : null
kms_account_id = length(local.parsed_kms_key_crn) > 0 ? split("/", local.kms_scope)[1] : null
kms_key_id = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[9] : null
parsed_kms_key_crn = var.kms_key_crn != null ? split(":", var.kms_key_crn) : []
kms_service = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[4] : null
kms_scope = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[6] : null
kms_account_id = length(local.parsed_kms_key_crn) > 0 ? split("/", local.kms_scope)[1] : null
kms_key_id = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[9] : null
}

# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478
Expand Down
2 changes: 1 addition & 1 deletion modules/fscloud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The default values in this profile were scanned by [IBM Code Risk Analyzer (CRA)

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.9.0 |
| <a name="requirement_ibm"></a> [ibm](#requirement\_ibm) | >= 1.71.0, <2.0.0 |

### Modules
Expand Down
2 changes: 1 addition & 1 deletion modules/fscloud/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.3.0"
required_version = ">= 1.9.0"
required_providers {
# The below tflint-ignore is required because although the below provider is not directly required by this submodule,
# it is required by consuming modules, and if not set here, the top level module calling this module will not be
Expand Down
70 changes: 70 additions & 0 deletions solutions/enterprise/DA-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
Several optional input variables in the IBM Cloud Event Streams deployable architecture use complex object types. You specify these inputs when you configure you deployable architecture.

- [Service credentials](#svc-credential-name) (`service_credential_names`)
- [Service credential secrets](#service-credential-secrets) (`service_credential_secrets`)
- [Quotas](#quotas) (`quotas`)
- [Mirroring](#mirroring) (`quotas`)

## Service credentials <a name="svc-credential-name"></a>

You can specify a set of IAM credentials to connect to the instance with the `service_credential_names` input variable. Include a credential name and IAM service role for each key-value pair. Each role provides a specific level of access to the instance. For more information, see [Adding and viewing credentials](https://cloud.ibm.com/docs/account?topic=account-service_credentials&interface=ui).

If you want to add service credentials to secret manager and to allow secret manager to manage it, you should use `service_credential_secrets` , see [Service credential secrets](#service-credential-secrets).

- Variable name: `service_credential_names`.
- Type: A map. The key is the name of the service credential. The value is the role that is assigned to that credential.
- Default value: An empty map (`{}`).
Expand All @@ -29,6 +32,73 @@ You can specify a set of IAM credentials to connect to the instance with the `se
}
```

## Service credential secrets <a name="service-credential-secrets"></a>

When you add an IBM Event Streams deployable architecture from the IBM Cloud catalog to IBM Cloud Project, you can configure service credentials. In edit mode for the projects configuration, from the configure panel click the optional tab.

To enter a custom value, use the edit action to open the "Edit Array" panel. Add the service credential secrets configurations to the array here.

In the configuration, specify the secret group name, whether it already exists or will be created and include all the necessary service credential secrets that need to be created within that secret group.

[Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-getting-started#getting-started) about service credential secrets.

- Variable name: `service_credential_secrets`.
- Type: A list of objects that represent service credential secret groups and secrets
- Default value: An empty list (`[]`)

### Options for service_credential_secrets

- `secret_group_name` (required): A unique human-readable name that identifies this service credential secret group.
- `secret_group_description` (optional, default = `null`): A human-readable description for this secret group.
- `existing_secret_group`: (optional, default = `false`): Set to true, if secret group name provided in the variable `secret_group_name` already exists.
- `service_credentials`: (required): A list of object that represents a service credential secret.

#### Options for service_credentials

- `secret_name`: (required): A unique human-readable name of the secret to create.
- `service_credentials_source_service_role_crn`: (required): The CRN of the role to give the service credential in the IBM Cloud Database service. Service credentials role CRNs can be found at https://cloud.ibm.com/iam/roles, select the IBM Cloud Database and select the role.
- `secret_labels`: (optional, default = `[]`): Labels of the secret to create. Up to 30 labels can be created. Labels can be 2 - 30 characters, including spaces. Special characters that are not permitted include the angled brackets (<>), comma (,), colon (:), ampersand (&), and vertical pipe character (|).
- `secret_auto_rotation`: (optional, default = `true`): Whether to configure automatic rotation of service credential.
- `secret_auto_rotation_unit`: (optional, default = `day`): Specifies the unit of time for rotation of a secret. Acceptable values are `day` or `month`.
- `secret_auto_rotation_interval`: (optional, default = `89`): Specifies the rotation interval for the rotation unit.
- `service_credentials_ttl`: (optional, default = `7776000`): The time-to-live (TTL) to assign to generated service credentials (in seconds).
- `service_credential_secret_description`: (optional, default = `null`): Description of the secret to create.

The following example includes all the configuration options for four service credentials and two secret groups.
```hcl
[
{
"secret_group_name": "sg-1"
"existing_secret_group": true
"service_credentials": [ #pragma: allowlist secret
{
"secret_name": "cred-1"
"service_credentials_source_service_role_crn": "crn:v1:bluemix:public:iam::::role:Writer"
"secret_labels": ["test-writer-1", "test-writer-2"]
"secret_auto_rotation": true
"secret_auto_rotation_unit": "day"
"secret_auto_rotation_interval": 89
"service_credentials_ttl": 7776000
"service_credential_secret_description": "sample description"
},
{
"secret_name": "cred-2"
"service_credentials_source_service_role_crn": "crn:v1:bluemix:public:iam::::role:Reader"
}
]
},
{
"secret_group_name": "sg-2"
"service_credentials": [ #pragma: allowlist secret
{
"secret_name": "cred-3"
"service_credentials_source_service_role_crn": "crn:v1:bluemix:public:iam::::role:Editor"
}
]
}
]
```

## Quotas <a name="quotas"></a>

You can set quotas of an Event Streams service instance. Both the default quota and user quotas may be managed. Quotas are only available on Event Streams Enterprise plan service instances. For more information, see [Event Streams Quotas](https://cloud.ibm.com/docs/EventStreams?topic=EventStreams-enabling_kafka_quotas).
Expand Down
69 changes: 69 additions & 0 deletions solutions/enterprise/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,72 @@ module "event_streams" {
skip_kms_iam_authorization_policy = var.skip_event_streams_kms_auth_policy
skip_es_s2s_iam_authorization_policy = var.skip_event_streams_s2s_iam_auth_policy
}

########################################################################################################################
# Service Credentials
########################################################################################################################

# If existing EN intance CRN passed, parse details from it
module "existing_sm_crn_parser" {
count = var.existing_secrets_manager_instance_crn != null ? 1 : 0
source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser"
version = "1.1.0"
crn = var.existing_secrets_manager_instance_crn
}

locals {
# parse SM GUID from CRN
existing_secrets_manager_instance_guid = var.existing_secrets_manager_instance_crn != null ? module.existing_sm_crn_parser[0].service_instance : null
# parse SM region from CRN
existing_secrets_manager_instance_region = var.existing_secrets_manager_instance_crn != null ? module.existing_sm_crn_parser[0].region : null
# generate list of service credential secrets to create
service_credential_secrets = [
for service_credentials in var.service_credential_secrets : {
secret_group_name = service_credentials.secret_group_name
secret_group_description = service_credentials.secret_group_description
existing_secret_group = service_credentials.existing_secret_group
secrets = [
for secret in service_credentials.service_credentials : {
secret_name = secret.secret_name
secret_labels = secret.secret_labels
secret_auto_rotation = secret.secret_auto_rotation
secret_auto_rotation_unit = secret.secret_auto_rotation_unit
secret_auto_rotation_interval = secret.secret_auto_rotation_interval
service_credentials_ttl = secret.service_credentials_ttl
service_credential_secret_description = secret.service_credential_secret_description
service_credentials_source_service_role_crn = secret.service_credentials_source_service_role_crn
service_credentials_source_service_crn = module.event_streams.crn
secret_type = "service_credentials" #checkov:skip=CKV_SECRET_6
}
]
}
]
}

# create a service authorization between Secrets Manager and the target service (Event Streams)
resource "ibm_iam_authorization_policy" "secrets_manager_key_manager" {
count = var.skip_event_streams_secrets_manager_auth_policy || var.existing_secrets_manager_instance_crn == null ? 0 : 1
source_service_name = "secrets-manager"
source_resource_instance_id = local.existing_secrets_manager_instance_guid
target_service_name = "messagehub"
target_resource_instance_id = module.event_streams.guid
roles = ["Key Manager"]
description = "Allow Secrets Manager instance to manage key for the event-streams instance"
}

# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478
resource "time_sleep" "wait_for_en_authorization_policy" {
depends_on = [ibm_iam_authorization_policy.secrets_manager_key_manager]
create_duration = "30s"
}

module "secrets_manager_service_credentials" {
count = length(local.service_credential_secrets) > 0 ? 1 : 0
depends_on = [time_sleep.wait_for_en_authorization_policy]
source = "terraform-ibm-modules/secrets-manager/ibm//modules/secrets"
version = "1.22.0"
existing_sm_instance_guid = local.existing_secrets_manager_instance_guid
existing_sm_instance_region = local.existing_secrets_manager_instance_region
endpoint_type = var.existing_secrets_manager_endpoint_type
secrets = local.service_credential_secrets
}
10 changes: 10 additions & 0 deletions solutions/enterprise/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ output "service_credentials_object" {
sensitive = true
}

output "service_credential_secrets" {
description = "Service credential secrets"
value = length(local.service_credential_secrets) > 0 ? module.secrets_manager_service_credentials[0].secrets : null
}

output "service_credential_secret_groups" {
description = "Service credential secret groups"
value = length(local.service_credential_secrets) > 0 ? module.secrets_manager_service_credentials[0].secret_groups : null
}

output "mirroring_config_id" {
description = "The ID of the mirroring config in CRN format"
value = module.event_streams.mirroring_config_id
Expand Down
Loading