diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml new file mode 100644 index 0000000..8627012 --- /dev/null +++ b/.catalog-onboard-pipeline.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +offerings: + - name: deploy-arch-secrets-manager-public-cert-engine + kind: solution + catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd + offering_id: 79d8474b-7105-4c36-81d7-989f042bce0e + variations: + - name: fully-configurable + mark_ready: true + install_type: fullstack + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south diff --git a/.releaserc b/.releaserc index 708916f..4160e57 100644 --- a/.releaserc +++ b/.releaserc @@ -10,6 +10,9 @@ }], ["@semantic-release/exec", { "successCmd": "echo \"SEMVER_VERSION=${nextRelease.version}\" >> $GITHUB_ENV" + }], + ["@semantic-release/exec",{ + "publishCmd": "./ci/trigger-catalog-onboarding-pipeline.sh --version=v${nextRelease.version}" }] ] } diff --git a/README.md b/README.md index 09ef1b2..13bc9b7 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ No modules. | [ibmcloud\_cis\_api\_key](#input\_ibmcloud\_cis\_api\_key) | Optional, when not using IAM authorization, use an API key for CIS DNS configuration | `string` | `null` | no | | [internet\_service\_domain\_id](#input\_internet\_service\_domain\_id) | (optional) Specific domain in the CIS to authorize Secrets Manager access to. | `string` | `null` | no | | [internet\_services\_crn](#input\_internet\_services\_crn) | CRN of the CIS instance to authorize Secrets Manager against | `string` | `null` | no | -| [lets\_encrypt\_environment](#input\_lets\_encrypt\_environment) | Let's Encrtyp environment (staging, production) | `string` | `"production"` | no | +| [lets\_encrypt\_environment](#input\_lets\_encrypt\_environment) | Let's Encrypt environment (staging, production) | `string` | `"production"` | no | | [private\_key\_secrets\_manager\_instance\_guid](#input\_private\_key\_secrets\_manager\_instance\_guid) | The Secrets Manager instance GUID of the Secrets Manager containing your ACME private key. Required if acme\_letsencrypt\_private\_key is not set. | `string` | `null` | no | | [private\_key\_secrets\_manager\_region](#input\_private\_key\_secrets\_manager\_region) | The region of the Secrets Manager instance containing your ACME private key. (Only needed if different from the region variable) | `string` | `null` | no | | [private\_key\_secrets\_manager\_secret\_id](#input\_private\_key\_secrets\_manager\_secret\_id) | The secret ID of your ACME private key. Required if acme\_letsencrypt\_private\_key is not set. If both are set, this value will be used as the private key. | `string` | `null` | no | diff --git a/ibm_catalog.json b/ibm_catalog.json new file mode 100644 index 0000000..1a6c58d --- /dev/null +++ b/ibm_catalog.json @@ -0,0 +1,180 @@ +{ + "products": [ + { + "name": "deploy-arch-secrets-manager-public-cert-engine", + "label": "Cloud automation for Secrets Manager public certificates engine", + "product_kind": "solution", + "tags": [ + "ibm_created", + "target_terraform", + "terraform", + "solution", + "security" + ], + "keywords": [ + "Public certificates engine", + "Secrets Manager", + "IaC", + "infrastructure as code", + "terraform", + "solution" + ], + "short_description": "Creates and configures a Secrets Manager Public Certificates Engine.", + "long_description" : "This deployable architecture is used to configure an Internet Service DNS configuration, establish authorization between Secrets Manager and the Internet Service, and set up Let's Encrypt as the certificate authority. 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 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-public-cert-engine/blob/main/README.md", + "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-secrets-manager-public-cert-engine/main/images/secrets_manager_public_cert_engine.svg", + "provider_name": "IBM", + "features": [ + { + "title": "Configures Internet Service DNS", + "description": "Configures Internet Service DNS." + }, + { + "title": "Creates Secrets Manager Internet Service authorization", + "description": "Creates authorization between Secrets Manager and Internet Service." + }, + { + "title": "Configures Let's Encrypt certificate authority", + "description": "Configures Let's Encrypt certificate authority." + } + ], + "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-public-cert-engine/issues](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager-public-cert-engine/issues). Please note this product is not supported via the IBM Cloud Support Center.", + "flavors": [ + { + "label": "Fully configurable", + "name": "fully-configurable", + "install_type": "fullstack", + "working_directory": "solutions/fully-configurable", + "architecture": { + "descriptions": "This architecture supports creating and configuring a Secrets Manager Public Certificates Engine.", + "features": [ + { + "title": "Internet Service DNS", + "description": "Configures Internet Service DNS configuration." + }, + { + "title": "Authorization between Secrets Manager and Internet Service", + "description": "Creates authorization between Secrets Manager and Internet Service." + }, + { + "title": "Let's Encrypt certificate authority", + "description": "Provisions a Let's Encrypt certificate authority." + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Secrets Manager Public Certificates Engine", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-secrets-manager-public-cert-engine/main/reference-architecture/secrets_manager_public_cert_engine.svg", + "type": "image/svg+xml" + }, + "description": "This architcture supports creating secrets manager public certificates engine within a secrets manager instance." + } + ] + }, + "iam_permissions": [ + { + "service_name": "iam-access-groups", + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Editor" + ] + }, + { + "service_name": "iam-identity", + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Operator" + ] + }, + { + "service_name": "resource-group", + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Viewer" + ] + }, + { + "service_name": "secrets-manager", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Administrator", + "crn:v1:bluemix:public:iam::::serviceRole:Manager" + ] + } + ], + "configuration": [ + { + "key": "ibmcloud_api_key" + }, + { + "key": "existing_secrets_manager_crn", + "required": true + }, + { + "key": "prefix", + "required": true + }, + { + "key": "ibmcloud_cis_api_key" + }, + { + "key": "internet_services_crn" + }, + { + "key": "internet_services_account_id" + }, + { + "key": "internet_service_domain_id" + }, + { + "key": "dns_config_name" + }, + { + "key": "ca_config_name" + }, + { + "key": "lets_encrypt_environment", + "options": [ + { + "displayname": "staging", + "value": "staging" + }, + { + "displayname": "production", + "value": "production" + } + ] + }, + { + "key": "acme_letsencrypt_private_key" + }, + { + "key":"private_key_secrets_manager_secret_crn" + }, + { + "key":"skip_iam_authorization_policy" + } + ], + "dependencies": [ + { + "name": "deploy-arch-ibm-secrets-manager", + "description":"Configures Secrets Manager instance for the public certificates engine to be provisioned in.", + "id": "6d6ebc76-7bbd-42f5-8bc7-78f4fabd5944-global", + "version": "v2.6.1", + "flavors": [ + "fully-configurable" + ], + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "optional": true, + "on_by_default": true, + "input_mapping": [ + { + "dependency_output": "secrets_manager_crn", + "version_input": "existing_secrets_manager_crn" + } + ] + } + ], + "dependency_version_2": true + } + ] + } + ] + } diff --git a/images/secrets_manager_public_cert_engine.svg b/images/secrets_manager_public_cert_engine.svg new file mode 100644 index 0000000..0b6e9f7 --- /dev/null +++ b/images/secrets_manager_public_cert_engine.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/reference-architecture/secrets_manager_public_cert_engine.svg b/reference-architecture/secrets_manager_public_cert_engine.svg new file mode 100644 index 0000000..54a6c8a --- /dev/null +++ b/reference-architecture/secrets_manager_public_cert_engine.svg @@ -0,0 +1,4 @@ + + + +
IBM Cloud
Region
Resource Group
Secrets Manager
Secrets Manager Public Certificates Engine
Internet Services
Internet service authorization
\ No newline at end of file diff --git a/solutions/fully-configurable/README.md b/solutions/fully-configurable/README.md new file mode 100644 index 0000000..7dd6209 --- /dev/null +++ b/solutions/fully-configurable/README.md @@ -0,0 +1,10 @@ +# Secrets Manager Public Certificate Engine + +This solution supports the following: +- Provisioning a Secrets Manager public certificate authority configuration to configure Let's Encrypt as a Certificate Authority (CA). +- Provisioning a Secrets Manager DNS provider configuration for IBM Cloud Internet Services. +- Provisioning a Secrets Manager to Cloud Internet Service authorization policy. + +![secrets-manager-public-cert-engine-deployable-architecture](../../reference-architecture/secrets_manager_public_cert_engine.svg) + +**NB:** This solution is not intended to be invoked by other modules, as it includes provider configuration. As a result, it is incompatible 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/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template new file mode 100644 index 0000000..b5e2fa0 --- /dev/null +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -0,0 +1,7 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "existing_secrets_manager_crn": $SM_CRN, + "prefix": $PREFIX, + "private_key_secrets_manager_secret_crn": $ACME_LETSENCRYPT_PRIVATE_KEY_SECRET_CRN, + "internet_services_crn": $CIS_INSTANCE_ID +} diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf new file mode 100644 index 0000000..b80122b --- /dev/null +++ b/solutions/fully-configurable/main.tf @@ -0,0 +1,50 @@ +######################################################################################################################## +# Secrets Manager Public Cert Engine +######################################################################################################################## + +locals { + prefix = var.prefix != null ? trimspace(var.prefix) != "" ? "${var.prefix}-" : "" : "" +} + +module "secrets_manager_crn_parser" { + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_secrets_manager_crn +} + +module "secret_crn_parser" { + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.private_key_secrets_manager_secret_crn +} + +locals { + existing_secrets_manager_guid = module.secrets_manager_crn_parser.service_instance + existing_secrets_manager_region = module.secrets_manager_crn_parser.region + + secret_region = module.secret_crn_parser.region + secret_id = module.secret_crn_parser.resource +} + +module "secrets_manager_public_cert_engine" { + source = "../.." + providers = { + ibm = ibm + ibm.secret-store = ibm.secret-store + } + secrets_manager_guid = local.existing_secrets_manager_guid + region = local.existing_secrets_manager_region + ibmcloud_cis_api_key = var.ibmcloud_cis_api_key + internet_services_crn = var.internet_services_crn + cis_account_id = var.internet_services_account_id + internet_service_domain_id = var.internet_service_domain_id + dns_config_name = "${local.prefix}${var.dns_config_name}" + ca_config_name = "${local.prefix}${var.ca_config_name}" + lets_encrypt_environment = var.lets_encrypt_environment + acme_letsencrypt_private_key = var.acme_letsencrypt_private_key + service_endpoints = "private" + skip_iam_authorization_policy = var.skip_iam_authorization_policy + private_key_secrets_manager_instance_guid = local.existing_secrets_manager_guid + private_key_secrets_manager_secret_id = local.secret_id + private_key_secrets_manager_region = local.secret_region +} diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf new file mode 100644 index 0000000..ac9338f --- /dev/null +++ b/solutions/fully-configurable/outputs.tf @@ -0,0 +1,5 @@ +############################################################################## +# Outputs +############################################################################## + +############################################################################## diff --git a/solutions/fully-configurable/provider.tf b/solutions/fully-configurable/provider.tf new file mode 100644 index 0000000..75c03c3 --- /dev/null +++ b/solutions/fully-configurable/provider.tf @@ -0,0 +1,10 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = local.existing_secrets_manager_region +} + +provider "ibm" { + alias = "secret-store" + ibmcloud_api_key = var.ibmcloud_api_key + region = local.existing_secrets_manager_region +} diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf new file mode 100644 index 0000000..0443e35 --- /dev/null +++ b/solutions/fully-configurable/variables.tf @@ -0,0 +1,113 @@ +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key used to provision resources." + sensitive = true +} + +variable "existing_secrets_manager_crn" { + type = string + description = "CRN of an existing secrets manager instance to create the secret engine in." +} + +variable "prefix" { + type = string + nullable = true + description = "The prefix to be added to all resources created by this solution. To skip using a prefix, set this value to null or an empty string. The prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It should not exceed 16 characters, must not end with a hyphen('-'), and can not contain consecutive hyphens ('--'). Example: prod-0205-cos. [Learn more](https://terraform-ibm-modules.github.io/documentation/#/prefix.md)." + + validation { + # - null and empty string is allowed + # - Must not contain consecutive hyphens (--): length(regexall("--", var.prefix)) == 0 + # - Starts with a lowercase letter: [a-z] + # - Contains only lowercase letters (a–z), digits (0–9), and hyphens (-) + # - Must not end with a hyphen (-): [a-z0-9] + condition = (var.prefix == null || var.prefix == "" ? true : + alltrue([ + can(regex("^[a-z][-a-z0-9]*[a-z0-9]$", var.prefix)), + length(regexall("--", var.prefix)) == 0 + ]) + ) + error_message = "Prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It must not end with a hyphen('-'), and cannot contain consecutive hyphens ('--')." + } + + validation { + # must not exceed 16 characters in length + condition = length(var.prefix) <= 16 + error_message = "Prefix must not exceed 16 characters." + } +} + + +variable "ibmcloud_cis_api_key" { + type = string + description = "If not using IAM authorization, supply an API key for Internet Services DNS configuration. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-secrets-manager-cli#secrets-manager-configurations-cli)." + default = null + sensitive = true +} + +variable "internet_services_crn" { + type = string + description = "The CRN of the Internet Service instance to authorize Secrets Manager against. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-secrets-manager-cli#secrets-manager-configurations-cli)." + default = null +} + +variable "internet_services_account_id" { + type = string + description = "The Account ID of the Internet Service instance (only needed if different from Secrets Manager account)." + default = null +} + +variable "internet_service_domain_id" { + type = string + description = "Specific domain in the Internet Service to authorize Secrets Manager access to." + default = null +} + +variable "dns_config_name" { + type = string + description = "Name of the DNS config for the public_cert secrets engine. If a prefix input variable is specified, it is added to the value in the `-value` format. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-secrets-manager-cli#secrets-manager-configurations-cli)." + default = "certificate-dns" +} + +variable "ca_config_name" { + type = string + description = "Name of the CA config for the public certificate secrets engine. If a prefix input variable is specified, it is added to the value in the `-value` format. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-secrets-manager-cli#secrets-manager-configurations-cli)." + default = "cert-auth" +} + +variable "lets_encrypt_environment" { + type = string + description = "Let's Encrypt environment (staging, production). [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-secrets-manager-cli#secrets-manager-configurations-cli)." + default = "production" + + validation { + condition = contains(["staging", "production"], var.lets_encrypt_environment) + error_message = "lets_encrypt_environment must be either 'staging' or 'production'." + } +} + +variable "acme_letsencrypt_private_key" { + type = string + description = "The private key generated by the ACME account creation tool. Required if private_key_secrets_manager_secret_crn is not set. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-secrets-manager-cli#secrets-manager-configurations-cli)." + default = null + sensitive = true +} + +variable "skip_iam_authorization_policy" { + type = bool + description = "Set to true to skip the creation of an IAM authorization policy that permits Secrets Manager to create a DNS config in the CIS specified in `internet_services_crn`. WARNING: An authorization policy must exist before a DNS config can be created, OR an API key must be provided in `ibmcloud_cis_api_key`." + default = false +} + +variable "private_key_secrets_manager_secret_crn" { + type = string + description = "The secret CRN of your ACME private key. Required if acme_letsencrypt_private_key is not set. If both are set, this value will be used as the private key." + default = null + + validation { + condition = ( + var.private_key_secrets_manager_secret_crn != null || + var.acme_letsencrypt_private_key != null + ) + error_message = "If `acme_letsencrypt_private_key` is not set, you must provide a value for `private_key_secrets_manager_secret_crn`." + } +} diff --git a/solutions/fully-configurable/version.tf b/solutions/fully-configurable/version.tf new file mode 100644 index 0000000..5e71416 --- /dev/null +++ b/solutions/fully-configurable/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= v1.9.0" + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "= 1.79.0" + } + } +} diff --git a/tests/README.md b/tests/README.md index dfd6842..05d524e 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,5 +1,3 @@ # Tests For information about how to create and run tests, see [Validation tests](https://terraform-ibm-modules.github.io/documentation/#/tests) in the project documentation. - - diff --git a/tests/pr_test.go b/tests/pr_test.go index b2a602d..82f8abb 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -22,6 +22,7 @@ const resourceGroup = "geretain-test-sm-pub-cert-eng" const keyExampleTerraformDir = "examples/api_key_auth" const IAMExampleTerraformDir = "examples/iam_auth" +const fullyConfigurableDir = "solutions/fully-configurable" const bestRegionYAMLPath = "../common-dev-assets/common-go-assets/cloudinfo-region-secmgr-prefs.yaml" // TestMain will be run before any parallel tests, used to read data from yaml for use with tests @@ -109,3 +110,65 @@ func TestRunUpgradeExample(t *testing.T) { assert.NotNil(t, output, "Expected some output") } } + +func TestRunSolutionsFullyConfigurableSchematics(t *testing.T) { + t.Parallel() + + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: "sm-pbce", + TarIncludePatterns: []string{ + "*.tf", + fullyConfigurableDir + "/*.tf", + }, + ResourceGroup: resourceGroup, + TemplateFolder: fullyConfigurableDir, + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 80, + }) + + 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: "existing_secrets_manager_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"}, + {Name: "private_key_secrets_manager_secret_crn", Value: permanentResources["acme_letsencrypt_private_key_secret_crn"], DataType: "string"}, + {Name: "internet_services_crn", Value: permanentResources["cisInstanceId"], DataType: "string"}, + {Name: "skip_iam_authorization_policy", Value: true, DataType: "bool"}, // A permanent cis-sm auth policy already exists in the account + } + + err := options.RunSchematicTest() + assert.Nil(t, err, "This should not have errored") +} + +func TestRunSolutionsFullyConfigurableUpgradeSchematics(t *testing.T) { + t.Parallel() + + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: "sm-pbce-up", + TarIncludePatterns: []string{ + "*.tf", + fullyConfigurableDir + "/*.tf", + }, + ResourceGroup: resourceGroup, + TemplateFolder: fullyConfigurableDir, + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 80, + }) + + 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: "existing_secrets_manager_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"}, + {Name: "private_key_secrets_manager_secret_crn", Value: permanentResources["acme_letsencrypt_private_key_secret_crn"], DataType: "string"}, + {Name: "internet_services_crn", Value: permanentResources["cisInstanceId"], DataType: "string"}, + {Name: "skip_iam_authorization_policy", Value: true, DataType: "bool"}, // A permanent cis-sm auth policy already exists in the account + } + + err := options.RunSchematicUpgradeTest() + if !options.UpgradeTestSkipped { + assert.Nil(t, err, "This should not have errored") + } +} diff --git a/variables.tf b/variables.tf index ec941d7..bd9c2cf 100644 --- a/variables.tf +++ b/variables.tf @@ -57,7 +57,7 @@ variable "ca_config_name" { variable "lets_encrypt_environment" { type = string - description = "Let's Encrtyp environment (staging, production)" + description = "Let's Encrypt environment (staging, production)" default = "production" }