Skip to content

Commit 1bfd5e1

Browse files
authored
feat: add support to use existing Secrets Manager instance in the DA solution (#109)
1 parent 506c47e commit 1bfd5e1

File tree

8 files changed

+123
-76
lines changed

8 files changed

+123
-76
lines changed

solutions/standard/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ This solution supports the following:
66
- Optionally configure an IBM Secrets Manager IAM credentials engine to an IBM Secrets Manager instance.
77
- Configuring KMS encryption using a newly created key, or passing an existing key.
88

9+
![secret-manager-deployable-architecture](../../reference-architecture/secrets_manager.svg)
10+
911
**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)

solutions/standard/main.tf

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,15 @@ module "kms" {
5757
# Secrets Manager
5858
########################################################################################################################
5959

60+
locals {
61+
parsed_existing_secrets_manager_crn = var.existing_secrets_manager_crn != null ? split(":", var.existing_secrets_manager_crn) : []
62+
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[0].secrets_manager_guid
63+
secrets_manager_crn = var.existing_secrets_manager_crn != null ? var.existing_secrets_manager_crn : module.secrets_manager[0].secrets_manager_crn
64+
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[0].secrets_manager_region
65+
}
66+
6067
module "secrets_manager" {
68+
count = var.existing_secrets_manager_crn != null ? 0 : 1
6169
source = "../.."
6270
resource_group_id = module.resource_group.resource_group_id
6371
region = var.region
@@ -84,7 +92,7 @@ module "iam_secrets_engine" {
8492
version = "1.1.0"
8593
region = var.region
8694
iam_engine_name = var.prefix != null ? "${var.prefix}-${var.iam_engine_name}" : var.iam_engine_name
87-
secrets_manager_guid = module.secrets_manager.secrets_manager_guid
95+
secrets_manager_guid = local.secrets_manager_guid
8896
endpoint_type = var.allowed_network == "private-only" ? "private" : "public"
8997
}
9098

@@ -104,8 +112,8 @@ module "secrets_manager_public_cert_engine" {
104112
ibm = ibm
105113
ibm.secret-store = ibm
106114
}
107-
secrets_manager_guid = module.secrets_manager.secrets_manager_guid
108-
region = module.secrets_manager.secrets_manager_region
115+
secrets_manager_guid = local.secrets_manager_guid
116+
region = local.secrets_manager_region
109117
internet_services_crn = var.cis_id
110118
ibmcloud_cis_api_key = var.ibmcloud_api_key
111119
dns_config_name = var.dns_provider_name
@@ -120,7 +128,7 @@ module "private_secret_engine" {
120128
count = var.private_engine_enabled ? 1 : 0
121129
source = "terraform-ibm-modules/secrets-manager-private-cert-engine/ibm"
122130
version = "1.3.0"
123-
secrets_manager_guid = module.secrets_manager.secrets_manager_guid
131+
secrets_manager_guid = local.secrets_manager_guid
124132
region = var.region
125133
root_ca_name = var.root_ca_name
126134
root_ca_common_name = var.root_ca_common_name

solutions/standard/outputs.tf

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@ output "resource_group_id" {
1010

1111
output "secrets_manager_guid" {
1212
description = "GUID of Secrets Manager instance"
13-
value = module.secrets_manager.secrets_manager_guid
13+
value = local.secrets_manager_guid
1414
}
1515

1616
output "secrets_manager_id" {
1717
description = "ID of Secrets Manager instance"
18-
value = module.secrets_manager.secrets_manager_id
18+
value = var.existing_secrets_manager_crn == null ? module.secrets_manager[0].secrets_manager_id : null
1919
}
2020

2121
output "secrets_manager_name" {
22-
value = module.secrets_manager.secrets_manager_name
22+
value = var.existing_secrets_manager_crn == null ? module.secrets_manager[0].secrets_manager_name : null
2323
description = "Name of the Secrets Manager instance"
2424
}
2525

2626
output "secrets_manager_crn" {
27-
value = module.secrets_manager.secrets_manager_crn
27+
value = local.secrets_manager_crn
2828
description = "CRN of the Secrets Manager instance"
2929
}
3030

3131
output "secrets_manager_region" {
32-
value = module.secrets_manager.secrets_manager_region
32+
value = local.secrets_manager_region
3333
description = "Region of the Secrets Manager instance"
3434
}

solutions/standard/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ variable "secrets_manager_instance_name" {
4141
default = "base-security-services-sm"
4242
}
4343

44+
variable "existing_secrets_manager_crn" {
45+
type = string
46+
description = "The CRN of an existing Secrets Manager instance. If not supplied, a new Secrets Manager instance will be created."
47+
default = null
48+
}
49+
4450
variable "service_plan" {
4551
type = string
4652
description = "The service/pricing plan to use when provisioning a new Secrets Manager instance. Allowed values: 'standard' and 'trial'. Only used if `provision_sm_instance` is set to true."

tests/existing-resources/main.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,22 @@ module "key_protect" {
4646
}
4747
]
4848
}
49+
50+
##############################################################################
51+
# Secrets Manager
52+
##############################################################################
53+
54+
module "secrets_manager" {
55+
source = "terraform-ibm-modules/secrets-manager/ibm"
56+
version = "1.11.0"
57+
resource_group_id = module.resource_group.resource_group_id
58+
region = var.region
59+
secrets_manager_name = "${var.prefix}-secrets-manager" #tfsec:ignore:general-secrets-no-plaintext-exposure
60+
sm_service_plan = "trial"
61+
sm_tags = var.resource_tags
62+
kms_encryption_enabled = true
63+
existing_kms_instance_guid = module.key_protect.kms_guid
64+
kms_key_crn = module.key_protect.keys["${var.prefix}-sm.${var.prefix}-sm-key"].crn
65+
enable_event_notification = true
66+
existing_en_instance_crn = module.event_notifications.crn
67+
}

tests/existing-resources/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ output "event_notification_instance_crn" {
2222
value = module.event_notifications.crn
2323
description = "CRN of created event notification"
2424
}
25+
26+
output "secrets_manager_instance_crn" {
27+
value = module.secrets_manager.secrets_manager_crn
28+
description = "CRN of created secret manager instance"
29+
}

tests/other_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package test
22

33
import (
4+
"math/rand"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
78
"github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper"
9+
"github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testschematic"
810
)
911

1012
func TestRunBasicExample(t *testing.T) {
@@ -20,3 +22,44 @@ func TestRunBasicExample(t *testing.T) {
2022
assert.Nil(t, err, "This should not have errored")
2123
assert.NotNil(t, output, "Expected some output")
2224
}
25+
26+
func TestRunCompleteExample(t *testing.T) {
27+
t.Parallel()
28+
29+
options := setupOptions(t, "secrets-mgr")
30+
31+
output, err := options.RunTestConsistency()
32+
assert.Nil(t, err, "This should not have errored")
33+
assert.NotNil(t, output, "Expected some output")
34+
}
35+
36+
func TestFSCloudInSchematics(t *testing.T) {
37+
t.Parallel()
38+
39+
options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{
40+
Testing: t,
41+
Prefix: "sm-fscloud",
42+
TarIncludePatterns: []string{
43+
"*.tf",
44+
fscloudExampleTerraformDir + "/*.tf",
45+
"modules/fscloud/*.tf",
46+
},
47+
// ResourceGroup: resourceGroup,
48+
TemplateFolder: fscloudExampleTerraformDir,
49+
Tags: []string{"test-schematic"},
50+
DeleteWorkspaceOnFail: false,
51+
WaitJobCompleteMinutes: 60,
52+
})
53+
54+
options.TerraformVars = []testschematic.TestSchematicTerraformVar{
55+
{Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true},
56+
{Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"},
57+
{Name: "prefix", Value: options.Prefix, DataType: "string"},
58+
{Name: "existing_kms_instance_guid", Value: permanentResources["hpcs_south"], DataType: "string"},
59+
{Name: "kms_key_crn", Value: permanentResources["hpcs_south_root_key_crn"], DataType: "string"},
60+
{Name: "sm_service_plan", Value: "trial", DataType: "string"},
61+
}
62+
63+
err := options.RunSchematicTest()
64+
assert.Nil(t, err, "This should not have errored")
65+
}

tests/pr_test.go

Lines changed: 31 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package test
44
import (
55
"fmt"
66
"log"
7+
"math/rand"
78
"os"
89
"strings"
910
"testing"
@@ -32,6 +33,14 @@ const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-res
3233

3334
var permanentResources map[string]interface{}
3435

36+
// Current supported Event Notification regions
37+
var validRegions = []string{
38+
"us-south",
39+
"eu-de",
40+
"eu-gb",
41+
"au-syd",
42+
}
43+
3544
// TestMain will be run before any parallel tests, used to read data from yaml for use with tests
3645
func TestMain(m *testing.M) {
3746

@@ -49,27 +58,17 @@ func setupOptions(t *testing.T, prefix string) *testhelper.TestOptions {
4958
Testing: t,
5059
TerraformDir: completeExampleTerraformDir,
5160
Prefix: prefix,
61+
Region: validRegions[rand.Intn(len(validRegions))],
5262
/*
5363
Comment out the 'ResourceGroup' input to force this tests to create a unique resource group. This is because
5464
there is a restriction with the Event Notification service, which allows only one Lite plan instance per resource group.
5565
*/
5666
// ResourceGroup: resourceGroup,
57-
BestRegionYAMLPath: "../common-dev-assets/common-go-assets/cloudinfo-region-secmgr-prefs.yaml",
5867
})
5968

6069
return options
6170
}
6271

63-
func TestRunCompleteExample(t *testing.T) {
64-
t.Parallel()
65-
66-
options := setupOptions(t, "secrets-mgr")
67-
68-
output, err := options.RunTestConsistency()
69-
assert.Nil(t, err, "This should not have errored")
70-
assert.NotNil(t, output, "Expected some output")
71-
}
72-
7372
func TestRunUpgradeExample(t *testing.T) {
7473
t.Parallel()
7574

@@ -82,38 +81,6 @@ func TestRunUpgradeExample(t *testing.T) {
8281
}
8382
}
8483

85-
func TestFSCloudInSchematics(t *testing.T) {
86-
t.Parallel()
87-
88-
options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{
89-
Testing: t,
90-
Prefix: "sm-fscloud",
91-
TarIncludePatterns: []string{
92-
"*.tf",
93-
fscloudExampleTerraformDir + "/*.tf",
94-
"modules/fscloud/*.tf",
95-
},
96-
BestRegionYAMLPath: "../common-dev-assets/common-go-assets/cloudinfo-region-secmgr-prefs.yaml",
97-
// ResourceGroup: resourceGroup,
98-
TemplateFolder: fscloudExampleTerraformDir,
99-
Tags: []string{"test-schematic"},
100-
DeleteWorkspaceOnFail: false,
101-
WaitJobCompleteMinutes: 60,
102-
})
103-
104-
options.TerraformVars = []testschematic.TestSchematicTerraformVar{
105-
{Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true},
106-
{Name: "region", Value: options.Region, DataType: "string"},
107-
{Name: "prefix", Value: options.Prefix, DataType: "string"},
108-
{Name: "existing_kms_instance_guid", Value: permanentResources["hpcs_south"], DataType: "string"},
109-
{Name: "kms_key_crn", Value: permanentResources["hpcs_south_root_key_crn"], DataType: "string"},
110-
{Name: "sm_service_plan", Value: "trial", DataType: "string"},
111-
}
112-
113-
err := options.RunSchematicTest()
114-
assert.Nil(t, err, "This should not have errored")
115-
}
116-
11784
func TestRunDASolutionSchematics(t *testing.T) {
11885
t.Parallel()
11986

@@ -133,13 +100,12 @@ func TestRunDASolutionSchematics(t *testing.T) {
133100
Tags: []string{"test-schematic"},
134101
DeleteWorkspaceOnFail: false,
135102
WaitJobCompleteMinutes: 60,
136-
BestRegionYAMLPath: "../common-dev-assets/common-go-assets/cloudinfo-region-secmgr-prefs.yaml",
137103
})
138104

139105
options.TerraformVars = []testschematic.TestSchematicTerraformVar{
140106
{Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true},
141107
{Name: "prefix", Value: options.Prefix, DataType: "string"},
142-
{Name: "region", Value: options.Region, DataType: "string"},
108+
{Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"},
143109
{Name: "resource_group_name", Value: options.Prefix, DataType: "string"},
144110
{Name: "service_plan", Value: "trial", DataType: "string"},
145111
{Name: "allowed_network", Value: "private-only", DataType: "string"},
@@ -183,15 +149,6 @@ func GetSecretsManagerKey(sm_id string, sm_region string, sm_key_id string) *str
183149
func TestRunExistingResourcesInstances(t *testing.T) {
184150
t.Parallel()
185151

186-
// Init test options for DA to get the region, which is used for provisioning the existing resources
187-
options := testhelper.TestOptionsDefault(&testhelper.TestOptions{
188-
Testing: t,
189-
TerraformDir: solutionsTerraformDir,
190-
// Do not hard fail the test if the implicit destroy steps fail to allow a full destroy of resource to occur
191-
ImplicitRequired: false,
192-
BestRegionYAMLPath: "../common-dev-assets/common-go-assets/cloudinfo-region-secmgr-prefs.yaml",
193-
})
194-
195152
// ------------------------------------------------------------------------------------
196153
// Provision Event Notification, KMS key and resource group first
197154
// ------------------------------------------------------------------------------------
@@ -200,7 +157,6 @@ func TestRunExistingResourcesInstances(t *testing.T) {
200157
realTerraformDir := "./existing-resources"
201158
tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId())))
202159
tags := common.GetTagsFromTravis()
203-
region := "us-south"
204160

205161
// Verify ibmcloud_api_key variable is set
206162
checkVariable := "TF_VAR_ibmcloud_api_key"
@@ -212,7 +168,7 @@ func TestRunExistingResourcesInstances(t *testing.T) {
212168
TerraformDir: tempTerraformDir,
213169
Vars: map[string]interface{}{
214170
"prefix": prefix,
215-
"region": options.Region,
171+
"region": validRegions[rand.Intn(len(validRegions))],
216172
"resource_tags": tags,
217173
},
218174
// Set Upgrade to true to ensure latest version of providers and modules are used by terratest.
@@ -225,17 +181,25 @@ func TestRunExistingResourcesInstances(t *testing.T) {
225181
if existErr != nil {
226182
assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed")
227183
} else {
228-
// add existing resources to previously created options
229-
options.TerraformVars = map[string]interface{}{
230-
"ibmcloud_api_key": os.Getenv("TF_VAR_ibmcloud_api_key"),
231-
"region": region,
232-
"resource_group_name": terraform.Output(t, existingTerraformOptions, "resource_group_name"),
233-
"use_existing_resource_group": true,
234-
"existing_event_notification_instance_crn": terraform.Output(t, existingTerraformOptions, "event_notification_instance_crn"),
235-
"existing_secrets_manager_kms_key_crn": terraform.Output(t, existingTerraformOptions, "secrets_manager_kms_key_crn"),
236-
"existing_kms_instance_crn": terraform.Output(t, existingTerraformOptions, "secrets_manager_kms_instance_crn"),
237-
"service_plan": "trial",
238-
}
184+
options := testhelper.TestOptionsDefault(&testhelper.TestOptions{
185+
Testing: t,
186+
TerraformDir: solutionsTerraformDir,
187+
// Do not hard fail the test if the implicit destroy steps fail to allow a full destroy of resource to occur
188+
ImplicitRequired: false,
189+
TerraformVars: map[string]interface{}{
190+
"ibmcloud_api_key": os.Getenv("TF_VAR_ibmcloud_api_key"),
191+
"region": validRegions[rand.Intn(len(validRegions))],
192+
"resource_group_name": terraform.Output(t, existingTerraformOptions, "resource_group_name"),
193+
"use_existing_resource_group": true,
194+
"existing_event_notification_instance_crn": terraform.Output(t, existingTerraformOptions, "event_notification_instance_crn"),
195+
"existing_secrets_manager_kms_key_crn": terraform.Output(t, existingTerraformOptions, "secrets_manager_kms_key_crn"),
196+
"existing_kms_instance_crn": terraform.Output(t, existingTerraformOptions, "secrets_manager_kms_instance_crn"),
197+
"service_plan": "trial",
198+
"existing_secrets_manager_crn": terraform.Output(t, existingTerraformOptions, "secrets_manager_instance_crn"),
199+
"iam_engine_enabled": true,
200+
"private_engine_enabled": true,
201+
},
202+
})
239203

240204
output, err := options.RunTestConsistency()
241205
assert.Nil(t, err, "This should not have errored")

0 commit comments

Comments
 (0)