diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml index 019c7766..8b1b246c 100644 --- a/.catalog-onboard-pipeline.yaml +++ b/.catalog-onboard-pipeline.yaml @@ -6,9 +6,17 @@ offerings: catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd offering_id: 6d6ebc76-7bbd-42f5-8bc7-78f4fabd5944 variations: - - name: standard + - name: security-enforced mark_ready: true install_type: fullstack scc: instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 region: us-south + scope_resource_group_var_name: existing_resource_group_name + - name: fully-configurable + mark_ready: true + install_type: fullstack + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south + scope_resource_group_var_name: existing_resource_group_name diff --git a/cra-config.yaml b/cra-config.yaml index 465562e0..5d485b6b 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -1,10 +1,12 @@ # More info about this file at https://github.com/terraform-ibm-modules/common-pipeline-assets/blob/main/.github/workflows/terraform-test-pipeline.md#cra-config-yaml version: "v1" CRA_TARGETS: - - CRA_TARGET: "solutions/standard" # Target directory for CRA scan. If not provided, the CRA Scan will not be run. + - CRA_TARGET: "solutions/fully-configurable" # Target directory for CRA scan. If not provided, the CRA Scan will not be run. CRA_IGNORE_RULES_FILE: "cra-tf-validate-ignore-rules.json" # CRA Ignore file to use. If not provided, it checks the repo root directory for `cra-tf-validate-ignore-rules.json` PROFILE_ID: "fe96bd4d-9b37-40f2-b39f-a62760e326a3" # SCC profile ID (currently set to 'IBM Cloud Framework for Financial Services' '1.7.0' profile). CRA_ENVIRONMENT_VARIABLES: # An optional map of environment variables for CRA, where the key is the variable name and value is the value. Useful for providing TF_VARs. TF_VAR_existing_kms_instance_crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/abac0df06b644a9cabc6e44f55b3880e:e6dce284-e80f-46e1-a3c1-830f7adff7a9::" - TF_VAR_resource_group_name: "test" + TF_VAR_kms_encryption_enabled: "true" + TF_VAR_existing_resource_group_name: "geretain-test-secrets-manager" TF_VAR_provider_visibility: "public" + TF_VAR_prefix: "test" diff --git a/ibm_catalog.json b/ibm_catalog.json index e0182bf6..4b89b135 100644 --- a/ibm_catalog.json +++ b/ibm_catalog.json @@ -20,8 +20,8 @@ "solution" ], "short_description": "Creates and configures a Secrets Manager instance.", - "long_description": "This solution is used to provision and configure an IBM Cloud Secrets Manager instance.", - "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/blob/main/solutions/standard/README.md", + "long_description": "This deployable architecture is used to provision and configure an [IBM Cloud Secrets Manager](https://www.ibm.com/products/secrets-manager) instance. Centrally manage your secrets in a single-tenant, dedicated instance. This Terraform-based automation is part of a broader suite of IBM-maintained Infrastructure as Code (IaC) asset collection, each following the naming pattern \"Cloud automation for *servicename*\" and focusing on single IBM Cloud service. These single-service deployable architectures can be used on their own to streamline and automate service deployments through an [IaC approach](https://cloud.ibm.com/docs/secure-enterprise?topic=secure-enterprise-understanding-projects), or assembled together into a broader [automated IaC stack](https://cloud.ibm.com/docs/secure-enterprise?topic=secure-enterprise-config-stack) to automate the deployment of an end-to-end solution architecture.", + "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/blob/main/README.md", "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-secrets-manager/main/images/secrets_manager.svg", "provider_name": "IBM", "features": [ @@ -39,16 +39,16 @@ }, { "title": "Configures lifecycle notifications for the Secrets Manager instance.", - "description": "Configures lifecycle notifications for the IBM Secrets Manager instance by connecting an IBM Event Notifications service. The DA supports optionally creating a KMS key ring and key, or using an already existing one to encrypt data." + "description": "Configures lifecycle notifications for the IBM Secrets Manager instance by connecting an IBM Event Notifications service. The automation supports optionally creating a KMS key ring and key, or using an already existing one to encrypt data." } ], "support_details": "This product is in the community registry, as such support is handled through the originated repo. If you experience issues please open an issue in that repository [https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/issues](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/issues). Please note this product is not supported via the IBM Cloud Support Center.", "flavors": [ { - "label": "Standard", - "name": "standard", + "label": "Fully configurable", + "name": "fully-configurable", "install_type": "fullstack", - "working_directory": "solutions/standard", + "working_directory": "solutions/fully-configurable", "compliance": { "authority": "scc-v3", "profiles": [ @@ -113,18 +113,6 @@ "required": true, "description": "Prefix to add to all resources created by this solution. To not use any prefix value, you can enter the string `__NULL__`." }, - { - "key": "use_existing_resource_group", - "required": true - }, - { - "key": "resource_group_name", - "required": true - }, - { - "key": "existing_kms_instance_crn", - "required": true - }, { "key": "provider_visibility", "options": [ @@ -142,6 +130,18 @@ } ] }, + { + "key": "existing_resource_group_name", + "required": true, + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + } + }, { "key": "secrets_manager_instance_name" }, @@ -149,7 +149,20 @@ "key": "existing_secrets_manager_crn" }, { - "key": "secrets_manager_tags", + "key": "secrets_manager_endpoint_type", + "options": [ + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "private", + "value": "private" + } + ] + }, + { + "key": "secrets_manager_resource_tags", "custom_config": { "grouping": "deployment", "original_grouping": "deployment", @@ -172,74 +185,266 @@ ] }, { - "key": "iam_engine_enabled" + "key": "skip_sm_ce_iam_authorization_policy" }, { - "key": "iam_engine_name" + "key": "allowed_network", + "options": [ + { + "displayname": "private-only", + "value": "private-only" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] }, { - "key": "public_cert_engine_enabled" + "key": "kms_encryption_enabled" }, { - "key": "public_cert_engine_internet_services_crn" + "key": "existing_kms_instance_crn", + "required": true }, { - "key": "public_cert_engine_dns_provider_config_name" + "key": "existing_secrets_manager_kms_key_crn" }, { - "key": "public_cert_engine_lets_encrypt_config_ca_name" + "key": "skip_sm_kms_iam_authorization_policy" }, { - "key": "acme_letsencrypt_private_key" + "key": "ibmcloud_kms_api_key" }, { - "key": "private_cert_engine_enabled" + "key": "kms_endpoint_type", + "options": [ + { + "displayname": "Public", + "value": "public" + }, + { + "displayname": "Private", + "value": "private" + } + ] }, { - "key": "private_cert_engine_config_root_ca_name" + "key": "kms_key_ring_name" }, { - "key": "private_cert_engine_config_root_ca_common_name" + "key": "kms_key_name" }, { - "key": "private_cert_engine_config_root_ca_max_ttl" + "key": "event_notifications_email_list" }, { - "key": "private_cert_engine_config_intermediate_ca_name" + "key": "event_notifications_from_email" }, { - "key": "private_cert_engine_config_template_name" + "key": "event_notifications_reply_to_email" }, { - "key": "existing_secrets_manager_kms_key_crn" + "key": "existing_event_notifications_instance_crn" }, { - "key": "skip_kms_iam_authorization_policy" + "key": "skip_event_notifications_iam_authorization_policy" }, { - "key": "ibmcloud_kms_api_key" + "key": "secrets_manager_cbr_rules" + } + ], + "architecture": { + "descriptions": "This architecture supports creating and configuring a Secrets Manager instance.", + "features": [ + { + "title": "Secrets manager instance creation", + "description": "Yes" + }, + { + "title": "Use existing secrets manager instance", + "description": "Yes" + }, + { + "title": "New resource group creation", + "description": "No" + }, + { + "title": "Use existing resource group", + "description": "Yes" + }, + { + "title": "Enforced private-only endpoint communication", + "description": "Yes" + }, + { + "title": "Enforced KMS encryption", + "description": "Yes" + }, + { + "title": "KMS instance creation", + "description": "No" + }, + { + "title": "KMS key ring and key creation", + "description": "Yes" + }, + { + "title": "Use existing KMS key", + "description": "Yes" + }, + { + "title": "IAM s2s auth policies creation", + "description": "Yes" + }, + { + "title": "Event Notifications integration", + "description": "Yes" + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Secrets Manager", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-secrets-manager/main/reference-architecture/secrets_manager.svg", + "type": "image/svg+xml" + }, + "description": "This architecture supports creating and configuring IBM Secrets Manager instance." + } + ] + } + }, + { + "label": "Security-enforced", + "name": "security-enforced", + "install_type": "fullstack", + "working_directory": "solutions/security-enforced", + "compliance": { + "authority": "scc-v3", + "profiles": [ + { + "profile_name": "IBM Cloud Framework for Financial Services", + "profile_version": "1.7.0" + } + ] + }, + "configuration": [ + { + "key": "ibmcloud_api_key" }, { - "key": "kms_endpoint_type", + "key": "region", + "required": true, "options": [ { - "displayname": "Public", - "value": "public" + "displayname": "Osaka (jp-osa)", + "value": "jp-osa" }, { - "displayname": "Private", - "value": "private" + "displayname": "Sydney (au-syd)", + "value": "au-syd" + }, + { + "displayname": "Tokyo (jp-tok)", + "value": "jp-tok" + }, + { + "displayname": "Frankfurt (eu-de)", + "value": "eu-de" + }, + { + "displayname": "London (eu-gb)", + "value": "eu-gb" + }, + { + "displayname": "Madrid (eu-es)", + "value": "eu-es" + }, + { + "displayname": "Dallas (us-south)", + "value": "us-south" + }, + { + "displayname": "Toronto (ca-tor)", + "value": "ca-tor" + }, + { + "displayname": "Washington DC (us-east)", + "value": "us-east" + }, + { + "displayname": "Sao Paulo (br-sao)", + "value": "br-sao" } ] }, { - "key": "kms_key_ring_name" + "key": "prefix", + "required": true, + "description": "Prefix to add to all resources created by this solution. To not use any prefix value, you can enter the string `__NULL__`." }, { - "key": "kms_key_name" + "key": "existing_kms_instance_crn", + "required": true + }, + { + "key": "secrets_manager_instance_name" + }, + { + "key": "existing_secrets_manager_crn" + }, + { + "key": "secrets_manager_resource_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "service_plan", + "options": [ + { + "displayname": "Standard", + "value": "standard" + }, + { + "displayname": "Trial", + "value": "trial" + } + ] + }, + { + "key": "skip_sm_ce_iam_authorization_policy" + }, + { + "key": "existing_resource_group_name", + "required": true, + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + } + }, + { + "key": "existing_secrets_manager_kms_key_crn" + }, + { + "key": "skip_sm_kms_iam_authorization_policy" }, { - "key": "enable_event_notifications" + "key": "ibmcloud_kms_api_key" + }, + { + "key": "kms_key_ring_name" + }, + { + "key": "kms_key_name" }, { "key": "event_notifications_email_list" @@ -257,27 +462,55 @@ "key": "skip_event_notifications_iam_authorization_policy" }, { - "key":"cbr_rules" + "key": "secrets_manager_cbr_rules" } ], "architecture": { "descriptions": "This architecture supports creating and configuring a Secrets Manager instance.", "features": [ { - "title": "Creates a Secrets Manager instance.", - "description": "Creates and configures an IBM Secrets Manager instance." + "title": "Secrets manager instance creation", + "description": "Yes" + }, + { + "title": "Use existing secrets manager instance", + "description": "Yes" + }, + { + "title": "New resource group creation", + "description": "No" + }, + { + "title": "Use existing resource group", + "description": "Yes" + }, + { + "title": "Enforced private-only endpoint communication", + "description": "Yes" + }, + { + "title": "Enforced KMS encryption", + "description": "Yes" + }, + { + "title": "KMS instance creation", + "description": "No" + }, + { + "title": "KMS key ring and key creation", + "description": "Yes" }, { - "title": "Optionally configure an IBM Secrets Manager IAM credentials engine to an IBM Secrets Manager instance.", - "description": "Optionally configure an IBM Secrets Manager IAM credentials engine to an IBM Secrets Manager instance." + "title": "Use existing KMS key", + "description": "Yes" }, { - "title": "Sets up authorization policy.", - "description": "Sets up IBM IAM authorization policy between IBM Secrets Manager instance and IBM Key Management Service (KMS) instance. It also supports Event Notification authorization policy." + "title": "IAM s2s auth policies creation", + "description": "Yes" }, { - "title": "Configures lifecycle notifications for the Secrets Manager instance.", - "description": "Configures lifecycle notifications for the IBM Secrets Manager instance by connecting an IBM Event Notifications service. The DA supports optionally creating a KMS key ring and key, or using an already existing one to encrypt data." + "title": "Event Notifications integration", + "description": "Yes" } ], "diagrams": [ diff --git a/modules/fscloud/README.md b/modules/fscloud/README.md index fc02cb5d..52028253 100644 --- a/modules/fscloud/README.md +++ b/modules/fscloud/README.md @@ -30,7 +30,7 @@ module "secrets_manager" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [terraform](#requirement\_terraform) | >= 1.9.0 | | [ibm](#requirement\_ibm) | >=1.62.0, <2.0.0 | ### Modules diff --git a/modules/fscloud/version.tf b/modules/fscloud/version.tf index 8d342a5f..08387bee 100644 --- a/modules/fscloud/version.tf +++ b/modules/fscloud/version.tf @@ -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 diff --git a/modules/secrets/README.md b/modules/secrets/README.md index 54c335a4..fc8132b1 100644 --- a/modules/secrets/README.md +++ b/modules/secrets/README.md @@ -43,7 +43,7 @@ module "secrets_manager" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [terraform](#requirement\_terraform) | >= 1.9.0 | | [ibm](#requirement\_ibm) | >=1.62.0, <2.0.0 | ### Modules diff --git a/modules/secrets/version.tf b/modules/secrets/version.tf index 8d342a5f..08387bee 100644 --- a/modules/secrets/version.tf +++ b/modules/secrets/version.tf @@ -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 diff --git a/reference-architecture/secrets_manager.svg b/reference-architecture/secrets_manager.svg index dcf003a4..5cb34c79 100644 --- a/reference-architecture/secrets_manager.svg +++ b/reference-architecture/secrets_manager.svg @@ -1,4 +1,4 @@ -
IBM Cloud
Region
Resource Group
Secrets Manager
IAM Engine
Existing KMS
Key Ring
Root  KeyEvent Notifications
+
IBM Cloud
Region
Existing Resource Group
Secrets Manager
s2s IAM auth
Existing KMS
Key Ring
Root  Key
Existing Event NotificationsIAM
\ No newline at end of file diff --git a/solutions/standard/DA-cbr_rules.md b/solutions/fully-configurable/DA-cbr_rules.md similarity index 79% rename from solutions/standard/DA-cbr_rules.md rename to solutions/fully-configurable/DA-cbr_rules.md index 155a5368..4ff1ac22 100644 --- a/solutions/standard/DA-cbr_rules.md +++ b/solutions/fully-configurable/DA-cbr_rules.md @@ -2,18 +2,18 @@ Several optional input variables in the IBM Cloud [Secrets Manager deployable architecture](https://cloud.ibm.com/catalog#deployable_architecture) use complex object types. You specify these inputs when you configure deployable architecture. -* Context-Based Restrictions Rules (`cbr_rules`) +* Context-Based Restrictions Rules (`secrets_manager_cbr_rules`) -## Rules For Context-Based Restrictions +## Rules For Context-Based Restrictions -The `cbr_rules` input variable allows you to provide a rule for the target service to enforce access restrictions for the service based on the context of access requests. Contexts are criteria that include the network location of access requests, the endpoint type from where the request is sent, etc. +The `secrets_manager_cbr_rules` input variable allows you to provide a rule for the target service to enforce access restrictions for the service based on the context of access requests. Contexts are criteria that include the network location of access requests, the endpoint type from where the request is sent, etc. -- Variable name: `cbr_rules`. +- Variable name: `secrets_manager_cbr_rules`. - Type: A list of objects. Allows only one object representing a rule for the target service - Default value: An empty list (`[]`). -### Options for cbr_rules +### Options for secrets_manager_cbr_rules - `description` (required): The description of the rule to create. - `account_id` (required): The IBM Cloud Account ID @@ -34,7 +34,7 @@ The `cbr_rules` input variable allows you to provide a rule for the target servi ### Example Rule For Context-Based Restrictions Configuration ```hcl -cbr_rules = [ +[ { description = "Secrets Manager can be accessed from xyz" account_id = "defc0df06b644a9cabc6e44f55b3880s." diff --git a/solutions/fully-configurable/README.md b/solutions/fully-configurable/README.md new file mode 100644 index 00000000..4d60060f --- /dev/null +++ b/solutions/fully-configurable/README.md @@ -0,0 +1,88 @@ +# Secrets Manager fully-configurable solution + +This solution supports the following: +- Taking in an existing resource group. +- Provisioning and configuring of a Secrets Manager instance. +- Configuring KMS encryption using a newly created key, or passing an existing key. + +![secret-manager-deployable-architecture](../../reference-architecture/secrets_manager.svg) + +**NB:** This solution is not intended to be called by one or more other modules since it contains a provider configurations, meaning it is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers) + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [ibm](#requirement\_ibm) | 1.76.2 | +| [time](#requirement\_time) | 0.13.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [kms](#module\_kms) | terraform-ibm-modules/kms-all-inclusive/ibm | 4.20.0 | +| [kms\_instance\_crn\_parser](#module\_kms\_instance\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | +| [kms\_key\_crn\_parser](#module\_kms\_key\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | +| [resource\_group](#module\_resource\_group) | terraform-ibm-modules/resource-group/ibm | 1.1.6 | +| [secrets\_manager](#module\_secrets\_manager) | ../.. | n/a | + +### Resources + +| Name | Type | +|------|------| +| [ibm_en_subscription_email.email_subscription](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.2/docs/resources/en_subscription_email) | resource | +| [ibm_en_topic.en_topic](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.2/docs/resources/en_topic) | resource | +| [ibm_iam_authorization_policy.kms_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.2/docs/resources/iam_authorization_policy) | resource | +| [ibm_iam_authorization_policy.secrets_manager_hpcs_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.2/docs/resources/iam_authorization_policy) | resource | +| [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/0.13.0/docs/resources/sleep) | resource | +| [time_sleep.wait_for_secrets_manager](https://registry.terraform.io/providers/hashicorp/time/0.13.0/docs/resources/sleep) | resource | +| [time_sleep.wait_for_sm_hpcs_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/0.13.0/docs/resources/sleep) | resource | +| [ibm_en_destinations.en_destinations](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.2/docs/data-sources/en_destinations) | data source | +| [ibm_iam_account_settings.iam_account_settings](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.2/docs/data-sources/iam_account_settings) | data source | +| [ibm_resource_instance.existing_sm](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.2/docs/data-sources/resource_instance) | data source | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [allowed\_network](#input\_allowed\_network) | The types of service endpoints to set on the Secrets Manager instance. Possible values are `private-only` or `public-and-private`. | `string` | `"private-only"` | no | +| [event\_notifications\_email\_list](#input\_event\_notifications\_email\_list) | The list of email address to target out when Secrets Manager triggers an event | `list(string)` | `[]` | no | +| [event\_notifications\_from\_email](#input\_event\_notifications\_from\_email) | The email address used to send any Secrets Manager event coming via Event Notifications | `string` | `"compliancealert@ibm.com"` | no | +| [event\_notifications\_reply\_to\_email](#input\_event\_notifications\_reply\_to\_email) | The email address specified in the 'reply\_to' section for any Secret Manager event coming via Event Notifications | `string` | `"no-reply@ibm.com"` | no | +| [existing\_event\_notifications\_instance\_crn](#input\_existing\_event\_notifications\_instance\_crn) | The CRN of the Event Notifications service used to enable lifecycle notifications for your Secrets Manager instance. | `string` | `null` | no | +| [existing\_kms\_instance\_crn](#input\_existing\_kms\_instance\_crn) | The CRN of the KMS instance (Hyper Protect Crypto Services or Key Protect). Required only if `existing_secrets_manager_crn` or `existing_secrets_manager_kms_key_crn` is not specified. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`. | `string` | `null` | no | +| [existing\_resource\_group\_name](#input\_existing\_resource\_group\_name) | The name of an existing resource group to provision resource in. | `string` | `"Default"` | no | +| [existing\_secrets\_manager\_crn](#input\_existing\_secrets\_manager\_crn) | The CRN of an existing Secrets Manager instance. If not supplied, a new instance is created. | `string` | `null` | no | +| [existing\_secrets\_manager\_kms\_key\_crn](#input\_existing\_secrets\_manager\_kms\_key\_crn) | The CRN of a Key Protect or Hyper Protect Crypto Services key to use for Secrets Manager. If not specified, a key ring and key are created. | `string` | `null` | no | +| [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key used to provision resources. | `string` | n/a | yes | +| [ibmcloud\_kms\_api\_key](#input\_ibmcloud\_kms\_api\_key) | The IBM Cloud API key that can create a root key and key ring in the key management service (KMS) instance. If not specified, the 'ibmcloud\_api\_key' variable is used. Specify this key if the instance in `existing_kms_instance_crn` is in an account that's different from the Secrets Manager instance. Leave this input empty if the same account owns both instances. | `string` | `null` | no | +| [kms\_encryption\_enabled](#input\_kms\_encryption\_enabled) | Set to true to enable Secrets Manager Secrets Encryption using customer managed keys. When set to true, a value must be passed for either `existing_kms_instance_crn` or `existing_secrets_manager_kms_key_crn`. Cannot be set to true if passing a value for `existing_secrets_manager_crn`. | `bool` | `false` | no | +| [kms\_endpoint\_type](#input\_kms\_endpoint\_type) | The endpoint for communicating with the Key Protect or Hyper Protect Crypto Services instance. Possible values: `public`, `private`. Applies only if `existing_secrets_manager_kms_key_crn` is not specified. | `string` | `"private"` | no | +| [kms\_key\_name](#input\_kms\_key\_name) | The name for the new root key. Applies only if `existing_secrets_manager_kms_key_crn` is not specified. If a prefix input variable is passed, it is added to the value in the `-value` format. | `string` | `"secrets-manager-key"` | no | +| [kms\_key\_ring\_name](#input\_kms\_key\_ring\_name) | The name for the new key ring to store the key. Applies only if `existing_secrets_manager_kms_key_crn` is not specified. If a prefix input variable is passed, it is added to the value in the `-value` format. . | `string` | `"secrets-manager-key-ring"` | no | +| [prefix](#input\_prefix) | The prefix to add to all resources created by this solution. To not use any prefix value, you can set this value to `null` or an empty string. | `string` | n/a | yes | +| [provider\_visibility](#input\_provider\_visibility) | Set the visibility value for the IBM terraform provider. Supported values are `public`, `private`, `public-and-private`. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/guides/custom-service-endpoints). | `string` | `"private"` | no | +| [region](#input\_region) | The region to provision resources to. | `string` | `"us-south"` | no | +| [secrets\_manager\_cbr\_rules](#input\_secrets\_manager\_cbr\_rules) | (Optional, list) List of CBR rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/blob/main/solutions/fully-configurable/DA-cbr_rules.md) |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
operations = optional(list(object({
api_types = list(object({
api_type_id = string
}))
})))
}))
| `[]` | no | +| [secrets\_manager\_endpoint\_type](#input\_secrets\_manager\_endpoint\_type) | The type of endpoint (public or private) to connect to the Secrets Manager API. The Terraform provider uses this endpoint type to interact with the Secrets Manager API and configure Event Notifications. | `string` | `"private"` | no | +| [secrets\_manager\_instance\_name](#input\_secrets\_manager\_instance\_name) | The name to give the Secrets Manager instance provisioned by this solution. If a prefix input variable is specified, it is added to the value in the `-value` format. Applies only if `existing_secrets_manager_crn` is not provided. | `string` | `"secrets-manager"` | no | +| [secrets\_manager\_resource\_tags](#input\_secrets\_manager\_resource\_tags) | The list of resource tags you want to associate with your Secrets Manager instance. Applies only if `existing_secrets_manager_crn` is not provided. | `list(any)` | `[]` | no | +| [service\_plan](#input\_service\_plan) | The pricing plan to use when provisioning a Secrets Manager instance. Possible values: `standard`, `trial`. | `string` | `"standard"` | no | +| [skip\_event\_notifications\_iam\_authorization\_policy](#input\_skip\_event\_notifications\_iam\_authorization\_policy) | If set to true, this skips the creation of a service to service authorization from Secrets Manager to Event Notifications. If false, the service to service authorization is created. | `bool` | `false` | no | +| [skip\_sm\_ce\_iam\_authorization\_policy](#input\_skip\_sm\_ce\_iam\_authorization\_policy) | Whether to skip the creation of the IAM authorization policies required to enable the IAM credentials engine. If set to false, policies will be created that grants the Secrets Manager instance 'Operator' access to the IAM identity service, and 'Groups Service Member Manage' access to the IAM groups service. | `bool` | `false` | no | +| [skip\_sm\_kms\_iam\_authorization\_policy](#input\_skip\_sm\_kms\_iam\_authorization\_policy) | Set to true to skip the creation of an IAM authorization policy that permits all Secrets Manager instances in the resource group to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the `existing_kms_instance_crn` variable. If a value is specified for `ibmcloud_kms_api_key`, the policy is created in the KMS account. | `bool` | `false` | no | + +### Outputs + +| Name | Description | +|------|-------------| +| [resource\_group\_id](#output\_resource\_group\_id) | Resource group ID | +| [resource\_group\_name](#output\_resource\_group\_name) | Resource group name | +| [secrets\_manager\_crn](#output\_secrets\_manager\_crn) | CRN of the Secrets Manager instance | +| [secrets\_manager\_guid](#output\_secrets\_manager\_guid) | GUID of Secrets Manager instance | +| [secrets\_manager\_id](#output\_secrets\_manager\_id) | ID of Secrets Manager instance. | +| [secrets\_manager\_name](#output\_secrets\_manager\_name) | Name of the Secrets Manager instance | +| [secrets\_manager\_region](#output\_secrets\_manager\_region) | Region of the Secrets Manager instance | + diff --git a/solutions/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template new file mode 100644 index 00000000..2adb0113 --- /dev/null +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -0,0 +1,9 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "existing_resource_group_name": "geretain-test-secrets-manager", + "prefix": $PREFIX, + "service_plan": "trial", + "kms_encryption_enabled": true, + "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN, + "region": "eu-de" +} diff --git a/solutions/standard/main.tf b/solutions/fully-configurable/main.tf similarity index 59% rename from solutions/standard/main.tf rename to solutions/fully-configurable/main.tf index da932fdb..94b74d59 100644 --- a/solutions/standard/main.tf +++ b/solutions/fully-configurable/main.tf @@ -2,45 +2,37 @@ # Resource Group ######################################################################################################################## locals { - # tflint-ignore: terraform_unused_declarations - validate_resource_group = (var.existing_secrets_manager_crn == null && var.resource_group_name == null) ? tobool("Resource group name can not be null if existing secrets manager CRN is not set.") : true - # tflint-ignore: terraform_unused_declarations - validate_event_notifications = (var.existing_event_notifications_instance_crn == null && var.enable_event_notifications) ? tobool("To enable event notifications, an existing event notifications CRN must be set.") : true - prefix = var.prefix != null ? (var.prefix != "" ? var.prefix : null) : null + prefix = var.prefix != null ? trimspace(var.prefix) != "" ? "${var.prefix}-" : "" : "" } module "resource_group" { - count = var.existing_secrets_manager_crn == null ? 1 : 0 source = "terraform-ibm-modules/resource-group/ibm" version = "1.1.6" - resource_group_name = var.use_existing_resource_group == false ? try("${local.prefix}-${var.resource_group_name}", var.resource_group_name) : null - existing_resource_group_name = var.use_existing_resource_group == true ? var.resource_group_name : null + existing_resource_group_name = var.existing_resource_group_name } ####################################################################################################################### # KMS Key ####################################################################################################################### locals { - kms_key_crn = var.existing_secrets_manager_crn == null ? (var.existing_secrets_manager_kms_key_crn != null ? var.existing_secrets_manager_kms_key_crn : module.kms[0].keys[format("%s.%s", local.kms_key_ring_name, local.kms_key_name)].crn) : var.existing_secrets_manager_kms_key_crn - kms_key_ring_name = try("${local.prefix}-${var.kms_key_ring_name}", var.kms_key_ring_name) - kms_key_name = try("${local.prefix}-${var.kms_key_name}", var.kms_key_name) + kms_key_crn = var.existing_secrets_manager_crn == null ? (var.existing_secrets_manager_kms_key_crn != null ? var.existing_secrets_manager_kms_key_crn : (var.kms_encryption_enabled == true ? module.kms[0].keys[format("%s.%s", local.kms_key_ring_name, local.kms_key_name)].crn : null)) : var.existing_secrets_manager_kms_key_crn + kms_key_ring_name = "${local.prefix}${var.kms_key_ring_name}" + kms_key_name = "${local.prefix}${var.kms_key_name}" - parsed_existing_kms_instance_crn = var.existing_kms_instance_crn != null ? split(":", var.existing_kms_instance_crn) : [] - kms_region = length(local.parsed_existing_kms_instance_crn) > 0 ? local.parsed_existing_kms_instance_crn[5] : null + kms_region = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].region : null - parsed_service_name = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_name : module.kms_key_crn_parser[0].service_name + parsed_service_name = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_name : (var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].service_name : null) is_hpcs_key = local.parsed_service_name == "hs-crypto" ? true : false - create_cross_account_auth_policy = var.existing_secrets_manager_crn == null && !var.skip_kms_iam_authorization_policy && var.ibmcloud_kms_api_key != null + create_cross_account_auth_policy = var.existing_secrets_manager_crn == null && !var.skip_sm_kms_iam_authorization_policy && var.ibmcloud_kms_api_key != null create_cross_account_hpcs_auth_policy = local.create_cross_account_auth_policy == true && local.is_hpcs_key ? 1 : 0 - kms_service_name = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].service_name : module.kms_instance_crn_parser[0].service_name - kms_key_id = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].resource : module.kms_instance_crn_parser[0].resource - kms_instance_guid = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].service_instance : module.kms_instance_crn_parser[0].service_instance - kms_account_id = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].account_id : module.kms_instance_crn_parser[0].account_id - + kms_service_name = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].service_name : (var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_name : null) + kms_key_id = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].resource : (var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].resource : null) + kms_instance_guid = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].service_instance : (var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_instance : null) + kms_account_id = var.existing_secrets_manager_kms_key_crn != null ? module.kms_key_crn_parser[0].account_id : (var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].account_id : null) } -# Lookup account ID + data "ibm_iam_account_settings" "iam_account_settings" { count = local.create_cross_account_auth_policy ? 1 : 0 } @@ -71,7 +63,7 @@ resource "ibm_iam_authorization_policy" "kms_policy" { source_service_name = "secrets-manager" source_resource_group_id = module.resource_group[0].resource_group_id roles = ["Reader"] - description = "Allow all Secrets Manager instances in the resource group ${module.resource_group[0].resource_group_id} in the account ${data.ibm_iam_account_settings.iam_account_settings[0].account_id} to read the ${local.kms_service_name} key ${local.kms_key_id} from the instance GUID ${local.kms_instance_guid}" + description = "Allow all Secrets Manager instances in the resource group ${local.kms_account_id} to read the ${local.kms_service_name} key ${local.kms_key_id} from the instance GUID ${local.kms_instance_guid}" resource_attributes { name = "serviceName" operator = "stringEquals" @@ -121,7 +113,7 @@ resource "ibm_iam_authorization_policy" "secrets_manager_hpcs_policy" { target_service_name = local.kms_service_name target_resource_instance_id = local.kms_instance_guid roles = ["Viewer"] - description = "Allow all Secrets Manager instances in the resource group ${module.resource_group[0].resource_group_id} in the account ${data.ibm_iam_account_settings.iam_account_settings[0].account_id} to view from the ${local.kms_service_name} instance GUID ${local.kms_instance_guid}" + description = "Allow all Secrets Manager instances in the resource group ${module.resource_group[0].resource_group_id} in the account ${local.kms_account_id} to view from the ${local.kms_service_name} instance GUID ${local.kms_instance_guid}" } # workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 @@ -131,15 +123,14 @@ resource "time_sleep" "wait_for_sm_hpcs_authorization_policy" { create_duration = "30s" } - # KMS root key for Secrets Manager secret encryption module "kms" { providers = { ibm = ibm.kms } - count = var.existing_secrets_manager_crn != null || var.existing_secrets_manager_kms_key_crn != null ? 0 : 1 # no need to create any KMS resources if passing an existing key, or bucket + count = var.existing_secrets_manager_crn == null && var.kms_encryption_enabled && var.existing_secrets_manager_kms_key_crn == null ? 1 : 0 # no need to create any KMS resources if passing an existing key source = "terraform-ibm-modules/kms-all-inclusive/ibm" - version = "4.21.3" + version = "4.20.0" create_key_protect_instance = false region = local.kms_region existing_kms_instance_crn = var.existing_kms_instance_crn @@ -155,7 +146,7 @@ module "kms" { standard_key = false rotation_interval_month = 3 dual_auth_delete_enabled = false - force_delete = true + force_delete = true # Force delete must be set to true, or the terraform destroy will fail since the service does not de-register itself from the key until the reclamation period has expired. } ] } @@ -171,73 +162,31 @@ locals { secrets_manager_guid = var.existing_secrets_manager_crn != null ? (length(local.parsed_existing_secrets_manager_crn) > 0 ? local.parsed_existing_secrets_manager_crn[7] : null) : module.secrets_manager.secrets_manager_guid secrets_manager_crn = var.existing_secrets_manager_crn != null ? var.existing_secrets_manager_crn : module.secrets_manager.secrets_manager_crn secrets_manager_region = var.existing_secrets_manager_crn != null ? (length(local.parsed_existing_secrets_manager_crn) > 0 ? local.parsed_existing_secrets_manager_crn[5] : null) : module.secrets_manager.secrets_manager_region + enable_event_notifications = var.existing_event_notifications_instance_crn != null ? true : false } module "secrets_manager" { - depends_on = [time_sleep.wait_for_authorization_policy, time_sleep.wait_for_sm_hpcs_authorization_policy] - source = "../../modules/fscloud" - existing_sm_instance_crn = var.existing_secrets_manager_crn - resource_group_id = var.existing_secrets_manager_crn == null ? module.resource_group[0].resource_group_id : data.ibm_resource_instance.existing_sm[0].resource_group_id - region = var.region - secrets_manager_name = try("${local.prefix}-${var.secrets_manager_instance_name}", var.secrets_manager_instance_name) - service_plan = var.service_plan - sm_tags = var.secrets_manager_tags - is_hpcs_key = local.is_hpcs_key + depends_on = [time_sleep.wait_for_authorization_policy] + source = "../.." + existing_sm_instance_crn = var.existing_secrets_manager_crn + resource_group_id = module.resource_group.resource_group_id + region = var.region + secrets_manager_name = "${local.prefix}${var.secrets_manager_instance_name}" + sm_service_plan = var.service_plan + sm_tags = var.secrets_manager_resource_tags + skip_iam_authorization_policy = var.skip_sm_ce_iam_authorization_policy # kms dependency + is_hpcs_key = local.is_hpcs_key + kms_encryption_enabled = var.kms_encryption_enabled kms_key_crn = local.kms_key_crn - skip_kms_iam_authorization_policy = var.skip_kms_iam_authorization_policy || local.create_cross_account_auth_policy + skip_kms_iam_authorization_policy = var.skip_sm_kms_iam_authorization_policy || local.create_cross_account_auth_policy # event notifications dependency - enable_event_notification = var.enable_event_notifications + enable_event_notification = local.enable_event_notifications existing_en_instance_crn = var.existing_event_notifications_instance_crn skip_en_iam_authorization_policy = var.skip_event_notifications_iam_authorization_policy - cbr_rules = var.cbr_rules -} - -# Configure an IBM Secrets Manager IAM credentials engine for an existing IBM Secrets Manager instance. -module "iam_secrets_engine" { - count = var.iam_engine_enabled ? 1 : 0 - source = "terraform-ibm-modules/secrets-manager-iam-engine/ibm" - version = "1.2.10" - region = local.secrets_manager_region - iam_engine_name = try("${local.prefix}-${var.iam_engine_name}", var.iam_engine_name) - secrets_manager_guid = local.secrets_manager_guid - endpoint_type = "private" -} - - -# Configure an IBM Secrets Manager public certificate engine for an existing IBM Secrets Manager instance. -module "secrets_manager_public_cert_engine" { - count = var.public_cert_engine_enabled ? 1 : 0 - source = "terraform-ibm-modules/secrets-manager-public-cert-engine/ibm" - version = "1.0.3" - providers = { - ibm = ibm - ibm.secret-store = ibm - } - secrets_manager_guid = local.secrets_manager_guid - region = local.secrets_manager_region - internet_services_crn = var.public_cert_engine_internet_services_crn - ibmcloud_cis_api_key = var.ibmcloud_api_key - dns_config_name = var.public_cert_engine_dns_provider_config_name - ca_config_name = var.public_cert_engine_lets_encrypt_config_ca_name - acme_letsencrypt_private_key = var.acme_letsencrypt_private_key - service_endpoints = "private" -} - - -# Configure an IBM Secrets Manager private certificate engine for an existing IBM Secrets Manager instance. -module "private_secret_engine" { - count = var.private_cert_engine_enabled ? 1 : 0 - source = "terraform-ibm-modules/secrets-manager-private-cert-engine/ibm" - version = "1.3.5" - secrets_manager_guid = local.secrets_manager_guid - region = var.region - root_ca_name = var.private_cert_engine_config_root_ca_name - root_ca_common_name = var.private_cert_engine_config_root_ca_common_name - root_ca_max_ttl = var.private_cert_engine_config_root_ca_max_ttl - intermediate_ca_name = var.private_cert_engine_config_intermediate_ca_name - certificate_template_name = var.private_cert_engine_config_template_name - endpoint_type = "private" + cbr_rules = var.secrets_manager_cbr_rules + endpoint_type = var.secrets_manager_endpoint_type + allowed_network = var.allowed_network } data "ibm_resource_instance" "existing_sm" { @@ -256,14 +205,14 @@ locals { data "ibm_en_destinations" "en_destinations" { # if existing SM instance CRN is passed (!= null), then never do data lookup for EN destinations - count = var.existing_secrets_manager_crn == null && var.enable_event_notifications ? 1 : 0 + count = var.existing_secrets_manager_crn == null && local.enable_event_notifications ? 1 : 0 instance_guid = local.existing_en_guid } # workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/5533 resource "time_sleep" "wait_for_secrets_manager" { # if existing SM instance CRN is passed (!= null), then never work with EN - count = var.existing_secrets_manager_crn == null && var.enable_event_notifications ? 1 : 0 + count = var.existing_secrets_manager_crn == null && local.enable_event_notifications ? 1 : 0 depends_on = [module.secrets_manager] create_duration = "30s" @@ -271,10 +220,10 @@ resource "time_sleep" "wait_for_secrets_manager" { resource "ibm_en_topic" "en_topic" { # if existing SM instance CRN is passed (!= null), then never create EN topic - count = var.existing_secrets_manager_crn == null && var.enable_event_notifications ? 1 : 0 + count = var.existing_secrets_manager_crn == null && local.enable_event_notifications ? 1 : 0 depends_on = [time_sleep.wait_for_secrets_manager] instance_guid = local.existing_en_guid - name = "Secrets Manager Topic" + name = "Topic for SCC instance ${module.secrets_manager.secrets_manager_guid}" description = "Topic for Secrets Manager events routing" sources { id = local.secrets_manager_crn @@ -287,7 +236,7 @@ resource "ibm_en_topic" "en_topic" { resource "ibm_en_subscription_email" "email_subscription" { # if existing SM instance CRN is passed (!= null), then never create EN email subscription - count = var.existing_secrets_manager_crn == null && var.enable_event_notifications && length(var.event_notifications_email_list) > 0 ? 1 : 0 + count = var.existing_secrets_manager_crn == null && local.enable_event_notifications && length(var.event_notifications_email_list) > 0 ? 1 : 0 instance_guid = local.existing_en_guid name = "Email for Secrets Manager Subscription" description = "Subscription for Secret Manager Events" diff --git a/solutions/standard/outputs.tf b/solutions/fully-configurable/outputs.tf similarity index 69% rename from solutions/standard/outputs.tf rename to solutions/fully-configurable/outputs.tf index 9c4d0c8f..52332205 100644 --- a/solutions/standard/outputs.tf +++ b/solutions/fully-configurable/outputs.tf @@ -1,11 +1,11 @@ output "resource_group_name" { description = "Resource group name" - value = var.existing_secrets_manager_crn == null ? module.resource_group[0].resource_group_name : data.ibm_resource_instance.existing_sm[0].resource_group_name + value = module.resource_group.resource_group_name } output "resource_group_id" { description = "Resource group ID" - value = var.existing_secrets_manager_crn == null ? module.resource_group[0].resource_group_id : data.ibm_resource_instance.existing_sm[0].resource_group_id + value = module.resource_group.resource_group_id } output "secrets_manager_guid" { @@ -14,7 +14,7 @@ output "secrets_manager_guid" { } output "secrets_manager_id" { - description = "ID of Secrets Manager instance. Same value as secrets_manager_guid" + description = "ID of Secrets Manager instance." value = var.existing_secrets_manager_crn == null ? module.secrets_manager.secrets_manager_id : local.secrets_manager_guid } diff --git a/solutions/standard/provider.tf b/solutions/fully-configurable/provider.tf similarity index 100% rename from solutions/standard/provider.tf rename to solutions/fully-configurable/provider.tf diff --git a/solutions/standard/variables.tf b/solutions/fully-configurable/variables.tf similarity index 61% rename from solutions/standard/variables.tf rename to solutions/fully-configurable/variables.tf index de0173c2..55df6bb1 100644 --- a/solutions/standard/variables.tf +++ b/solutions/fully-configurable/variables.tf @@ -4,7 +4,7 @@ variable "ibmcloud_api_key" { type = string - description = "The API Key to use for IBM Cloud." + description = "The IBM Cloud API key used to provision resources." sensitive = true } @@ -19,16 +19,11 @@ variable "provider_visibility" { } } -variable "use_existing_resource_group" { - type = bool - description = "Whether to use an existing resource group." - default = false -} - -variable "resource_group_name" { +variable "existing_resource_group_name" { type = string - description = "The name of a new or existing resource group to provision resources to. If a prefix input variable is specified, it's added to the value in the `-value` format. Optional if `existing_secrets_manager_crn` is not specified." - default = null + description = "The name of an existing resource group to provision resource in." + default = "Default" + nullable = false } variable "region" { @@ -40,7 +35,16 @@ variable "region" { variable "prefix" { type = string description = "The prefix to add to all resources created by this solution. To not use any prefix value, you can set this value to `null` or an empty string." - default = "dev" + + validation { + condition = (var.prefix == null ? true : + alltrue([ + can(regex("^[a-z]{0,1}[-a-z0-9]{0,14}[a-z0-9]{0,1}$", var.prefix)), + length(regexall("^.*--.*", var.prefix)) == 0 + ]) + ) + error_message = "Prefix must begin with a lowercase letter, contain only lowercase letters, numbers, and - characters. Prefixes must end with a lowercase letter or number and be 16 or fewer characters." + } } ######################################################################################################################## @@ -49,7 +53,7 @@ variable "prefix" { variable "secrets_manager_instance_name" { type = string - description = "The name to give the Secrets Manager instance provisioned by this solution. If a prefix input variable is specified, it is added to the value in the `-value` format." + description = "The name to give the Secrets Manager instance provisioned by this solution. If a prefix input variable is specified, it is added to the value in the `-value` format. Applies only if `existing_secrets_manager_crn` is not provided." default = "secrets-manager" } @@ -69,132 +73,106 @@ variable "service_plan" { } } -variable "secrets_manager_tags" { - type = list(any) - description = "The list of resource tags you want to associate with your Secrets Manager instance." - default = [] -} - -######################################################################################################################## -# Public cert engine config -######################################################################################################################## - -variable "public_cert_engine_enabled" { +variable "skip_sm_ce_iam_authorization_policy" { type = bool - description = "Set this to true to configure a Secrets Manager public certificate engine for an existing Secrets Manager instance. If set to false, no public certificate engine will be configured for your instance." + description = "Whether to skip the creation of the IAM authorization policies required to enable the IAM credentials engine. If set to false, policies will be created that grants the Secrets Manager instance 'Operator' access to the IAM identity service, and 'Groups Service Member Manage' access to the IAM groups service." default = false } -variable "public_cert_engine_internet_services_crn" { - type = string - description = "Cloud Internet Service ID." - default = null -} - -variable "public_cert_engine_dns_provider_config_name" { - type = string - description = "The name of the DNS provider for the public certificate secrets engine configuration." - default = "certificate-dns" +variable "secrets_manager_resource_tags" { + type = list(any) + description = "The list of resource tags you want to associate with your Secrets Manager instance. Applies only if `existing_secrets_manager_crn` is not provided." + default = [] } -variable "public_cert_engine_lets_encrypt_config_ca_name" { +variable "secrets_manager_endpoint_type" { type = string - description = "The name of the certificate authority for Secrets Manager." - default = "cert-auth" + description = "The type of endpoint (public or private) to connect to the Secrets Manager API. The Terraform provider uses this endpoint type to interact with the Secrets Manager API and configure Event Notifications." + default = "private" + validation { + condition = contains(["public", "private"], var.secrets_manager_endpoint_type) + error_message = "The specified service endpoint is not a valid selection!" + } } -variable "acme_letsencrypt_private_key" { +variable "allowed_network" { type = string - description = "The private key generated by the ACME account creation tool." - sensitive = true - default = null + description = "The types of service endpoints to set on the Secrets Manager instance. Possible values are `private-only` or `public-and-private`." + default = "private-only" + validation { + condition = contains(["private-only", "public-and-private"], var.allowed_network) + error_message = "The specified allowed_network is not a valid selection!" + } } ######################################################################################################################## -# Private cert engine config +# Key Protect ######################################################################################################################## -variable "private_cert_engine_enabled" { +variable "skip_sm_kms_iam_authorization_policy" { type = bool - description = "Set this to true to configure a Secrets Manager private certificate engine for an existing instance. If set to false, no private certificate engine will be configured for your instance." + description = "Set to true to skip the creation of an IAM authorization policy that permits all Secrets Manager instances in the resource group to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the `existing_kms_instance_crn` variable. If a value is specified for `ibmcloud_kms_api_key`, the policy is created in the KMS account." default = false } -variable "private_cert_engine_config_root_ca_name" { - type = string - description = "The name of the root certificate authority associated with the private_cert secret engine." - default = "root-ca" -} - -variable "private_cert_engine_config_root_ca_common_name" { - type = string - description = "The fully qualified domain name or host domain name for the certificate that will be created." - default = "terraform-modules.ibm.com" -} - -variable "private_cert_engine_config_root_ca_max_ttl" { - type = string - description = "The maximum time-to-live value for the root certificate authority." - default = "87600h" -} - -variable "private_cert_engine_config_intermediate_ca_name" { - type = string - description = "A human-readable unique name to assign to the intermediate certificate authority configuration." - default = "intermediate-ca" -} - -variable "private_cert_engine_config_template_name" { +variable "existing_secrets_manager_kms_key_crn" { type = string - description = "The name of the certificate template." - default = "default-cert-template" + description = "The CRN of a Key Protect or Hyper Protect Crypto Services key to use for Secrets Manager. If not specified, a key ring and key are created." + default = null } ######################################################################################################################## -# IAM engine config +# KMS properties required when creating an encryption key, rather than passing an existing key CRN. ######################################################################################################################## -variable "iam_engine_enabled" { +variable "kms_encryption_enabled" { type = bool - description = "Set this to true to to configure a Secrets Manager IAM credentials engine. If set to false, no IAM engine will be configured for your instance." + description = "Set to true to enable Secrets Manager Secrets Encryption using customer managed keys. When set to true, a value must be passed for either `existing_kms_instance_crn` or `existing_secrets_manager_kms_key_crn`. Cannot be set to true if passing a value for `existing_secrets_manager_crn`." default = false -} -variable "iam_engine_name" { - type = string - description = "The name of the IAM engine used to configure a Secrets Manager IAM credentials engine. If the prefix input variable is passed it is attached before the value in the format of '-value'." - default = "iam-engine" -} + validation { + condition = var.kms_encryption_enabled ? var.existing_secrets_manager_crn == null : true + error_message = "'kms_encryption_enabled' should be false if passing a value for 'existing_secrets_manager_crn'." + } -######################################################################################################################## -# Key Protect -######################################################################################################################## + validation { + condition = var.existing_secrets_manager_kms_key_crn != null ? var.kms_encryption_enabled : true + error_message = "If passing a value for 'existing_secrets_manager_kms_key_crn', you should set 'kms_encryption_enabled' to true." + } -variable "skip_kms_iam_authorization_policy" { - type = bool - description = "Set to true to skip the creation of an IAM authorization policy that permits all Secrets Manager instances in the resource group to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the `existing_kms_instance_crn` variable. If a value is specified for `ibmcloud_kms_api_key`, the policy is created in the KMS account." - default = false -} + validation { + condition = var.existing_kms_instance_crn != null ? var.kms_encryption_enabled : true + error_message = "If passing a value for 'existing_kms_instance_crn', you should set 'kms_encryption_enabled' to true." + } -variable "existing_secrets_manager_kms_key_crn" { - type = string - description = "The CRN of a Key Protect or Hyper Protect Crypto Services key to use for Secrets Manager. If not specified, a key ring and key are created." - default = null + validation { + condition = var.kms_encryption_enabled ? ((var.existing_kms_instance_crn != null || var.existing_secrets_manager_kms_key_crn != null) ? true : false) : true + error_message = "Either 'existing_kms_instance_crn' or `existing_secrets_manager_kms_key_crn` is required if 'kms_encryption_enabled' is set to true." + } } -######################################################################################################################## -# KMS properties required when creating an encryption key, rather than passing an existing key CRN. -######################################################################################################################## - variable "existing_kms_instance_crn" { type = string default = null description = "The CRN of the KMS instance (Hyper Protect Crypto Services or Key Protect). Required only if `existing_secrets_manager_crn` or `existing_secrets_manager_kms_key_crn` is not specified. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`." + + validation { + condition = anytrue([ + can(regex("^crn:(.*:){3}(kms|hs-crypto):(.*:){2}[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}::$", var.existing_kms_instance_crn)), + var.existing_kms_instance_crn == null, + ]) + error_message = "The provided KMS instance CRN in the input 'existing_kms_instance_crn' in not valid." + } + + validation { + condition = var.existing_kms_instance_crn != null ? var.existing_secrets_manager_crn == null : true + error_message = "A value should not be passed for 'existing_kms_instance_crn' when passing an existing secrets manager instance using the 'existing_secrets_manager_crn' input." + } } variable "kms_endpoint_type" { type = string - description = "The type of endpoint to use for communicating with the Key Protect or Hyper Protect Crypto Services instance. Possible values: `public`, `private`. Applies only if `existing_secrets_manager_kms_key_crn` is not specified." + description = "The endpoint for communicating with the Key Protect or Hyper Protect Crypto Services instance. Possible values: `public`, `private`. Applies only if `existing_secrets_manager_kms_key_crn` is not specified." default = "private" validation { condition = can(regex("public|private", var.kms_endpoint_type)) @@ -225,12 +203,6 @@ variable "ibmcloud_kms_api_key" { # Event Notifications ######################################################################################################################## -variable "enable_event_notifications" { - type = bool - default = false - description = "Set this to true to enable lifecycle notifications for your Secrets Manager instance by connecting an Event Notifications service. When setting this to true, a value must be passed for `existing_event_notification_instance_crn`" -} - variable "existing_event_notifications_instance_crn" { type = string description = "The CRN of the Event Notifications service used to enable lifecycle notifications for your Secrets Manager instance." @@ -265,7 +237,7 @@ variable "event_notifications_reply_to_email" { # Context-based restriction (CBR) ############################################################## -variable "cbr_rules" { +variable "secrets_manager_cbr_rules" { type = list(object({ description = string account_id = string @@ -281,7 +253,7 @@ variable "cbr_rules" { })) }))) })) - description = "(Optional, list) List of CBR rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/blob/main/solutions/standard/DA-cbr_rules.md)" + description = "(Optional, list) List of CBR rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/blob/main/solutions/fully-configurable/DA-cbr_rules.md)" default = [] # Validation happens in the rule module } diff --git a/solutions/standard/version.tf b/solutions/fully-configurable/version.tf similarity index 89% rename from solutions/standard/version.tf rename to solutions/fully-configurable/version.tf index 8f470172..1230d4d2 100644 --- a/solutions/standard/version.tf +++ b/solutions/fully-configurable/version.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.3.0" + required_version = ">= 1.9.0" # Lock DA into an exact provider version - renovate automation will keep it updated required_providers { ibm = { diff --git a/solutions/security-enforced/README.md b/solutions/security-enforced/README.md new file mode 100644 index 00000000..0932c4e6 --- /dev/null +++ b/solutions/security-enforced/README.md @@ -0,0 +1,62 @@ +# Secrets Manager security-enforced solution + +This solution supports the following: +- Taking in an existing resource group. +- Provisioning and configuring of a Secrets Manager instance. +- Configuring KMS encryption using a newly created key, or passing an existing key. + +**NB:** This solution is not intended to be called by one or more other modules since it contains a provider configurations, meaning it is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers) + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [secrets\_manager](#module\_secrets\_manager) | ../fully-configurable | n/a | + +### Resources + +No resources. + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [event\_notifications\_email\_list](#input\_event\_notifications\_email\_list) | The list of email address to target out when Secrets Manager triggers an event | `list(string)` | `[]` | no | +| [event\_notifications\_from\_email](#input\_event\_notifications\_from\_email) | The email address used to send any Secrets Manager event coming via Event Notifications | `string` | `"compliancealert@ibm.com"` | no | +| [event\_notifications\_reply\_to\_email](#input\_event\_notifications\_reply\_to\_email) | The email address specified in the 'reply\_to' section for any Secret Manager event coming via Event Notifications | `string` | `"no-reply@ibm.com"` | no | +| [existing\_event\_notifications\_instance\_crn](#input\_existing\_event\_notifications\_instance\_crn) | The CRN of the Event Notifications service used to enable lifecycle notifications for your Secrets Manager instance. | `string` | `null` | no | +| [existing\_kms\_instance\_crn](#input\_existing\_kms\_instance\_crn) | The CRN of the KMS instance (Hyper Protect Crypto Services or Key Protect). Required only if `existing_secrets_manager_crn` or `existing_secrets_manager_kms_key_crn` is not specified. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`. | `string` | `null` | no | +| [existing\_resource\_group\_name](#input\_existing\_resource\_group\_name) | The name of an existing resource group to provision resource in. | `string` | `"Default"` | no | +| [existing\_secrets\_manager\_crn](#input\_existing\_secrets\_manager\_crn) | The CRN of an existing Secrets Manager instance. If not supplied, a new instance is created. | `string` | `null` | no | +| [existing\_secrets\_manager\_kms\_key\_crn](#input\_existing\_secrets\_manager\_kms\_key\_crn) | The CRN of a Key Protect or Hyper Protect Crypto Services key to use for Secrets Manager. If not specified, a key ring and key are created. | `string` | `null` | no | +| [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key used to provision resources. | `string` | n/a | yes | +| [ibmcloud\_kms\_api\_key](#input\_ibmcloud\_kms\_api\_key) | The IBM Cloud API key that can create a root key and key ring in the key management service (KMS) instance. If not specified, the 'ibmcloud\_api\_key' variable is used. Specify this key if the instance in `existing_kms_instance_crn` is in an account that's different from the Secrets Manager instance. Leave this input empty if the same account owns both instances. | `string` | `null` | no | +| [kms\_key\_name](#input\_kms\_key\_name) | The name for the new root key. Applies only if `existing_secrets_manager_kms_key_crn` is not specified. If a prefix input variable is passed, it is added to the value in the `-value` format. | `string` | `"secrets-manager-key"` | no | +| [kms\_key\_ring\_name](#input\_kms\_key\_ring\_name) | The name for the new key ring to store the key. Applies only if `existing_secrets_manager_kms_key_crn` is not specified. If a prefix input variable is passed, it is added to the value in the `-value` format. . | `string` | `"secrets-manager-key-ring"` | no | +| [prefix](#input\_prefix) | The prefix to add to all resources created by this solution. To not use any prefix value, you can set this value to `null` or an empty string. | `string` | n/a | yes | +| [region](#input\_region) | The region to provision resources to. | `string` | `"us-south"` | no | +| [secrets\_manager\_cbr\_rules](#input\_secrets\_manager\_cbr\_rules) | (Optional, list) List of CBR rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/blob/main/solutions/fully-configurable/DA-cbr_rules.md) |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
operations = optional(list(object({
api_types = list(object({
api_type_id = string
}))
})))
}))
| `[]` | no | +| [secrets\_manager\_instance\_name](#input\_secrets\_manager\_instance\_name) | The name to give the Secrets Manager instance provisioned by this solution. If a prefix input variable is specified, it is added to the value in the `-value` format. Applies only if `existing_secrets_manager_crn` is not provided. | `string` | `"secrets-manager"` | no | +| [secrets\_manager\_resource\_tags](#input\_secrets\_manager\_resource\_tags) | The list of resource tags you want to associate with your Secrets Manager instance. Applies only if `existing_secrets_manager_crn` is not provided. | `list(any)` | `[]` | no | +| [service\_plan](#input\_service\_plan) | The pricing plan to use when provisioning a Secrets Manager instance. Possible values: `standard`, `trial`. | `string` | `"standard"` | no | +| [skip\_event\_notifications\_iam\_authorization\_policy](#input\_skip\_event\_notifications\_iam\_authorization\_policy) | If set to true, this skips the creation of a service to service authorization from Secrets Manager to Event Notifications. If false, the service to service authorization is created. | `bool` | `false` | no | +| [skip\_sm\_ce\_iam\_authorization\_policy](#input\_skip\_sm\_ce\_iam\_authorization\_policy) | Whether to skip the creation of the IAM authorization policies required to enable the IAM credentials engine. If set to false, policies will be created that grants the Secrets Manager instance 'Operator' access to the IAM identity service, and 'Groups Service Member Manage' access to the IAM groups service. | `bool` | `false` | no | +| [skip\_sm\_kms\_iam\_authorization\_policy](#input\_skip\_sm\_kms\_iam\_authorization\_policy) | Set to true to skip the creation of an IAM authorization policy that permits all Secrets Manager instances in the resource group to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the `existing_kms_instance_crn` variable. If a value is specified for `ibmcloud_kms_api_key`, the policy is created in the KMS account. | `bool` | `false` | no | + +### Outputs + +| Name | Description | +|------|-------------| +| [resource\_group\_id](#output\_resource\_group\_id) | Resource group ID | +| [resource\_group\_name](#output\_resource\_group\_name) | Resource group name | +| [secrets\_manager\_crn](#output\_secrets\_manager\_crn) | CRN of Secrets Manager instance. | +| [secrets\_manager\_guid](#output\_secrets\_manager\_guid) | GUID of Secrets Manager instance | +| [secrets\_manager\_id](#output\_secrets\_manager\_id) | ID of Secrets Manager instance. | + diff --git a/solutions/standard/catalogValidationValues.json.template b/solutions/security-enforced/catalogValidationValues.json.template similarity index 62% rename from solutions/standard/catalogValidationValues.json.template rename to solutions/security-enforced/catalogValidationValues.json.template index 5a9fe734..c5b44365 100644 --- a/solutions/standard/catalogValidationValues.json.template +++ b/solutions/security-enforced/catalogValidationValues.json.template @@ -1,6 +1,7 @@ { "ibmcloud_api_key": $VALIDATION_APIKEY, - "resource_group_name": $PREFIX, + "existing_resource_group_name": "geretain-test-secrets-manager", + "prefix": $PREFIX, "service_plan": "trial", "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN, "region": "eu-de" diff --git a/solutions/security-enforced/main.tf b/solutions/security-enforced/main.tf new file mode 100644 index 00000000..b7174343 --- /dev/null +++ b/solutions/security-enforced/main.tf @@ -0,0 +1,29 @@ +module "secrets_manager" { + source = "../fully-configurable" + ibmcloud_api_key = var.ibmcloud_api_key + existing_resource_group_name = var.existing_resource_group_name + prefix = var.prefix + provider_visibility = "private" + region = var.region + secrets_manager_instance_name = var.secrets_manager_instance_name + existing_secrets_manager_crn = var.existing_secrets_manager_crn + service_plan = var.service_plan + skip_sm_ce_iam_authorization_policy = var.skip_sm_ce_iam_authorization_policy + secrets_manager_resource_tags = var.secrets_manager_resource_tags + secrets_manager_endpoint_type = "private" + allowed_network = "private-only" + skip_sm_kms_iam_authorization_policy = var.skip_sm_kms_iam_authorization_policy + existing_secrets_manager_kms_key_crn = var.existing_secrets_manager_kms_key_crn + kms_encryption_enabled = true + existing_kms_instance_crn = var.existing_kms_instance_crn + kms_endpoint_type = "private" + kms_key_ring_name = var.kms_key_ring_name + kms_key_name = var.kms_key_name + ibmcloud_kms_api_key = var.ibmcloud_kms_api_key + existing_event_notifications_instance_crn = var.existing_event_notifications_instance_crn + skip_event_notifications_iam_authorization_policy = var.skip_event_notifications_iam_authorization_policy + event_notifications_email_list = var.event_notifications_email_list + event_notifications_from_email = var.event_notifications_from_email + event_notifications_reply_to_email = var.event_notifications_reply_to_email + secrets_manager_cbr_rules = var.secrets_manager_cbr_rules +} diff --git a/solutions/security-enforced/outputs.tf b/solutions/security-enforced/outputs.tf new file mode 100644 index 00000000..d10dfacc --- /dev/null +++ b/solutions/security-enforced/outputs.tf @@ -0,0 +1,24 @@ +output "resource_group_name" { + description = "Resource group name" + value = module.secrets_manager.resource_group_name +} + +output "resource_group_id" { + description = "Resource group ID" + value = module.secrets_manager.resource_group_id +} + +output "secrets_manager_guid" { + description = "GUID of Secrets Manager instance" + value = module.secrets_manager.secrets_manager_guid +} + +output "secrets_manager_id" { + description = "ID of Secrets Manager instance." + value = module.secrets_manager.secrets_manager_id +} + +output "secrets_manager_crn" { + description = "CRN of Secrets Manager instance." + value = module.secrets_manager.secrets_manager_crn +} diff --git a/solutions/security-enforced/provider.tf b/solutions/security-enforced/provider.tf new file mode 100644 index 00000000..4c6add22 --- /dev/null +++ b/solutions/security-enforced/provider.tf @@ -0,0 +1 @@ +# Explicit provider config not required here as provider config in fully-configurable is used diff --git a/solutions/security-enforced/variables.tf b/solutions/security-enforced/variables.tf new file mode 100644 index 00000000..ad308e4a --- /dev/null +++ b/solutions/security-enforced/variables.tf @@ -0,0 +1,192 @@ +######################################################################################################################## +# Common variables +######################################################################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key used to provision resources." + sensitive = true +} + +variable "existing_resource_group_name" { + type = string + description = "The name of an existing resource group to provision resource in." + default = "Default" + nullable = false +} + +variable "region" { + type = string + description = "The region to provision resources to." + default = "us-south" +} + +variable "prefix" { + type = string + description = "The prefix to add to all resources created by this solution. To not use any prefix value, you can set this value to `null` or an empty string." + + validation { + condition = (var.prefix == null ? true : + alltrue([ + can(regex("^[a-z]{0,1}[-a-z0-9]{0,14}[a-z0-9]{0,1}$", var.prefix)), + length(regexall("^.*--.*", var.prefix)) == 0 + ]) + ) + error_message = "Prefix must begin with a lowercase letter, contain only lowercase letters, numbers, and - characters. Prefixes must end with a lowercase letter or number and be 16 or fewer characters." + } +} + +######################################################################################################################## +# Secrets Manager +######################################################################################################################## + +variable "secrets_manager_instance_name" { + type = string + description = "The name to give the Secrets Manager instance provisioned by this solution. If a prefix input variable is specified, it is added to the value in the `-value` format. Applies only if `existing_secrets_manager_crn` is not provided." + default = "secrets-manager" +} + +variable "existing_secrets_manager_crn" { + type = string + description = "The CRN of an existing Secrets Manager instance. If not supplied, a new instance is created." + default = null +} + +variable "service_plan" { + type = string + description = "The pricing plan to use when provisioning a Secrets Manager instance. Possible values: `standard`, `trial`." + default = "standard" + validation { + condition = contains(["standard", "trial"], var.service_plan) + error_message = "Only \"standard\" and \"trial\" are allowed values for secrets_manager_service_plan.Applies only if not providing a value for the `existing_secrets_manager_crn` input." + } +} + +variable "skip_sm_ce_iam_authorization_policy" { + type = bool + description = "Whether to skip the creation of the IAM authorization policies required to enable the IAM credentials engine. If set to false, policies will be created that grants the Secrets Manager instance 'Operator' access to the IAM identity service, and 'Groups Service Member Manage' access to the IAM groups service." + default = false +} + +variable "secrets_manager_resource_tags" { + type = list(any) + description = "The list of resource tags you want to associate with your Secrets Manager instance. Applies only if `existing_secrets_manager_crn` is not provided." + default = [] +} + +######################################################################################################################## +# Key Protect +######################################################################################################################## + +variable "skip_sm_kms_iam_authorization_policy" { + type = bool + description = "Set to true to skip the creation of an IAM authorization policy that permits all Secrets Manager instances in the resource group to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the `existing_kms_instance_crn` variable. If a value is specified for `ibmcloud_kms_api_key`, the policy is created in the KMS account." + default = false +} + +variable "existing_secrets_manager_kms_key_crn" { + type = string + description = "The CRN of a Key Protect or Hyper Protect Crypto Services key to use for Secrets Manager. If not specified, a key ring and key are created." + default = null +} + +######################################################################################################################## +# KMS properties required when creating an encryption key, rather than passing an existing key CRN. +######################################################################################################################## + +variable "existing_kms_instance_crn" { + type = string + default = null + description = "The CRN of the KMS instance (Hyper Protect Crypto Services or Key Protect). Required only if `existing_secrets_manager_crn` or `existing_secrets_manager_kms_key_crn` is not specified. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`." + + validation { + condition = anytrue([ + can(regex("^crn:(.*:){3}(kms|hs-crypto):(.*:){2}[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}::$", var.existing_kms_instance_crn)), + var.existing_kms_instance_crn == null, + ]) + error_message = "The provided KMS instance CRN in the input 'existing_kms_instance_crn' in not valid." + } + + validation { + condition = var.existing_kms_instance_crn != null ? var.existing_secrets_manager_crn == null : true + error_message = "A value should not be passed for 'existing_kms_instance_crn' when passing an existing secrets manager instance using the 'existing_secrets_manager_crn' input." + } +} + +variable "kms_key_ring_name" { + type = string + default = "secrets-manager-key-ring" + description = "The name for the new key ring to store the key. Applies only if `existing_secrets_manager_kms_key_crn` is not specified. If a prefix input variable is passed, it is added to the value in the `-value` format. ." +} + +variable "kms_key_name" { + type = string + default = "secrets-manager-key" + description = "The name for the new root key. Applies only if `existing_secrets_manager_kms_key_crn` is not specified. If a prefix input variable is passed, it is added to the value in the `-value` format." +} + +variable "ibmcloud_kms_api_key" { + type = string + description = "The IBM Cloud API key that can create a root key and key ring in the key management service (KMS) instance. If not specified, the 'ibmcloud_api_key' variable is used. Specify this key if the instance in `existing_kms_instance_crn` is in an account that's different from the Secrets Manager instance. Leave this input empty if the same account owns both instances." + sensitive = true + default = null +} + +######################################################################################################################## +# Event Notifications +######################################################################################################################## + +variable "existing_event_notifications_instance_crn" { + type = string + description = "The CRN of the Event Notifications service used to enable lifecycle notifications for your Secrets Manager instance." + default = null +} + +variable "skip_event_notifications_iam_authorization_policy" { + type = bool + description = "If set to true, this skips the creation of a service to service authorization from Secrets Manager to Event Notifications. If false, the service to service authorization is created." + default = false +} + +variable "event_notifications_email_list" { + type = list(string) + description = "The list of email address to target out when Secrets Manager triggers an event" + default = [] +} + +variable "event_notifications_from_email" { + type = string + description = "The email address used to send any Secrets Manager event coming via Event Notifications" + default = "compliancealert@ibm.com" +} + +variable "event_notifications_reply_to_email" { + type = string + description = "The email address specified in the 'reply_to' section for any Secret Manager event coming via Event Notifications" + default = "no-reply@ibm.com" +} + +############################################################## +# Context-based restriction (CBR) +############################################################## + +variable "secrets_manager_cbr_rules" { + type = list(object({ + description = string + account_id = string + rule_contexts = list(object({ + attributes = optional(list(object({ + name = string + value = string + }))) })) + enforcement_mode = string + operations = optional(list(object({ + api_types = list(object({ + api_type_id = string + })) + }))) + })) + description = "(Optional, list) List of CBR rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/blob/main/solutions/fully-configurable/DA-cbr_rules.md)" + default = [] + # Validation happens in the rule module +} diff --git a/solutions/security-enforced/version.tf b/solutions/security-enforced/version.tf new file mode 100644 index 00000000..70b38b5b --- /dev/null +++ b/solutions/security-enforced/version.tf @@ -0,0 +1,6 @@ +terraform { + required_version = ">= 1.9.0" + # Lock DA into an exact provider version - renovate automation will keep it updated + required_providers { + } +} diff --git a/solutions/standard/README.md b/solutions/standard/README.md deleted file mode 100644 index a34dc2f7..00000000 --- a/solutions/standard/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Secrets Manager solution - -This solution supports the following: -- Creating a new resource group, or taking in an existing one. -- Provisioning and configuring of a Secrets Manager instance. -- Optionally configure an IBM Secrets Manager IAM credentials engine to an IBM Secrets Manager instance. -- Configuring KMS encryption using a newly created key, or passing an existing key. - -![secret-manager-deployable-architecture](../../reference-architecture/secrets_manager.svg) - -**NB:** This solution is not intended to be called by one or more other modules since it contains a provider configurations, meaning it is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers) diff --git a/solutions/standard/moved.tf b/solutions/standard/moved.tf deleted file mode 100644 index 4064d8b9..00000000 --- a/solutions/standard/moved.tf +++ /dev/null @@ -1,14 +0,0 @@ -moved { - from = module.secrets_manager.ibm_resource_instance.secrets_manager_instance - to = module.secrets_manager.module.secrets_manager.ibm_resource_instance.secrets_manager_instance -} - -moved { - from = module.secrets_manager.ibm_iam_authorization_policy.kms_policy - to = module.secrets_manager.module.secrets_manager.ibm_iam_authorization_policy.kms_policy -} - -moved { - from = module.secrets_manager.time_sleep.wait_for_authorization_policy - to = module.secrets_manager.module.secrets_manager.time_sleep.wait_for_authorization_policy -} diff --git a/tests/pr_test.go b/tests/pr_test.go index 02678123..d2300c18 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -9,8 +9,6 @@ import ( "strings" "testing" - "github.com/IBM/go-sdk-core/v5/core" - "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2" "github.com/gruntwork-io/terratest/modules/files" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/random" @@ -24,9 +22,10 @@ import ( const completeExampleTerraformDir = "examples/complete" const fscloudExampleTerraformDir = "examples/fscloud" -const solutionsTerraformDir = "solutions/standard" +const fullyConfigurableTerraformDir = "solutions/fully-configurable" +const securityEnforcedTerraformDir = "solutions/security-enforced" -const resourceGroup = "geretain-test-scc-module" +const resourceGroup = "geretain-test-secrets-manager" // Define a struct with fields that match the structure of the YAML data const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-resources.yaml" @@ -70,28 +69,21 @@ func setupOptions(t *testing.T, prefix string, checkApplyResultForUpgrade bool) return options } -func TestRunDASolutionSchematics(t *testing.T) { - t.Parallel() - - acme_letsencrypt_private_key := GetSecretsManagerKey( // pragma: allowlist secret - permanentResources["acme_letsencrypt_private_key_sm_id"].(string), - permanentResources["acme_letsencrypt_private_key_sm_region"].(string), - permanentResources["acme_letsencrypt_private_key_secret_id"].(string), - ) +func TestRunFullyConfigurableSchematics(t *testing.T) { // Set up a schematics test options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ Testing: t, TarIncludePatterns: []string{ "*.tf", - fmt.Sprintf("%s/*.tf", solutionsTerraformDir), + fmt.Sprintf("%s/*.tf", fullyConfigurableTerraformDir), fmt.Sprintf("%s/*.tf", fscloudExampleTerraformDir), fmt.Sprintf("%s/*.tf", "modules/secrets"), fmt.Sprintf("%s/*.tf", "modules/fscloud"), }, - TemplateFolder: solutionsTerraformDir, + TemplateFolder: fullyConfigurableTerraformDir, ResourceGroup: resourceGroup, - Prefix: "sm-da", + Prefix: "sm-fc", Tags: []string{"test-schematic"}, DeleteWorkspaceOnFail: false, WaitJobCompleteMinutes: 60, @@ -101,53 +93,22 @@ func TestRunDASolutionSchematics(t *testing.T) { {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, {Name: "prefix", Value: options.Prefix, DataType: "string"}, {Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"}, - {Name: "resource_group_name", Value: options.Prefix, DataType: "string"}, + {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, {Name: "service_plan", Value: "trial", DataType: "string"}, - {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, - {Name: "iam_engine_enabled", Value: true, DataType: "bool"}, - {Name: "public_engine_enabled", Value: true, DataType: "bool"}, - {Name: "private_engine_enabled", Value: true, DataType: "bool"}, - {Name: "cis_id", Value: permanentResources["cisInstanceId"], DataType: "string"}, - {Name: "ca_name", Value: permanentResources["certificateAuthorityName"], DataType: "string"}, - {Name: "dns_provider_name", Value: permanentResources["dnsProviderName"], DataType: "string"}, - {Name: "acme_letsencrypt_private_key", Value: *acme_letsencrypt_private_key, DataType: "string"}, } err := options.RunSchematicTest() assert.NoError(t, err, "Schematic Test had unexpected error") } -func GetSecretsManagerKey(sm_id string, sm_region string, sm_key_id string) *string { - secretsManagerService, err := secretsmanagerv2.NewSecretsManagerV2(&secretsmanagerv2.SecretsManagerV2Options{ - URL: fmt.Sprintf("https://%s.%s.secrets-manager.appdomain.cloud", sm_id, sm_region), - Authenticator: &core.IamAuthenticator{ - ApiKey: os.Getenv("TF_VAR_ibmcloud_api_key"), - }, - }) - if err != nil { - panic(err) - } - - getSecretOptions := secretsManagerService.NewGetSecretOptions( - sm_key_id, - ) - - secret, _, err := secretsManagerService.GetSecret(getSecretOptions) - if err != nil { - panic(err) - } - return secret.(*secretsmanagerv2.ArbitrarySecret).Payload -} - -// A test to pass existing resources to the SM DA -func TestRunExistingResourcesInstances(t *testing.T) { +func TestRunExistingResourcesInstancesFullyConfigurable(t *testing.T) { t.Parallel() // ------------------------------------------------------------------------------------ // Provision Event Notification, KMS key and resource group first // ------------------------------------------------------------------------------------ region := validRegions[rand.Intn(len(validRegions))] - prefix := fmt.Sprintf("scc-exist-%s", strings.ToLower(random.UniqueId())) + prefix := fmt.Sprintf("sm-exist-%s", strings.ToLower(random.UniqueId())) realTerraformDir := ".." tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) tags := common.GetTagsFromTravis() @@ -183,12 +144,13 @@ func TestRunExistingResourcesInstances(t *testing.T) { Testing: t, TarIncludePatterns: []string{ "*.tf", - fmt.Sprintf("%s/*.tf", solutionsTerraformDir), + fmt.Sprintf("%s/*.tf", fullyConfigurableTerraformDir), fmt.Sprintf("%s/*.tf", "modules/secrets"), fmt.Sprintf("%s/*.tf", "modules/fscloud"), }, - TemplateFolder: solutionsTerraformDir, + TemplateFolder: fullyConfigurableTerraformDir, ResourceGroup: resourceGroup, + Prefix: "ex-fc", Tags: []string{"test-schematic"}, DeleteWorkspaceOnFail: false, WaitJobCompleteMinutes: 60, @@ -196,17 +158,13 @@ func TestRunExistingResourcesInstances(t *testing.T) { options.TerraformVars = []testschematic.TestSchematicTerraformVar{ {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, {Name: "region", Value: region, DataType: "string"}, - {Name: "resource_group_name", Value: terraform.Output(t, existingTerraformOptions, "resource_group_name"), DataType: "string"}, - {Name: "use_existing_resource_group", Value: true, DataType: "bool"}, - {Name: "enable_event_notification", Value: true, DataType: "bool"}, + {Name: "existing_resource_group_name", Value: terraform.Output(t, existingTerraformOptions, "resource_group_name"), DataType: "string"}, {Name: "existing_event_notification_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "event_notification_instance_crn"), DataType: "string"}, {Name: "existing_secrets_manager_kms_key_crn", Value: permanentResources["hpcs_south_root_key_crn"], DataType: "string"}, - {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, + {Name: "kms_encryption_enabled", Value: true, DataType: "bool"}, {Name: "service_plan", Value: "trial", DataType: "string"}, - {Name: "iam_engine_enabled", Value: true, DataType: "bool"}, - {Name: "private_engine_enabled", Value: true, DataType: "bool"}, - {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, } err := options.RunSchematicTest() @@ -226,28 +184,85 @@ func TestRunExistingResourcesInstances(t *testing.T) { } } -func TestRunSecretsManagerSolutionUpgradeSchematic(t *testing.T) { - t.Parallel() +func TestRunExistingSMInstanceFullyConfigurable(t *testing.T) { + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + TarIncludePatterns: []string{ + "*.tf", + fmt.Sprintf("%s/*.tf", fullyConfigurableTerraformDir), + fmt.Sprintf("%s/*.tf", "modules/secrets"), + fmt.Sprintf("%s/*.tf", "modules/fscloud"), + }, + TemplateFolder: fullyConfigurableTerraformDir, + ResourceGroup: resourceGroup, + Prefix: "ex-scm", + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + }) + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"}, + {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, + {Name: "existing_secrets_manager_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"}, + {Name: "service_plan", Value: "trial", DataType: "string"}, + } + + err := options.RunSchematicTest() + assert.NoError(t, err, "Schematic Test had unexpected error") +} - acme_letsencrypt_private_key := GetSecretsManagerKey( // pragma: allowlist secret - permanentResources["acme_letsencrypt_private_key_sm_id"].(string), - permanentResources["acme_letsencrypt_private_key_sm_region"].(string), - permanentResources["acme_letsencrypt_private_key_secret_id"].(string), - ) +func TestRunSecurityEnforcedSchematics(t *testing.T) { // Set up a schematics test options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ Testing: t, TarIncludePatterns: []string{ "*.tf", - fmt.Sprintf("%s/*.tf", solutionsTerraformDir), + fmt.Sprintf("%s/*.tf", securityEnforcedTerraformDir), + fmt.Sprintf("%s/*.tf", fullyConfigurableTerraformDir), fmt.Sprintf("%s/*.tf", fscloudExampleTerraformDir), fmt.Sprintf("%s/*.tf", "modules/secrets"), fmt.Sprintf("%s/*.tf", "modules/fscloud"), }, - TemplateFolder: solutionsTerraformDir, + TemplateFolder: securityEnforcedTerraformDir, + ResourceGroup: resourceGroup, + Prefix: "sm-se", + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + }) + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"}, + {Name: "existing_resource_group_name", Value: "geretain-test-secrets-manager", DataType: "string"}, + {Name: "service_plan", Value: "trial", DataType: "string"}, + {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, + } + + err := options.RunSchematicTest() + assert.NoError(t, err, "Schematic Test had unexpected error") +} + +func TestRunSecretsManagerSecurityEnforcedUpgradeSchematic(t *testing.T) { + + // Set up a schematics test + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + TarIncludePatterns: []string{ + "*.tf", + fmt.Sprintf("%s/*.tf", securityEnforcedTerraformDir), + fmt.Sprintf("%s/*.tf", fullyConfigurableTerraformDir), + fmt.Sprintf("%s/*.tf", "modules/secrets"), + fmt.Sprintf("%s/*.tf", "modules/fscloud"), + }, + TemplateFolder: securityEnforcedTerraformDir, ResourceGroup: resourceGroup, - Prefix: "sm-da", + Prefix: "sm-se-ug", Tags: []string{"test-schematic"}, DeleteWorkspaceOnFail: false, WaitJobCompleteMinutes: 60, @@ -257,16 +272,9 @@ func TestRunSecretsManagerSolutionUpgradeSchematic(t *testing.T) { {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, {Name: "prefix", Value: options.Prefix, DataType: "string"}, {Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"}, - {Name: "resource_group_name", Value: options.Prefix, DataType: "string"}, + {Name: "existing_resource_group_name", Value: "geretain-test-secrets-manager", DataType: "string"}, {Name: "service_plan", Value: "trial", DataType: "string"}, {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, - {Name: "iam_engine_enabled", Value: true, DataType: "bool"}, - {Name: "public_engine_enabled", Value: true, DataType: "bool"}, - {Name: "private_engine_enabled", Value: true, DataType: "bool"}, - {Name: "cis_id", Value: permanentResources["cisInstanceId"], DataType: "string"}, - {Name: "ca_name", Value: permanentResources["certificateAuthorityName"], DataType: "string"}, - {Name: "dns_provider_name", Value: permanentResources["dnsProviderName"], DataType: "string"}, - {Name: "acme_letsencrypt_private_key", Value: *acme_letsencrypt_private_key, DataType: "string"}, } err := options.RunSchematicUpgradeTest()