From 9931045632c13926067289739415ac180394690e Mon Sep 17 00:00:00 2001 From: Khuzaima Shakeel <56439894+Khuzaima05@users.noreply.github.com> Date: Wed, 29 Nov 2023 22:07:16 +0530 Subject: [PATCH] feat: initial release (#1) --- .github/settings.yml | 6 +- .secrets.baseline | 15 +++- README.md | 103 ++++++++++++----------- common-dev-assets | 2 +- examples/basic/README.md | 13 ++- examples/basic/main.tf | 22 ++--- examples/basic/outputs.tf | 20 +---- examples/basic/provider.tf | 4 - examples/basic/variables.tf | 20 ++--- examples/basic/version.tf | 6 +- examples/complete-private/README.md | 5 ++ examples/complete-private/main.tf | 75 +++++++++++++++++ examples/complete-private/outputs.tf | 4 + examples/complete-private/provider.tf | 4 + examples/complete-private/variables.tf | 52 ++++++++++++ examples/complete-private/version.tf | 10 +++ examples/complete/README.md | 43 +++++++++- examples/complete/main.tf | 34 +++++++- examples/complete/outputs.tf | 25 +----- examples/complete/provider.tf | 4 - examples/complete/variables.tf | 19 +++-- examples/complete/version.tf | 7 +- main.tf | 108 ++++++++++++++++++++++++- outputs.tf | 14 ++-- tests/other_test.go | 9 ++- tests/pr_test.go | 72 ++++++++++++++--- variables.tf | 93 +++++++++++++++++++-- version.tf | 22 ++--- 28 files changed, 612 insertions(+), 199 deletions(-) create mode 100644 examples/complete-private/README.md create mode 100644 examples/complete-private/main.tf create mode 100644 examples/complete-private/outputs.tf create mode 100644 examples/complete-private/provider.tf create mode 100644 examples/complete-private/variables.tf create mode 100644 examples/complete-private/version.tf diff --git a/.github/settings.yml b/.github/settings.yml index 7b03ead..8481609 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -15,14 +15,14 @@ repository: # By changing this field, you rename the repository. # Uncomment this name property and set the name to the current repo name. - # name: "" + name: "terraform-ibm-secrets-manager" # The description is displayed under the repository name on the # organization page and in the 'About' section of the repository. # Uncomment this description property # and update the description to the current repo description. - # description: "" + description: "This module creates a Secrets Manager instance" # Use a comma-separated list of topics to set on the repo (ensure not to use any caps in the topic string). - topics: terraform, ibm-cloud, terraform-module + topics: core-team, terraform, ibm-cloud, terraform-module, secrets-manager, supported, graduated diff --git a/.secrets.baseline b/.secrets.baseline index 05cf1cd..27ac66b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2023-06-03T04:42:19Z", + "generated_at": "2023-11-29T09:46:00Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -76,7 +76,18 @@ "name": "TwilioKeyDetector" } ], - "results": {}, + "results": { + "README.md": [ + { + "hashed_secret": "33da8d0e8af2efc260f01d8e5edfcc5c5aba44ad", + "is_secret": true, + "is_verified": false, + "line_number": 32, + "type": "Secret Keyword", + "verified_result": null + } + ] + }, "version": "0.13.1+ibm.61.dss", "word_list": { "file": null, diff --git a/README.md b/README.md index c8fd64b..70f773d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,14 @@ # Secrets Manager module - -[![Incubating (Not yet consumable)](https://img.shields.io/badge/status-Incubating%20(Not%20yet%20consumable)-red)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) + +[![Graduated (Supported)](https://img.shields.io/badge/Status-Graduated%20(Supported)-brightgreen)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) [![latest release](https://img.shields.io/github/v/release/terraform-ibm-modules/terraform-ibm-secrets-manager?logo=GitHub&sort=semver)](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager/releases/latest) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) -TODO: Replace me with description of the module(s) in this repo +This module is used to provision and configure an IBM Cloud [Secrets Manager](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-getting-started) instance. @@ -21,64 +17,43 @@ TODO: Replace me with description of the module(s) in this repo * [terraform-ibm-secrets-manager](#terraform-ibm-secrets-manager) * [Examples](./examples) * [Basic example](./examples/basic) - * [Complete example](./examples/complete) + * [Complete example with BYOK encryption](./examples/complete) + * [Complete example with private only instance and KYOK encryption](./examples/complete-private) * [Contributing](#contributing) - - - - - ## terraform-ibm-secrets-manager ### Usage - - ```hcl - +provider "ibm" { + ibmcloud_api_key = "XXXXXXXXXXXXXX" + region = "us-south" +} +module "secrets_manager" { + source = "terraform-ibm-modules/secrets-manager/ibm" + version = "X.X.X" # Replace "X.X.X" with a release version to lock into a specific release + resource_group_id = "xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX" + region = "us-south" + secrets_manager_name = "my-secrets-manager" + sm_service_plan = "trial" + service_endpoints = "public-and-private" +} ``` -### Required IAM access policies - - - - - - - + - `Manager` service access @@ -87,23 +62,45 @@ statement instead the previous block. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.3.0, <1.6.0 | +| [terraform](#requirement\_terraform) | >= v1.0.0, <1.6.0 | +| [ibm](#requirement\_ibm) | >= 1.56.1, < 2.0.0 | +| [time](#requirement\_time) | >= 0.9.1, < 1.0.0 | ### Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [cbr\_rule](#module\_cbr\_rule) | terraform-ibm-modules/cbr/ibm//modules/cbr-rule-module | 1.16.0 | ### Resources -No resources. +| Name | Type | +|------|------| +| [ibm_iam_authorization_policy.policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource | +| [ibm_resource_instance.secrets_manager_instance](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/resource_instance) | resource | +| [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | ### Inputs -No inputs. +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cbr\_rules](#input\_cbr\_rules) | (Optional, list) List of CBR rules to create |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
}))
| `[]` | no | +| [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Hyper Protect Crypto Services or Key Protect instance in which the key specified in `kms_key_crn` is coming from. Required only if `kms_encryption_enabled` is set to true, and `skip_iam_authorization_policy` is set to false. | `string` | `null` | no | +| [kms\_encryption\_enabled](#input\_kms\_encryption\_enabled) | Set this to true to control the encryption keys used to encrypt the data that you store in Secrets Manager. If set to false, the data that you store is encrypted at rest by using envelope encryption. For more details, see https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-mng-data&interface=ui#about-encryption. | `bool` | `false` | no | +| [kms\_key\_crn](#input\_kms\_key\_crn) | The root key CRN of a Key Management Service like Key Protect or Hyper Protect Crypto Services (HPCS) that you want to use for encryption. Only used if `kms_encryption_enabled` is set to true. | `string` | `null` | no | +| [region](#input\_region) | The region to provision the Secrets Manager instance to. | `string` | n/a | yes | +| [resource\_group\_id](#input\_resource\_group\_id) | The ID of the resource group to provision the Secrets Manager instance to. | `string` | n/a | yes | +| [secrets\_manager\_name](#input\_secrets\_manager\_name) | The name to give the Secrets Manager instance. | `string` | n/a | yes | +| [service\_endpoints](#input\_service\_endpoints) | The types of service endpoints to set on the Secrets Manager instance. Possible values are `public`, `private` or `public-and-private`. | `string` | `"public-and-private"` | no | +| [skip\_iam\_authorization\_policy](#input\_skip\_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_guid` variable. In addition, no policy is created if `kms_encryption_enabled` is set to false. | `bool` | `false` | no | +| [sm\_service\_plan](#input\_sm\_service\_plan) | The Secrets Manager plan to provision. | `string` | `"standard"` | no | +| [sm\_tags](#input\_sm\_tags) | The list of resource tags that you want to associate with your Secrets Manager instance. | `list(string)` | `[]` | no | ### Outputs -No outputs. +| Name | Description | +|------|-------------| +| [secrets\_manager\_guid](#output\_secrets\_manager\_guid) | GUID of Secrets-Manager instance | diff --git a/common-dev-assets b/common-dev-assets index b224509..2f74f8e 160000 --- a/common-dev-assets +++ b/common-dev-assets @@ -1 +1 @@ -Subproject commit b2245098f2b3d23be7b9ed89e734f8766fb96696 +Subproject commit 2f74f8ee898fb1929307145ecbf002847873db25 diff --git a/examples/basic/README.md b/examples/basic/README.md index 86eab8e..aaf1daa 100644 --- a/examples/basic/README.md +++ b/examples/basic/README.md @@ -1,11 +1,8 @@ # Basic example - +A simple example that shows how to provision a basic Trial instance of Secrets Manager. -An end-to-end basic example that will provision the following: -- A new resource group if one is not passed in. -- A new Cloud Object Storage instance. +The following resources are provisioned by this example: + +- A new resource group, if an existing one is not passed in. +- A Trial instance of Secrets Manager. diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 4aeca62..f84bf3b 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -1,7 +1,3 @@ -######################################################################################################################## -# Resource group -######################################################################################################################## - module "resource_group" { source = "terraform-ibm-modules/resource-group/ibm" version = "1.1.4" @@ -10,15 +6,11 @@ module "resource_group" { existing_resource_group_name = var.resource_group } -######################################################################################################################## -# COS instance -######################################################################################################################## - -resource "ibm_resource_instance" "cos_instance" { - name = "${var.prefix}-cos" - resource_group_id = module.resource_group.resource_group_id - service = "cloud-object-storage" - plan = "standard" - location = "global" - tags = var.resource_tags +module "secrets_manager" { + source = "../.." + resource_group_id = module.resource_group.resource_group_id + region = var.region + secrets_manager_name = "${var.prefix}-secrets-manager" #tfsec:ignore:general-secrets-no-plaintext-exposure + sm_service_plan = "trial" + sm_tags = var.resource_tags } diff --git a/examples/basic/outputs.tf b/examples/basic/outputs.tf index 04b196e..f85ccd3 100644 --- a/examples/basic/outputs.tf +++ b/examples/basic/outputs.tf @@ -1,18 +1,4 @@ -######################################################################################################################## -# Outputs -######################################################################################################################## - -output "cos_instance_id" { - description = "COS instance id" - value = ibm_resource_instance.cos_instance.id -} - -output "resource_group_name" { - description = "Resource group name" - value = module.resource_group.resource_group_name -} - -output "resource_group_id" { - description = "Resource group ID" - value = module.resource_group.resource_group_id +output "secrets_manager_guid" { + value = module.secrets_manager.secrets_manager_guid + description = "GUID of Secrets Manager instance." } diff --git a/examples/basic/provider.tf b/examples/basic/provider.tf index 84b6985..df45ef5 100644 --- a/examples/basic/provider.tf +++ b/examples/basic/provider.tf @@ -1,7 +1,3 @@ -######################################################################################################################## -# Provider config -######################################################################################################################## - provider "ibm" { ibmcloud_api_key = var.ibmcloud_api_key region = var.region diff --git a/examples/basic/variables.tf b/examples/basic/variables.tf index dd0d0af..dc0b9d6 100644 --- a/examples/basic/variables.tf +++ b/examples/basic/variables.tf @@ -1,28 +1,24 @@ -######################################################################################################################## -# Input variables -######################################################################################################################## - variable "ibmcloud_api_key" { type = string - description = "The IBM Cloud API Key" + description = "The IBM Cloud API key this account authenticates to" sensitive = true } -variable "region" { +variable "prefix" { type = string - description = "Region to provision all resources created by this example" - default = "us-south" + description = "Prefix for sm instance" + default = "secrets-manager-test" } -variable "prefix" { +variable "region" { type = string - description = "Prefix to append to all resources created by this example" - default = "basic" + description = "Region where resources will be created" + default = "us-east" } variable "resource_group" { type = string - description = "The name of an existing resource group to provision resources in to. If not set a new resource group will be created using the prefix variable" + description = "An existing resource group name to use for this example, if unset a new resource group will be created" default = null } diff --git a/examples/basic/version.tf b/examples/basic/version.tf index 46915a6..3dce87c 100644 --- a/examples/basic/version.tf +++ b/examples/basic/version.tf @@ -1,12 +1,12 @@ terraform { - required_version = ">= 1.3.0, <1.6.0" + required_version = ">= v1.0.0, <1.6.0" # Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main - # module's version.tf (usually a basic example), and 1 example that will always use the latest provider version. + # module's version.tf (this example), and 1 example that will always use the latest provider version (complete example). required_providers { ibm = { source = "IBM-Cloud/ibm" - version = "1.49.0" + version = "1.56.1" } } } diff --git a/examples/complete-private/README.md b/examples/complete-private/README.md new file mode 100644 index 0000000..8fd2cac --- /dev/null +++ b/examples/complete-private/README.md @@ -0,0 +1,5 @@ +# Complete example with private only instance and KYOK encryption + +This examples handles the provisioning of Secrets-Manager instance, the IAM engine configuration in the recently created instance and a context-based restriction (CBR) rule to only allow Secret Manager to be accessible from within the VPC.. + +Only private service endpoints are enabled, public are disabled. Secrets Manager instances that are private only do not offer a UI management experience. diff --git a/examples/complete-private/main.tf b/examples/complete-private/main.tf new file mode 100644 index 0000000..c155b16 --- /dev/null +++ b/examples/complete-private/main.tf @@ -0,0 +1,75 @@ +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.1.4" + # if an existing resource group is not set (null) create a new one using prefix + resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null + existing_resource_group_name = var.resource_group +} + +############################################################################## +# Get Cloud Account ID +############################################################################## + +data "ibm_iam_account_settings" "iam_account_settings" { +} + +############################################################################## +# VPC +############################################################################## +resource "ibm_is_vpc" "example_vpc" { + name = "${var.prefix}-vpc" + resource_group = module.resource_group.resource_group_id + tags = var.resource_tags +} + +############################################################################## +# Create CBR Zone +############################################################################## +module "cbr_zone" { + source = "terraform-ibm-modules/cbr/ibm//modules/cbr-zone-module" + version = "1.16.0" + name = "${var.prefix}-VPC-network-zone" + zone_description = "CBR Network zone representing VPC" + account_id = data.ibm_iam_account_settings.iam_account_settings.account_id + addresses = [{ + type = "vpc", # to bind a specific vpc to the zone + value = ibm_is_vpc.example_vpc.crn, + }] +} + + +module "secrets_manager" { + source = "../.." + resource_group_id = module.resource_group.resource_group_id + region = var.region + secrets_manager_name = "${var.prefix}-secrets-manager" #tfsec:ignore:general-secrets-no-plaintext-exposure + sm_service_plan = var.sm_service_plan + sm_tags = var.resource_tags + service_endpoints = "private" + kms_encryption_enabled = var.kms_encryption_enabled + existing_kms_instance_guid = var.existing_kms_instance_guid + kms_key_crn = var.kms_key_crn + cbr_rules = [ + { + description = "${var.prefix}-Secrets Manager access only from vpc" + enforcement_mode = "enabled" + account_id = data.ibm_iam_account_settings.iam_account_settings.account_id + rule_contexts = [{ + attributes = [ + { + "name" : "endpointType", + "value" : "private" + }, + { + name = "networkZoneId" + value = module.cbr_zone.zone_id + }] + }] + operations = [{ + api_types = [{ + api_type_id = "crn:v1:bluemix:public:context-based-restrictions::::api-type:" + }] + }] + } + ] +} diff --git a/examples/complete-private/outputs.tf b/examples/complete-private/outputs.tf new file mode 100644 index 0000000..82eedc4 --- /dev/null +++ b/examples/complete-private/outputs.tf @@ -0,0 +1,4 @@ +output "secrets_manager_guid" { + value = module.secrets_manager.secrets_manager_guid + description = "GUID of Secrets-Manager instance in which IAM engine was configured" +} diff --git a/examples/complete-private/provider.tf b/examples/complete-private/provider.tf new file mode 100644 index 0000000..df45ef5 --- /dev/null +++ b/examples/complete-private/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/examples/complete-private/variables.tf b/examples/complete-private/variables.tf new file mode 100644 index 0000000..f0d4047 --- /dev/null +++ b/examples/complete-private/variables.tf @@ -0,0 +1,52 @@ +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API token this account authenticates to" + sensitive = true +} + +variable "prefix" { + type = string + description = "Prefix for sm instance" + default = "secrets-manager-test" +} +variable "sm_service_plan" { + type = string + description = "Secrets-Manager Trial plan" + default = "trial" +} + +variable "region" { + type = string + description = "Region where resources will be created" + default = "us-east" +} + +variable "resource_group" { + type = string + description = "An existing resource group name to use for this example, if unset a new resource group will be created" + default = null +} + +variable "resource_tags" { + type = list(string) + description = "Optional list of tags to be added to created resources" + default = [] +} + +variable "kms_encryption_enabled" { + type = bool + description = "Optional flag to enable KMS encryption" + default = false +} + +variable "existing_kms_instance_guid" { + type = string + description = "GUID of the KMS instance containing the key to use for encryption" + default = null +} + +variable "kms_key_crn" { + type = string + description = "CRN of the KMS key to use for encryption" + default = null +} diff --git a/examples/complete-private/version.tf b/examples/complete-private/version.tf new file mode 100644 index 0000000..8ca8bc9 --- /dev/null +++ b/examples/complete-private/version.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= v1.0.0, <1.6.0" + required_providers { + # Pin to the lowest provider version of the range defined in the main module to ensure lowest version still works + ibm = { + source = "IBM-Cloud/ibm" + version = ">= 1.56.1" + } + } +} diff --git a/examples/complete/README.md b/examples/complete/README.md index 139f8dd..ba51b68 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -1,4 +1,41 @@ -# Complete example +# Complete example with BYOK encryption - - +This examples handles the provisioning of a new Secrets Manager instance. + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= v1.0.0, <1.6.0 | +| [ibm](#requirement\_ibm) | >= 1.56.1 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [key\_protect](#module\_key\_protect) | terraform-ibm-modules/key-protect-all-inclusive/ibm | 4.4.1 | +| [resource\_group](#module\_resource\_group) | terraform-ibm-modules/resource-group/ibm | 1.1.4 | +| [secrets\_manager](#module\_secrets\_manager) | ../.. | n/a | + +### Resources + +No resources. + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key this account authenticates to | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix for sm instance | `string` | `"secrets-manager-test"` | no | +| [region](#input\_region) | Region where resources will be created | `string` | `"us-east"` | no | +| [resource\_group](#input\_resource\_group) | An existing resource group name to use for this example, if unset a new resource group will be created | `string` | `null` | no | +| [resource\_tags](#input\_resource\_tags) | Optional list of tags to be added to created resources | `list(string)` | `[]` | no | +| [sm\_service\_plan](#input\_sm\_service\_plan) | The Secrets Manager service plan to provision | `string` | `"trial"` | no | + +### Outputs + +| Name | Description | +|------|-------------| +| [secrets\_manager\_guid](#output\_secrets\_manager\_guid) | GUID of Secrets Manager instance. | + diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 558c210..f4fbff4 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -1,3 +1,31 @@ -############################################################################## -# Complete example -############################################################################## +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.1.4" + # if an existing resource group is not set (null) create a new one using prefix + resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null + existing_resource_group_name = var.resource_group +} + +module "key_protect" { + source = "terraform-ibm-modules/key-protect-all-inclusive/ibm" + version = "4.4.1" + key_protect_instance_name = "${var.prefix}-key-protect" + resource_group_id = module.resource_group.resource_group_id + region = var.region + key_map = { + "${var.prefix}-sm" = ["${var.prefix}-sm-key"] + } +} + +module "secrets_manager" { + source = "../.." + resource_group_id = module.resource_group.resource_group_id + region = var.region + secrets_manager_name = "${var.prefix}-secrets-manager" #tfsec:ignore:general-secrets-no-plaintext-exposure + sm_service_plan = var.sm_service_plan + sm_tags = var.resource_tags + service_endpoints = "public-and-private" + kms_encryption_enabled = true + existing_kms_instance_guid = module.key_protect.key_protect_guid + kms_key_crn = module.key_protect.keys["${var.prefix}-sm.${var.prefix}-sm-key"].crn +} diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index addadea..f85ccd3 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -1,23 +1,4 @@ -############################################################################## -# Outputs -############################################################################## - -output "region" { - description = "The region all resources were provisioned in" - value = var.region -} - -output "prefix" { - description = "The prefix used to name all provisioned resources" - value = var.prefix -} - -output "resource_group_name" { - description = "The name of the resource group used" - value = var.resource_group -} - -output "resource_tags" { - description = "List of resource tags" - value = var.resource_tags +output "secrets_manager_guid" { + value = module.secrets_manager.secrets_manager_guid + description = "GUID of Secrets Manager instance." } diff --git a/examples/complete/provider.tf b/examples/complete/provider.tf index 2080946..df45ef5 100644 --- a/examples/complete/provider.tf +++ b/examples/complete/provider.tf @@ -1,7 +1,3 @@ -############################################################################## -# Provider config -############################################################################## - provider "ibm" { ibmcloud_api_key = var.ibmcloud_api_key region = var.region diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index 170a5ab..0b25a00 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -1,19 +1,24 @@ variable "ibmcloud_api_key" { type = string - description = "The IBM Cloud API Key" + description = "The IBM Cloud API key this account authenticates to" sensitive = true } -variable "region" { +variable "prefix" { + type = string + description = "Prefix for sm instance" + default = "secrets-manager-test" +} +variable "sm_service_plan" { type = string - description = "Region to provision all resources created by this example" - default = "us-south" + description = "The Secrets Manager service plan to provision" + default = "trial" } -variable "prefix" { +variable "region" { type = string - description = "Prefix to append to all resources created by this example" - default = "complete" + description = "Region where resources will be created" + default = "us-east" } variable "resource_group" { diff --git a/examples/complete/version.tf b/examples/complete/version.tf index 5adb5b5..631e6fd 100644 --- a/examples/complete/version.tf +++ b/examples/complete/version.tf @@ -1,12 +1,9 @@ terraform { - required_version = ">= 1.3.0, <1.6.0" - - # Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main - # module's version.tf (usually a basic example), and 1 example that will always use the latest provider version. + required_version = ">= v1.0.0, <1.6.0" required_providers { ibm = { source = "IBM-Cloud/ibm" - version = ">= 1.49.0, < 2.0.0" + version = ">= 1.56.1" } } } diff --git a/main.tf b/main.tf index 0b919ea..46c027a 100644 --- a/main.tf +++ b/main.tf @@ -1,3 +1,105 @@ -/******************************************************************** -This file is used to implement the ROOT module. -*********************************************************************/ +############################################################################## +# Secrets Manager Module +############################################################################## + +# Validation +locals { + allowed_network = var.service_endpoints == "private" ? "private-only" : "public-and-private" + + # Validation (approach based on https://github.com/hashicorp/terraform/issues/25609#issuecomment-1057614400) + # tflint-ignore: terraform_unused_declarations + validate_kms_values = (!var.kms_encryption_enabled && var.kms_key_crn != null) ? tobool("When passing values for var.kms_key_crn, you must set var.kms_encryption_enabled to true. Otherwise unset them to use default encryption") : (!var.kms_encryption_enabled && var.existing_kms_instance_guid != null) ? tobool("When passing values for var.existing_kms_instance_guid, you must set var.kms_encryption_enabled to true. Otherwise unset them to use default encryption") : true + # tflint-ignore: terraform_unused_declarations + validate_kms_vars = var.kms_encryption_enabled && var.kms_key_crn == null ? tobool("When setting var.kms_encryption_enabled to true, a value must be passed for var.kms_key_crn") : true + # tflint-ignore: terraform_unused_declarations + validate_auth_policy = var.kms_encryption_enabled && var.skip_iam_authorization_policy == false && var.existing_kms_instance_guid == null ? tobool("When var.skip_iam_authorization_policy is set to false, and var.kms_encryption_enabled to true, a value must be passed for var.existing_kms_instance_guid in order to create the auth policy.") : true + +} + +# Create Secrets Manager Instance +resource "ibm_resource_instance" "secrets_manager_instance" { + depends_on = [ibm_iam_authorization_policy.policy] + name = var.secrets_manager_name + service = "secrets-manager" + service_endpoints = var.service_endpoints + plan = var.sm_service_plan + location = var.region + resource_group_id = var.resource_group_id + tags = var.sm_tags + parameters = { + "allowed_network" = local.allowed_network + "kms_instance" = var.existing_kms_instance_guid + "kms_key" = var.kms_key_crn + } + + timeouts { + create = "30m" # Extending provisioning time to 30 minutes + } +} + +locals { + # determine which service name to use for the policy + kms_service_name = var.kms_encryption_enabled && var.kms_key_crn != null ? ( + can(regex(".*kms.*", var.kms_key_crn)) ? "kms" : ( + can(regex(".*hs-crypto.*", var.kms_key_crn)) ? "hs-crypto" : null + ) + ) : null +} + +resource "ibm_iam_authorization_policy" "policy" { + count = var.kms_encryption_enabled && !var.skip_iam_authorization_policy ? 1 : 0 + source_service_name = "secrets-manager" + source_resource_group_id = var.resource_group_id + target_service_name = local.kms_service_name + target_resource_instance_id = var.existing_kms_instance_guid + roles = ["Reader"] + description = "Allow all Secrets Manager instances in the resource group ${var.resource_group_id} to read from the ${local.kms_service_name} instance GUID ${var.existing_kms_instance_guid}" +} + +# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 +resource "time_sleep" "wait_for_authorization_policy" { + depends_on = [ibm_iam_authorization_policy.policy] + + create_duration = "30s" +} + + +locals { + secrets_manager_guid = tolist(ibm_resource_instance.secrets_manager_instance[*].guid)[0] +} + +############################################################################## +# Context Based Restrictions +############################################################################## +module "cbr_rule" { + count = length(var.cbr_rules) > 0 ? length(var.cbr_rules) : 0 + source = "terraform-ibm-modules/cbr/ibm//modules/cbr-rule-module" + version = "1.16.0" + rule_description = var.cbr_rules[count.index].description + enforcement_mode = var.cbr_rules[count.index].enforcement_mode + rule_contexts = var.cbr_rules[count.index].rule_contexts + resources = [{ + attributes = [ + { + name = "accountId" + value = var.cbr_rules[count.index].account_id + operator = "stringEquals" + }, + { + name = "serviceInstance" + value = local.secrets_manager_guid + operator = "stringEquals" + }, + { + name = "serviceName" + value = "secrets-manager" + operator = "stringEquals" + } + ] + }] + operations = [{ + api_types = [{ + api_type_id = "crn:v1:bluemix:public:context-based-restrictions::::api-type:" + }] + }] +} diff --git a/outputs.tf b/outputs.tf index bb6ea66..f88281a 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,8 +1,10 @@ -######################################################################################################################## +############################################################################## # Outputs -######################################################################################################################## +############################################################################## -#output "myoutput" { -# description = "Description of my output" -# value = "value" -#} +output "secrets_manager_guid" { + value = local.secrets_manager_guid + description = "GUID of Secrets-Manager instance" +} + +############################################################################## diff --git a/tests/other_test.go b/tests/other_test.go index d03784f..6463484 100644 --- a/tests/other_test.go +++ b/tests/other_test.go @@ -1,16 +1,21 @@ -// Tests in this file are NOT run in the PR pipeline. They are run in the continuous testing pipeline along with the ones in pr_test.go package test import ( "testing" "github.com/stretchr/testify/assert" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" ) func TestRunBasicExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-basic", "examples/basic") + options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ + Testing: t, + TerraformDir: "examples/basic", + Prefix: "secrets-mgr-def", + ResourceGroup: resourceGroup, + }) output, err := options.RunTestConsistency() assert.Nil(t, err, "This should not have errored") diff --git a/tests/pr_test.go b/tests/pr_test.go index 896d726..41f93af 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -1,31 +1,52 @@ -// Tests in this file are run in the PR pipeline and the continuous testing pipeline +// Tests in this file are run in the PR pipeline package test import ( + "log" + "os" "testing" "github.com/stretchr/testify/assert" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" ) -// Use existing resource group -const resourceGroup = "geretain-test-resources" -const completeExampleDir = "examples/complete" +const resourceGroup = "geretain-test-secrets-manager" +const defaultExampleTerraformDir = "examples/complete" +const privateExampleTerraformDir = "examples/complete-private" -func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { +// 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" + +var permanentResources map[string]interface{} + +// TestMain will be run before any parallel tests, used to read data from yaml for use with tests +func TestMain(m *testing.M) { + + var err error + permanentResources, err = common.LoadMapFromYaml(yamlLocation) + if err != nil { + log.Fatal(err) + } + + os.Exit(m.Run()) +} + +func setupOptions(t *testing.T, prefix string) *testhelper.TestOptions { options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ Testing: t, - TerraformDir: dir, + TerraformDir: defaultExampleTerraformDir, Prefix: prefix, ResourceGroup: resourceGroup, }) + return options } -func TestRunCompleteExample(t *testing.T) { +func TestRunDefaultExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template", completeExampleDir) + options := setupOptions(t, "secrets-mgr") output, err := options.RunTestConsistency() assert.Nil(t, err, "This should not have errored") @@ -35,7 +56,7 @@ func TestRunCompleteExample(t *testing.T) { func TestRunUpgradeExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-upg", completeExampleDir) + options := setupOptions(t, "secrets-mgr-upg") output, err := options.RunTestUpgrade() if !options.UpgradeTestSkipped { @@ -43,3 +64,36 @@ func TestRunUpgradeExample(t *testing.T) { assert.NotNil(t, output, "Expected some output") } } + +func setupPrivateOptions(t *testing.T, prefix string) *testhelper.TestOptions { + options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ + Testing: t, + TerraformDir: privateExampleTerraformDir, + Prefix: prefix, + Region: "us-south", // Locking into us-south since that is where the HPCS permanent instance is + /* + Comment out the 'ResourceGroup' input to force this tests to create a unique resource group to ensure tests do + not clash. This is due to the fact that an auth policy may already exist in this resource group since we are + re-using a permanent HPCS instance. By using a new resource group, the auth policy will not already exist + since this module scopes auth policies by resource group. + */ + //ResourceGroup: resourceGroup, + TerraformVars: map[string]interface{}{ + "kms_encryption_enabled": true, + "existing_kms_instance_guid": permanentResources["hpcs_south"], + "kms_key_crn": permanentResources["hpcs_south_root_key_crn"], + }, + }) + + return options +} + +func TestRunPrivateExample(t *testing.T) { + t.Parallel() + + options := setupPrivateOptions(t, "secrets-mgr-priv") + + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") +} diff --git a/variables.tf b/variables.tf index df60434..14664d5 100644 --- a/variables.tf +++ b/variables.tf @@ -1,9 +1,88 @@ -######################################################################################################################## +############################################################################## # Input Variables -######################################################################################################################## +############################################################################## -#variable "my_variable" { -# type = string -# description = "A description of my variable" -# default = "default_value" -#} +variable "resource_group_id" { + type = string + description = "The ID of the resource group to provision the Secrets Manager instance to." +} + +variable "region" { + type = string + description = "The region to provision the Secrets Manager instance to." +} + +variable "secrets_manager_name" { + type = string + description = "The name to give the Secrets Manager instance." +} + +variable "sm_service_plan" { + type = string + description = "The Secrets Manager plan to provision." + default = "standard" + validation { + condition = contains(["standard", "trial"], var.sm_service_plan) + error_message = "The specified sm_service_plan is not a valid selection!" + } +} + +variable "sm_tags" { + type = list(string) + description = "The list of resource tags that you want to associate with your Secrets Manager instance." + default = [] +} + +variable "service_endpoints" { + type = string + description = "The types of service endpoints to set on the Secrets Manager instance. Possible values are `public`, `private` or `public-and-private`." + default = "public-and-private" + validation { + condition = contains(["public", "private", "public-and-private"], var.service_endpoints) + error_message = "The specified service_endpoints is not a valid selection!" + } +} + +variable "kms_encryption_enabled" { + type = bool + description = "Set this to true to control the encryption keys used to encrypt the data that you store in Secrets Manager. If set to false, the data that you store is encrypted at rest by using envelope encryption. For more details, see https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-mng-data&interface=ui#about-encryption." + default = false +} + +variable "skip_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_guid` variable. In addition, no policy is created if `kms_encryption_enabled` is set to false." + default = false +} + +variable "existing_kms_instance_guid" { + type = string + description = "The GUID of the Hyper Protect Crypto Services or Key Protect instance in which the key specified in `kms_key_crn` is coming from. Required only if `kms_encryption_enabled` is set to true, and `skip_iam_authorization_policy` is set to false." + default = null +} + +variable "kms_key_crn" { + type = string + description = "The root key CRN of a Key Management Service like Key Protect or Hyper Protect Crypto Services (HPCS) that you want to use for encryption. Only used if `kms_encryption_enabled` is set to true." + default = null +} + +############################################################## +# Context-based restriction (CBR) +############################################################## + +variable "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 + })) + description = "(Optional, list) List of CBR rules to create" + default = [] + # Validation happens in the rule module +} diff --git a/version.tf b/version.tf index bcb2505..1df8c96 100644 --- a/version.tf +++ b/version.tf @@ -1,12 +1,14 @@ terraform { - required_version = ">= 1.3.0, <1.6.0" - # If your module requires any terraform providers, uncomment the "required_providers" section below and add all required providers. - # Each required provider's version should be a flexible range to future proof the module's usage with upcoming minor and patch versions. - - # required_providers { - # ibm = { - # source = "IBM-Cloud/ibm" - # version = ">= 1.49.0, < 2.0.0" - # } - # } + required_version = ">= v1.0.0, <1.6.0" + required_providers { + # Use "greater than or equal to" range in modules + ibm = { + source = "IBM-Cloud/ibm" + version = ">= 1.56.1, < 2.0.0" + } + time = { + source = "hashicorp/time" + version = ">= 0.9.1, < 1.0.0" + } + } }