diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml new file mode 100644 index 0000000..c464f24 --- /dev/null +++ b/.catalog-onboard-pipeline.yaml @@ -0,0 +1,20 @@ +--- +apiVersion: v1 +offerings: + - name: deploy-arch-ibm-scc + kind: solution + catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd + offering_id: 9423f9bc-1290-4c71-a9ac-01898bfa7ccc + variations: + - name: fully-configurable + mark_ready: true + install_type: fullstack + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south + - name: security-enforced + mark_ready: true + install_type: fullstack + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south diff --git a/.github/settings.yml b/.github/settings.yml index 55f03d0..af6c8ab 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -25,4 +25,4 @@ repository: description: "Configures an IBM Cloud Security and Compliance 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, core-team, supported, stable, scc, security, compliance + topics: terraform, ibm-cloud, terraform-module, core-team, supported, stable, scc, security, compliance, deployable-architecture diff --git a/.secrets.baseline b/.secrets.baseline index df8b961..2fa675f 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2023-12-13T06:40:44Z", + "generated_at": "2025-03-13T14:35:20Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -76,7 +76,46 @@ "name": "TwilioKeyDetector" } ], - "results": {}, + "results": { + "solutions/fully-configurable/DA-cbr_rules.md": [ + { + "hashed_secret": "91bd6d8889493222b850338327aa2f54b7ab25d0", + "is_secret": false, + "is_verified": false, + "line_number": 42, + "type": "Hex High Entropy String", + "verified_result": null + } + ], + "solutions/fully-configurable/scopes_attachments.md": [ + { + "hashed_secret": "3b5bf5f75003778663c521c8c35ad277227dd4f5", + "is_secret": false, + "is_verified": false, + "line_number": 42, + "type": "Hex High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "a1dc91d03edccb97baada9b08525963807fb9d37", + "is_secret": false, + "is_verified": false, + "line_number": 46, + "type": "Hex High Entropy String", + "verified_result": null + } + ], + "tests/pr_test.go": [ + { + "hashed_secret": "3b5bf5f75003778663c521c8c35ad277227dd4f5", + "is_secret": false, + "is_verified": false, + "line_number": 44, + "type": "Hex High Entropy String", + "verified_result": null + } + ] + }, "version": "0.13.1+ibm.62.dss", "word_list": { "file": null, diff --git a/README.md b/README.md index af105a3..f254a11 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ This module configures an IBM Cloud Security and Compliance instance. * [Submodules](./modules) * [attachment](./modules/attachment) * [Examples](./examples) + * [Advanced example with CBR rules](./examples/advanced) * [Basic example](./examples/basic) - * [Complete example with CBR rules](./examples/complete) * [Contributing](#contributing) diff --git a/cra-config.yaml b/cra-config.yaml index 926f80d..4a3a117 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -1,6 +1,13 @@ # 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: "examples/basic" # 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: + TF_VAR_prefix: "test" + TF_VAR_existing_resource_group_name: "Default" + TF_VAR_kms_encryption_enabled_bucket: true + TF_VAR_existing_kms_instance_crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/abac0df06b644a9cabc6e44f55b3880e:e6dce284-e80f-46e1-a3c1-830f7adff7a9::" + TF_VAR_existing_cos_instance_crn: "crn:v1:bluemix:public:cloud-object-storage:global:a/abac0df06b644a9cabc6e44f55b3880e:855ed836-05ce-4f39-98fa-508774f29323::" + TF_VAR_provider_visibility: "public" diff --git a/cra-tf-validate-ignore-rules.json b/cra-tf-validate-ignore-rules.json index a34dedd..adbff6e 100644 --- a/cra-tf-validate-ignore-rules.json +++ b/cra-tf-validate-ignore-rules.json @@ -1,10 +1,3 @@ { - "scc_rules": [ - { - "scc_rule_id": "rule-c97259ee-336d-4c5f-b436-1868107a9558", - "description": "Check whether Cloud Object Storage is enabled with customer-managed encryption and Keep Your Own Key (KYOK)", - "ignore_reason": "This rule is not relevant to the module itself, just the COS resource that is used in the example that is scanned", - "is_valid": false - } - ] + "scc_rules": [] } diff --git a/examples/complete/README.md b/examples/advanced/README.md similarity index 93% rename from examples/complete/README.md rename to examples/advanced/README.md index 80afc9b..974debb 100644 --- a/examples/complete/README.md +++ b/examples/advanced/README.md @@ -1,4 +1,4 @@ -# Complete example with CBR rules +# Advanced example with CBR rules A complete example that will provision the following: - A new resource group if one is not passed in. diff --git a/examples/complete/main.tf b/examples/advanced/main.tf similarity index 100% rename from examples/complete/main.tf rename to examples/advanced/main.tf diff --git a/examples/complete/outputs.tf b/examples/advanced/outputs.tf similarity index 100% rename from examples/complete/outputs.tf rename to examples/advanced/outputs.tf diff --git a/examples/complete/provider.tf b/examples/advanced/provider.tf similarity index 100% rename from examples/complete/provider.tf rename to examples/advanced/provider.tf diff --git a/examples/complete/variables.tf b/examples/advanced/variables.tf similarity index 100% rename from examples/complete/variables.tf rename to examples/advanced/variables.tf diff --git a/examples/complete/version.tf b/examples/advanced/version.tf similarity index 100% rename from examples/complete/version.tf rename to examples/advanced/version.tf diff --git a/ibm_catalog.json b/ibm_catalog.json new file mode 100644 index 0000000..e8e6591 --- /dev/null +++ b/ibm_catalog.json @@ -0,0 +1,812 @@ +{ + "products": [ + { + "name": "deploy-arch-ibm-scc", + "label": "Cloud automation for Security and Compliance Center", + "product_kind": "solution", + "tags": [ + "ibm_created", + "target_terraform", + "terraform", + "solution", + "security" + ], + "keywords": [ + "SCC", + "security", + "compliance", + "workload-protection", + "IaC", + "infrastructure as code", + "terraform", + "solution" + ], + "short_description": "Creates and configures IBM Security and Compliance Center resources", + "long_description": "This architecture supports creating and configuring IBM Security and Compliance Center resources.", + "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-scc/blob/main/README.md", + "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-scc/main/images/scc-icon.svg", + "provider_name": "IBM", + "features": [ + { + "title": "Security and Compliance Center", + "description": "Creates and configures a Security and Compliance Center instance." + }, + { + "title": "Object Storage bucket for Security and Compliance Center", + "description": "Creates and configures an Object Storage bucket required for the Security and Compliance Center instance." + }, + { + "title": "KMS encryption", + "description": "Supports creating a new key, or using an existing one to encrypt the Object Storage bucket." + }, + { + "title": "Scopes and attachments", + "description": "Supports creating scopes and attachments." + }, + { + "title": "Event Notifications", + "description": "Supports integration with Event Notifications, including the creation of destination, topic and email subscription." + }, + { + "title": "Provider integration", + "description": "Supports integration with providers such as Workload Protection." + } + ], + "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-scc/issues](https://github.com/terraform-ibm-modules/terraform-ibm-scc/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", + "compliance": { + "authority": "scc-v3", + "profiles": [ + { + "profile_name": "IBM Cloud Framework for Financial Services", + "profile_version": "1.7.0" + } + ] + }, + "configuration": [ + { + "key": "ibmcloud_api_key" + }, + { + "key": "prefix", + "required": true, + "description": "Prefix to add to all resources that this solution creates. To not use any prefix value, you can enter the string `__NULL__`." + }, + { + "key": "existing_resource_group_name", + "required": true, + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + } + }, + { + "key": "existing_cos_instance_crn", + "required": true + }, + { + "key": "scc_service_plan", + "required": true, + "options": [ + { + "displayname": "Standard", + "value": "security-compliance-center-standard-plan" + }, + { + "displayname": "Trial", + "value": "security-compliance-center-trial-plan" + } + ] + }, + { + "key": "scc_region", + "required": true, + "options": [ + { + "displayname": "Dallas (us-south)", + "value": "us-south" + }, + { + "displayname": "Frankfurt (eu-de)", + "value": "eu-de" + }, + { + "displayname": "Madrid (eu-es)", + "value": "eu-es" + }, + { + "displayname": "Toronto (ca-tor)", + "value": "ca-tor" + } + ] + }, + { + "key": "provider_visibility", + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] + }, + { + "key": "scc_instance_name" + }, + { + "key": "scc_instance_resource_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "scc_instance_access_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "existing_scc_instance_crn" + }, + { + "key": "scopes" + }, + { + "key": "attachments" + }, + { + "key": "existing_scc_workload_protection_instance_crn" + }, + { + "key": "skip_scc_workload_protection_iam_auth_policy" + }, + { + "key": "custom_integrations" + }, + { + "key": "scc_cos_bucket_name" + }, + { + "key": "kms_encryption_enabled_bucket" + }, + { + "key": "scc_cos_bucket_region", + "options": [ + { + "displayname": "Same region as SCC instance", + "value": "__NULL__" + }, + { + "displayname": "Dallas (us-south)", + "value": "us-south" + }, + { + "displayname": "Sydney (au-syd)", + "value": "au-syd" + }, + { + "displayname": "Sao Paolo (br-sao)", + "value": "br-sao" + }, + { + "displayname": "Toronto (ca-tor)", + "value": "ca-tor" + }, + { + "displayname": "Frankfurt (eu-de)", + "value": "eu-de" + }, + { + "displayname": "London (eu-gb)", + "value": "eu-gb" + }, + { + "displayname": "Madrid (eu-es)", + "value": "eu-es" + }, + { + "displayname": "Osaka (jp-osa)", + "value": "jp-osa" + }, + { + "displayname": "Tokyo (jp-tok)", + "value": "jp-tok" + }, + { + "displayname": "Washington DC (us-east)", + "value": "us-east" + } + ] + }, + { + "key": "add_bucket_name_suffix" + }, + { + "key": "scc_cos_bucket_access_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "scc_cos_bucket_class", + "options": [ + { + "displayname": "Cold", + "value": "cold" + }, + { + "displayname": "Onerate active", + "value": "onerate_active" + }, + { + "displayname": "Smart", + "value": "smart" + }, + { + "displayname": "Standard", + "value": "standard" + }, + { + "displayname": "Vault", + "value": "vault" + } + ] + }, + { + "key": "management_endpoint_type_for_bucket", + "options": [ + { + "displayname": "Direct", + "value": "direct" + }, + { + "displayname": "Private", + "value": "private" + }, + { + "displayname": "Public", + "value": "public" + } + ] + }, + { + "key": "existing_monitoring_crn" + }, + { + "key": "skip_scc_cos_iam_auth_policy" + }, + { + "key": "existing_kms_instance_crn" + }, + { + "key": "existing_kms_key_crn" + }, + { + "key": "ibmcloud_kms_api_key" + }, + { + "key": "kms_endpoint_type", + "options": [ + { + "displayname": "Public", + "value": "public" + }, + { + "displayname": "Private", + "value": "private" + } + ] + }, + { + "key": "skip_cos_kms_iam_auth_policy" + }, + { + "key": "scc_cos_key_ring_name" + }, + { + "key": "scc_cos_key_name" + }, + { + "key": "force_delete_kms_key" + }, + { + "key": "existing_event_notifications_crn" + }, + { + "key": "event_notifications_source_name" + }, + { + "key": "event_notifications_source_description" + }, + { + "key": "scc_event_notifications_email_list" + }, + { + "key": "scc_event_notifications_from_email" + }, + { + "key": "scc_event_notifications_reply_to_email" + }, + { + "key": "scc_instance_cbr_rules" + } + ], + "iam_permissions": [ + { + "service_name": "compliance", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ] + }, + { + "service_name": "cloud-object-storage", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager" + ] + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "kms" + } + ], + "architecture": { + "descriptions": "This architecture supports creating and configuring a Security and Compliance Center instance.", + "features": [ + { + "title": "SCC instance creation", + "description": "Yes" + }, + { + "title": "Use existing SCC instance", + "description": "Yes" + }, + { + "title": "New resource group creation", + "description": "No" + }, + { + "title": "Use existing resource group", + "description": "Yes" + }, + { + "title": "COS instance creation", + "description": "No" + }, + { + "title": "COS bucket creation", + "description": "Yes" + }, + { + "title": "Enforced KMS encryption", + "description": "No" + }, + { + "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": "SCC scope creation", + "description": "Yes" + }, + { + "title": "SCC attachment creation", + "description": "Yes" + }, + { + "title": "Event Notifications integration", + "description": "Yes" + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Security and Compliance Center", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-scc/main/reference-architecture/scc.svg", + "type": "image/svg+xml" + }, + "description": "This architecture supports creating and configuring IBM Security and Compliance Center resources" + } + ] + } + }, + { + "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": "prefix", + "required": true, + "description": "Prefix to add to all resources that this solution creates. To not use any prefix value, you can enter the string `__NULL__`." + }, + { + "key": "existing_resource_group_name", + "required": true, + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + } + }, + { + "key": "existing_cos_instance_crn", + "required": true + }, + { + "key": "scc_service_plan", + "required": true, + "options": [ + { + "displayname": "Standard", + "value": "security-compliance-center-standard-plan" + }, + { + "displayname": "Trial", + "value": "security-compliance-center-trial-plan" + } + ] + }, + { + "key": "scc_region", + "required": true, + "options": [ + { + "displayname": "Dallas (us-south)", + "value": "us-south" + }, + { + "displayname": "Frankfurt (eu-de)", + "value": "eu-de" + }, + { + "displayname": "Madrid (eu-es)", + "value": "eu-es" + }, + { + "displayname": "Toronto (ca-tor)", + "value": "ca-tor" + } + ] + }, + { + "key": "scc_instance_name" + }, + { + "key": "scc_instance_resource_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "scc_instance_access_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "existing_scc_instance_crn" + }, + { + "key": "scopes" + }, + { + "key": "attachments" + }, + { + "key": "existing_scc_workload_protection_instance_crn" + }, + { + "key": "skip_scc_workload_protection_iam_auth_policy" + }, + { + "key": "custom_integrations" + }, + { + "key": "scc_cos_bucket_name" + }, + { + "key": "scc_cos_bucket_region", + "options": [ + { + "displayname": "Same region as SCC instance", + "value": "__NULL__" + }, + { + "displayname": "Dallas (us-south)", + "value": "us-south" + }, + { + "displayname": "Sydney (au-syd)", + "value": "au-syd" + }, + { + "displayname": "Sao Paolo (br-sao)", + "value": "br-sao" + }, + { + "displayname": "Toronto (ca-tor)", + "value": "ca-tor" + }, + { + "displayname": "Frankfurt (eu-de)", + "value": "eu-de" + }, + { + "displayname": "London (eu-gb)", + "value": "eu-gb" + }, + { + "displayname": "Madrid (eu-es)", + "value": "eu-es" + }, + { + "displayname": "Osaka (jp-osa)", + "value": "jp-osa" + }, + { + "displayname": "Tokyo (jp-tok)", + "value": "jp-tok" + }, + { + "displayname": "Washington DC (us-east)", + "value": "us-east" + } + ] + }, + { + "key": "add_bucket_name_suffix" + }, + { + "key": "scc_cos_bucket_access_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "scc_cos_bucket_class", + "options": [ + { + "displayname": "Cold", + "value": "cold" + }, + { + "displayname": "Onerate active", + "value": "onerate_active" + }, + { + "displayname": "Smart", + "value": "smart" + }, + { + "displayname": "Standard", + "value": "standard" + }, + { + "displayname": "Vault", + "value": "vault" + } + ] + }, + { + "key": "existing_monitoring_crn" + }, + { + "key": "skip_scc_cos_iam_auth_policy" + }, + { + "key": "existing_kms_instance_crn" + }, + { + "key": "existing_kms_key_crn" + }, + { + "key": "ibmcloud_kms_api_key" + }, + { + "key": "skip_cos_kms_iam_auth_policy" + }, + { + "key": "scc_cos_key_ring_name" + }, + { + "key": "scc_cos_key_name" + }, + { + "key": "force_delete_kms_key" + }, + { + "key": "existing_event_notifications_crn" + }, + { + "key": "event_notifications_source_name" + }, + { + "key": "event_notifications_source_description" + }, + { + "key": "scc_event_notifications_email_list" + }, + { + "key": "scc_event_notifications_from_email" + }, + { + "key": "scc_event_notifications_reply_to_email" + }, + { + "key": "scc_instance_cbr_rules" + } + ], + "iam_permissions": [ + { + "service_name": "compliance", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ] + }, + { + "service_name": "cloud-object-storage", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager" + ] + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "kms" + } + ], + "architecture": { + "descriptions": "This architecture supports creating and configuring a Security and Compliance Center instance.", + "features": [ + { + "title": "SCC instance creation", + "description": "Yes" + }, + { + "title": "Use existing SCC instance", + "description": "Yes" + }, + { + "title": "New resource group creation", + "description": "No" + }, + { + "title": "Use existing resource group", + "description": "Yes" + }, + { + "title": "COS instance creation", + "description": "No" + }, + { + "title": "COS bucket creation", + "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": "SCC scope creation", + "description": "Yes" + }, + { + "title": "SCC attachment creation", + "description": "Yes" + }, + { + "title": "Event Notifications integration", + "description": "Yes" + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Security and Compliance Center", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-scc/main/reference-architecture/scc.svg", + "type": "image/svg+xml" + }, + "description": "This architecture supports creating and configuring IBM Security and Compliance Center resources" + } + ] + } + } + ] + } + ] +} diff --git a/images/scc-icon.svg b/images/scc-icon.svg new file mode 100644 index 0000000..c402fe1 --- /dev/null +++ b/images/scc-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/reference-architecture/scc.svg b/reference-architecture/scc.svg new file mode 100644 index 0000000..9a1a852 --- /dev/null +++ b/reference-architecture/scc.svg @@ -0,0 +1,4 @@ + + + +
IBM Cloud
Activity Tracker
service
Existing
Monitoring instance
`
Event Notifications integration
Workload 
Protection 
integration
Custom provider
integration
Region
Existing resource group
SCC instance
Attchments
Scopes
Existing KMS instance
Existing COS instance
COS bucket
AT events
Metrics
KMS key
\ No newline at end of file diff --git a/solutions/fully-configurable/DA-cbr_rules.md b/solutions/fully-configurable/DA-cbr_rules.md new file mode 100644 index 0000000..18456f2 --- /dev/null +++ b/solutions/fully-configurable/DA-cbr_rules.md @@ -0,0 +1,54 @@ +# Configuring Context-Based Restrictions (CBRs) + +The `scc_instance_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: `scc_instance_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 scc_instance_cbr_rules + + - `description` (required): The description of the rule to create. + - `account_id` (required): The IBM Cloud Account ID + - `rule_contexts` (required): (List) The contexts the rule applies to + - `attributes` (optional): (List) Individual context attributes + - `name` (required): The attribute name. + - `value`(required): The attribute value. + + - `enforcement_mode` (required): The rule enforcement mode can have the following values: + - `enabled` - The restrictions are enforced and reported. This is the default. + - `disabled` - The restrictions are disabled. Nothing is enforced or reported. + - `report` - The restrictions are evaluated and reported, but not enforced. + - `operations` (optional): The operations this rule applies to + - `api_types`(required): (List) The API types this rule applies to. + - `api_type_id`(required): The API type ID + + +### Example Rule For Context-Based Restrictions Configuration + +```hcl +[ + { + "description" : "SCC Instance can be accessed from xyz" + "account_id" : "defc0df06b644a9cabc6e44f55b3880s." + "rule_contexts" : [{ + "attributes" : [ + { + "name" : "endpointType", + "value" : "private" + }, + { + "name" : "networkZoneId" + "value" : "93a51a1debe2674193217209601dde6f" + } + ] + }] + "enforcement_mode" : "enabled" + "operations" : [{ + "api_types" : [{ + "api_type_id" : "crn:v1:bluemix:public:context-based-restrictions::::api-type:" + }] + }] + } +] +``` diff --git a/solutions/fully-configurable/README.md b/solutions/fully-configurable/README.md new file mode 100644 index 0000000..615b90a --- /dev/null +++ b/solutions/fully-configurable/README.md @@ -0,0 +1,114 @@ +# Cloud automation for Security and Compliance Center (Fully configurable) + +## Prerequisites +- An existing resource group +- An existing COS instance +- An existing KMS instance (or key) if you wan't to encrypt the COS bucket created for use with SCC + +This solution supports provisioning and configuring the following infrastructure: +- A Security and Compliance Center instance. +- A Cloud Object Storage bucket which is required to store Security and Compliance Center data. +- Security and Compliance Center scopes and attachments. +- Integrations with Workload Protection and other custom providers + +:exclamation: **Important:** This solution is not intended to be called by other modules because it contains a provider configuration and 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). + +![scc-deployable-architecture](../../reference-architecture/scc.svg) + + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [ibm](#requirement\_ibm) | 1.76.1 | +| [time](#requirement\_time) | 0.12.1 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [buckets](#module\_buckets) | terraform-ibm-modules/cos/ibm//modules/buckets | 8.19.8 | +| [existing\_cos\_crn\_parser](#module\_existing\_cos\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | +| [existing\_en\_crn\_parser](#module\_existing\_en\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | +| [existing\_kms\_crn\_parser](#module\_existing\_kms\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | +| [existing\_kms\_key\_crn\_parser](#module\_existing\_kms\_key\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | +| [existing\_scc\_crn\_parser](#module\_existing\_scc\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | +| [kms](#module\_kms) | terraform-ibm-modules/kms-all-inclusive/ibm | 4.21.2 | +| [resource\_group](#module\_resource\_group) | terraform-ibm-modules/resource-group/ibm | 1.1.6 | +| [scc](#module\_scc) | ../.. | n/a | +| [scc\_attachment](#module\_scc\_attachment) | ../../modules/attachment | n/a | + +### Resources + +| Name | Type | +|------|------| +| [ibm_en_subscription_email.email_subscription](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.1/docs/resources/en_subscription_email) | resource | +| [ibm_en_topic.en_topic](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.1/docs/resources/en_topic) | resource | +| [ibm_iam_authorization_policy.cos_kms_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.1/docs/resources/iam_authorization_policy) | resource | +| [ibm_scc_scope.scc_scopes](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.1/docs/resources/scc_scope) | resource | +| [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/0.12.1/docs/resources/sleep) | resource | +| [time_sleep.wait_for_scc](https://registry.terraform.io/providers/hashicorp/time/0.12.1/docs/resources/sleep) | resource | +| [ibm_en_destinations.en_destinations](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.76.1/docs/data-sources/en_destinations) | data source | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [add\_bucket\_name\_suffix](#input\_add\_bucket\_name\_suffix) | Whether to add a generated 4-character suffix to the created Security and Compliance Center Object Storage bucket name. Applies only if not specifying an existing bucket. Set to `false` not to add the suffix to the bucket name in the `scc_cos_bucket_name` variable. Applies only if `existing_scc_instance_crn` is not provided. | `bool` | `true` | no | +| [attachments](#input\_attachments) | A list of attachments to create. A value must be passed for 'scope\_ids' (to use pre-existing scopes) and/or 'scope\_key\_references' (to use scopes created in the 'scopes' input). [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md). |
list(object({
profile_name = string
profile_version = string
attachment_name = string
attachment_description = string
attachment_schedule = string
scope_key_references = optional(list(string), [])
scope_ids = optional(list(string), [])
notifications = object({
enabled = optional(bool, true)
failed_control_ids = optional(list(string), [])
threshold_limit = optional(number, 10)
})
}))
| `[]` | no | +| [custom\_integrations](#input\_custom\_integrations) | A list of custom provider integrations to associate with the Security and Compliance Center instance. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/custom_integrations.md). |
list(object({
attributes = optional(map(string), {})
provider_name = string
integration_name = string
}))
| `[]` | no | +| [event\_notifications\_source\_description](#input\_event\_notifications\_source\_description) | Optional description to give for the Event Notifications integration source. Only used if a value is passed for `event_notifications_instance_crn`. | `string` | `null` | no | +| [event\_notifications\_source\_name](#input\_event\_notifications\_source\_name) | The source name to use for the Event Notifications integration. Required if a value is passed for `event_notifications_instance_crn`. This name must be unique per SCC instance that is integrated with the Event Notifications instance. | `string` | `"compliance"` | no | +| [existing\_cos\_instance\_crn](#input\_existing\_cos\_instance\_crn) | The CRN of an existing Object Storage instance. Not required if passing an existing SCC instance using the `existing_scc_instance_crn` input. | `string` | `null` | no | +| [existing\_event\_notifications\_crn](#input\_existing\_event\_notifications\_crn) | The CRN of an Event Notification instance. Used to integrate with Security and Compliance Center. | `string` | `null` | no | +| [existing\_kms\_instance\_crn](#input\_existing\_kms\_instance\_crn) | The CRN of an existing KMS instance (Hyper Protect Crypto Services or Key Protect). Used to create a new KMS key unless an existing key is passed using the `existing_scc_cos_kms_key_crn` input. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`. A value should not be passed passing existing SCC instance using the `existing_scc_instance_crn` input. | `string` | `null` | no | +| [existing\_kms\_key\_crn](#input\_existing\_kms\_key\_crn) | The CRN of an existing KMS key to use to encrypt the Security and Compliance Center Object Storage bucket. If no value is set for this variable, specify a value for either the `existing_kms_instance_crn` variable to create a key ring and key, or for the `existing_scc_cos_bucket_name` variable to use an existing bucket. | `string` | `null` | no | +| [existing\_monitoring\_crn](#input\_existing\_monitoring\_crn) | The CRN of an IBM Cloud Monitoring instance to to send Security and Compliance Object Storage bucket metrics to. If no value passed, metrics are sent to the instance associated to the container's location unless otherwise specified in the Metrics Router service configuration. Applies only if `existing_scc_instance_crn` is not provided. | `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\_scc\_instance\_crn](#input\_existing\_scc\_instance\_crn) | The CRN of an existing Security and Compliance Center instance. If not supplied, a new instance will be created. | `string` | `null` | no | +| [existing\_scc\_workload\_protection\_instance\_crn](#input\_existing\_scc\_workload\_protection\_instance\_crn) | The CRN of an existing Workload Protection instance to associate with the Security and Compliance Center instance. | `string` | `null` | no | +| [force\_delete\_kms\_key](#input\_force\_delete\_kms\_key) | If creating a new KMS key, toggle whether is should be force deleted or not on undeploy. | `bool` | `false` | 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 Security and Compliance Centre instance. Leave this input empty if the same account owns both instances. | `string` | `null` | no | +| [kms\_encryption\_enabled\_bucket](#input\_kms\_encryption\_enabled\_bucket) | Set to true to enable KMS encryption on the Object Storage bucket created for the Security and Compliance Center instance. When set to true, a value must be passed for either `existing_kms_key_crn` or `existing_kms_instance_crn` (to create a new key). Can not be set to true if passing a value for `existing_scc_instance_crn`. | `bool` | `false` | no | +| [kms\_endpoint\_type](#input\_kms\_endpoint\_type) | The endpoint for communicating with the KMS instance. Possible values: `public`, `private`. Applies only if `kms_encryption_enabled_bucket` is true | `string` | `"private"` | no | +| [management\_endpoint\_type\_for\_bucket](#input\_management\_endpoint\_type\_for\_bucket) | The type of endpoint for the IBM Terraform provider to use to manage Object Storage buckets. Possible values: `public`, `private`m `direct`. If you specify `private`, enable virtual routing and forwarding in your account, and the Terraform runtime must have access to the the IBM Cloud private network. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"private"` | no | +| [prefix](#input\_prefix) | The prefix to add to all resources that this solution creates (e.g `prod`, `test`, `dev`). 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 | +| [scc\_cos\_bucket\_access\_tags](#input\_scc\_cos\_bucket\_access\_tags) | The list of access tags to add to the Security and Compliance Center Object Storage bucket. Applies only if `existing_scc_instance_crn` is not provided. | `list(string)` | `[]` | no | +| [scc\_cos\_bucket\_class](#input\_scc\_cos\_bucket\_class) | The storage class of the newly provisioned Security and Compliance Center Object Storage bucket. Possible values: `standard`, `vault`, `cold`, `smart`, `onerate_active`. [Learn more](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-classes). Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"smart"` | no | +| [scc\_cos\_bucket\_name](#input\_scc\_cos\_bucket\_name) | The name for the Security and Compliance Center Object Storage bucket. Bucket names must globally unique. If `add_bucket_name_suffix` is true, a 4-character string is added to this name to ensure it's globally unique. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"scc-cos-bucket"` | no | +| [scc\_cos\_bucket\_region](#input\_scc\_cos\_bucket\_region) | The region to create the Object Storage bucket used by SCC. If not provided, the region specified in the `scc_region` input will be used. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `null` | no | +| [scc\_cos\_key\_name](#input\_scc\_cos\_key\_name) | The name for the key created for the Security and Compliance Center Object Storage bucket. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format. | `string` | `"scc-cos-key"` | no | +| [scc\_cos\_key\_ring\_name](#input\_scc\_cos\_key\_ring\_name) | The name for the key ring created for the Security and Compliance Center Object Storage bucket key. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format. | `string` | `"scc-cos-key-ring"` | no | +| [scc\_event\_notifications\_email\_list](#input\_scc\_event\_notifications\_email\_list) | The list of email addresses to notify when Security and Compliance Center triggers an event. | `list(string)` | `[]` | no | +| [scc\_event\_notifications\_from\_email](#input\_scc\_event\_notifications\_from\_email) | The `from` email address used in any Security and Compliance Center events coming via Event Notifications. | `string` | `"compliancealert@ibm.com"` | no | +| [scc\_event\_notifications\_reply\_to\_email](#input\_scc\_event\_notifications\_reply\_to\_email) | The `reply_to` email address used in any Security and Compliance Center events coming via Event Notifications. | `string` | `"no-reply@ibm.com"` | no | +| [scc\_instance\_access\_tags](#input\_scc\_instance\_access\_tags) | The list of access tags to add to the Security and Compliance Center instance. | `list(string)` | `[]` | no | +| [scc\_instance\_cbr\_rules](#input\_scc\_instance\_cbr\_rules) | (Optional, list) List of context-based restrictions rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/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 | +| [scc\_instance\_name](#input\_scc\_instance\_name) | The name for the Security and Compliance Center instance provisioned by this solution. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"scc"` | no | +| [scc\_instance\_resource\_tags](#input\_scc\_instance\_resource\_tags) | The list of tags to add to the Security and Compliance Center instance. Applies only if `existing_scc_instance_crn` is not provided. | `list(string)` | `[]` | no | +| [scc\_region](#input\_scc\_region) | The region to provision Security and Compliance Center resources in. If passing a value for `existing_scc_instance_crn`, ensure to select the region of the existing instance. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"us-south"` | no | +| [scc\_service\_plan](#input\_scc\_service\_plan) | The pricing plan to use when creating a new Security Compliance Center instance. Possible values: `security-compliance-center-standard-plan`, `security-compliance-center-trial-plan`. | `string` | `"security-compliance-center-standard-plan"` | no | +| [scopes](#input\_scopes) | A key map of scopes to create. The key name of each scope can be referenced in the attachments input using the 'scope\_key\_references' attribute. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md). |
map(object({
name = string
description = string
environment = optional(string, "ibm-cloud")
properties = object({
scope_id = string
scope_type = string
})
exclusions = optional(list(object({
scope_id = string
scope_type = string
})), [])
}))
| `{}` | no | +| [skip\_cos\_kms\_iam\_auth\_policy](#input\_skip\_cos\_kms\_iam\_auth\_policy) | Set to `true` to skip the creation of an IAM authorization policy that permits the Object Storage instance created 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. Applies only if `existing_scc_instance_crn` is not provided. | `bool` | `false` | no | +| [skip\_scc\_cos\_iam\_auth\_policy](#input\_skip\_scc\_cos\_iam\_auth\_policy) | Set to `true` to skip creation of an IAM authorization policy that permits the Security and Compliance Center to write to the Object Storage instance created by this solution. Applies only if `existing_scc_instance_crn` is not provided. | `bool` | `false` | no | +| [skip\_scc\_workload\_protection\_iam\_auth\_policy](#input\_skip\_scc\_workload\_protection\_iam\_auth\_policy) | Set to `true` to skip creating an IAM authorization policy that permits the Security and Compliance Center instance to read from the Workload Protection instance. Applies only if a value is passed for `existing_scc_workload_protection_instance_crn`. | `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 | +| [scc\_cos\_bucket\_config](#output\_scc\_cos\_bucket\_config) | List of buckets created | +| [scc\_cos\_bucket\_name](#output\_scc\_cos\_bucket\_name) | SCC COS bucket name | +| [scc\_cos\_instance\_crn](#output\_scc\_cos\_instance\_crn) | SCC COS instance CRN | +| [scc\_cos\_kms\_key\_crn](#output\_scc\_cos\_kms\_key\_crn) | SCC COS KMS Key CRN | +| [scc\_crn](#output\_scc\_crn) | SCC instance CRN | +| [scc\_guid](#output\_scc\_guid) | SCC instance guid | +| [scc\_id](#output\_scc\_id) | SCC instance ID | +| [scc\_name](#output\_scc\_name) | SCC instance name | + diff --git a/solutions/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template new file mode 100644 index 0000000..252ec00 --- /dev/null +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -0,0 +1,8 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "prefix": $PREFIX, + "existing_resource_group_name": "geretain-test-resources", + "kms_encryption_enabled_bucket": true, + "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN, + "existing_cos_instance_crn": $COS_INSTANCE_CRN +} diff --git a/solutions/fully-configurable/custom_integrations.md b/solutions/fully-configurable/custom_integrations.md new file mode 100644 index 0000000..dd7bf27 --- /dev/null +++ b/solutions/fully-configurable/custom_integrations.md @@ -0,0 +1,35 @@ +# Configuring custom integrations + +Custom provider integrations can be configured for the Security and Compliance Center instance using the `custom_integrations` input. + +:information_source: If you wan't to integrate with an SCC Workload Protection instance, simply use the `existing_scc_workload_protection_instance_crn` input instead. + +## Options for custom_integrations +The `custom_integrations` input a list type input which supporting configuring multiple integrations. Each entry in the list is a map object with the following options: + +- `attributes` (optional, default = `{}`): an optional map of string attributes +- `provider_name` (required): The unique provider name +- `integration_name` (required): The name to give the integration + +- The following example shows how to create an integration with the Caveonix provider: + + ``` + [ + { + provider_name = "Caveonix" + integration_name = "caveonix-integration" + } + ] + ``` + +- The following example shows how to create an integration with a provider that requires attributes: + + ``` + [ + { + provider_name = "Sample" + integration_name = "sample-integration" + attributes = {"description": "this is a sample"} + } + ] + ``` diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf new file mode 100644 index 0000000..10c6b43 --- /dev/null +++ b/solutions/fully-configurable/main.tf @@ -0,0 +1,321 @@ +###################################################################################################################### +# Resource Group +###################################################################################################################### + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.1.6" + existing_resource_group_name = var.existing_resource_group_name +} + +####################################################################################################################### +# KMS Key +####################################################################################################################### + +module "existing_kms_crn_parser" { + count = var.existing_kms_instance_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_kms_instance_crn +} + +module "existing_kms_key_crn_parser" { + count = var.existing_kms_key_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_kms_key_crn +} + +locals { + prefix = (var.prefix != null && trimspace(var.prefix) != "" ? "${var.prefix}-" : "") + kms_region = var.existing_kms_instance_crn != null ? module.existing_kms_crn_parser[0].region : var.existing_kms_key_crn != null ? module.existing_kms_key_crn_parser[0].region : null + existing_kms_guid = var.existing_kms_instance_crn != null ? module.existing_kms_crn_parser[0].service_instance : var.existing_kms_key_crn != null ? module.existing_kms_key_crn_parser[0].service_instance : null + kms_service_name = var.existing_kms_instance_crn != null ? module.existing_kms_crn_parser[0].service_name : var.existing_kms_key_crn != null ? module.existing_kms_key_crn_parser[0].service_name : null + kms_account_id = var.existing_kms_instance_crn != null ? module.existing_kms_crn_parser[0].account_id : var.existing_kms_key_crn != null ? module.existing_kms_key_crn_parser[0].account_id : null + kms_key_id = var.existing_kms_instance_crn != null ? module.kms[0].keys[format("%s.%s", local.scc_cos_key_ring_name, local.scc_cos_key_name)].key_id : var.existing_kms_key_crn != null ? module.existing_kms_key_crn_parser[0].resource : null + scc_cos_key_ring_name = "${local.prefix}${var.scc_cos_key_ring_name}" + scc_cos_key_name = "${local.prefix}${var.scc_cos_key_name}" + scc_cos_bucket_region = var.scc_cos_bucket_region != null && var.scc_cos_bucket_region != "" ? var.scc_cos_bucket_region : var.scc_region + scc_instance_name = "${local.prefix}${var.scc_instance_name}" + # Bucket name to be passed to the COS module to create a bucket + created_scc_cos_bucket_name = "${local.prefix}${var.scc_cos_bucket_name}" + # Final COS bucket name after being created by COS module (as it might have suffix added to it) + scc_cos_bucket_name = var.existing_scc_instance_crn == null ? module.buckets[0].buckets[local.created_scc_cos_bucket_name].bucket_name : null + create_cross_account_auth_policy = var.existing_scc_instance_crn == null ? !var.skip_cos_kms_iam_auth_policy && var.ibmcloud_kms_api_key == null ? false : (module.scc.account_id != module.existing_kms_crn_parser[0].account_id) : false +} + +# Create IAM Authorization Policy to allow COS to access KMS for the encryption key, if cross account KMS is passed in +resource "ibm_iam_authorization_policy" "cos_kms_policy" { + count = local.create_cross_account_auth_policy ? 1 : 0 + provider = ibm.kms + source_service_account = module.scc.account_id + source_service_name = "cloud-object-storage" + source_resource_instance_id = local.cos_instance_guid + roles = ["Reader"] + description = "Allow the COS instance ${local.cos_instance_guid} to read the ${local.kms_service_name} key ${local.kms_key_id} from the instance ${local.existing_kms_guid}" + resource_attributes { + name = "serviceName" + operator = "stringEquals" + value = local.kms_service_name + } + resource_attributes { + name = "accountId" + operator = "stringEquals" + value = local.kms_account_id + } + resource_attributes { + name = "serviceInstance" + operator = "stringEquals" + value = local.existing_kms_guid + } + resource_attributes { + name = "resourceType" + operator = "stringEquals" + value = "key" + } + resource_attributes { + name = "resource" + operator = "stringEquals" + value = local.kms_key_id + } + # Scope of policy now includes the key, so ensure to create new policy before + # destroying old one to prevent any disruption to every day services. + lifecycle { + create_before_destroy = true + } +} + +# 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.cos_kms_policy] + count = local.create_cross_account_auth_policy ? 1 : 0 + + create_duration = "30s" +} + +# KMS root key for SCC COS bucket +module "kms" { + providers = { + ibm = ibm.kms + } + count = var.existing_scc_instance_crn == null && var.kms_encryption_enabled_bucket && var.existing_kms_key_crn == null ? 1 : 0 + source = "terraform-ibm-modules/kms-all-inclusive/ibm" + version = "4.21.2" + create_key_protect_instance = false + region = local.kms_region + existing_kms_instance_crn = var.existing_kms_instance_crn + key_ring_endpoint_type = var.kms_endpoint_type + key_endpoint_type = var.kms_endpoint_type + keys = [ + { + key_ring_name = local.scc_cos_key_ring_name + existing_key_ring = false + keys = [ + { + key_name = local.scc_cos_key_name + standard_key = false + rotation_interval_month = 3 + dual_auth_delete_enabled = false + force_delete = var.force_delete_kms_key + } + ] + } + ] +} + +####################################################################################################################### +# COS +####################################################################################################################### + +module "existing_cos_crn_parser" { + count = var.existing_scc_instance_crn == null && var.existing_cos_instance_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_cos_instance_crn +} + +locals { + scc_cos_kms_key_crn = var.existing_scc_instance_crn == null && var.kms_encryption_enabled_bucket ? var.existing_kms_key_crn != null ? var.existing_kms_key_crn : module.kms[0].keys[format("%s.%s", local.scc_cos_key_ring_name, local.scc_cos_key_name)].crn : null + cos_instance_guid = var.existing_scc_instance_crn == null ? var.existing_cos_instance_crn != null ? module.existing_cos_crn_parser[0].service_instance : null : null + bucket_config = [{ + access_tags = var.scc_cos_bucket_access_tags + add_bucket_name_suffix = var.add_bucket_name_suffix + bucket_name = local.created_scc_cos_bucket_name + kms_encryption_enabled = var.kms_encryption_enabled_bucket + kms_guid = local.existing_kms_guid + kms_key_crn = local.scc_cos_kms_key_crn + skip_iam_authorization_policy = local.create_cross_account_auth_policy || var.skip_cos_kms_iam_auth_policy + management_endpoint_type = var.management_endpoint_type_for_bucket + storage_class = var.scc_cos_bucket_class + resource_instance_id = var.existing_cos_instance_crn + region_location = local.scc_cos_bucket_region + force_delete = true # If this is set to false, and the bucket contains data, the destroy will fail. Setting it to false on destroy has no impact, it has to be set on apply, so hence hard coding to true." + activity_tracking = { + read_data_events = true + write_data_events = true + management_events = true + } + metrics_monitoring = { + usage_metrics_enabled = true + request_metrics_enabled = true + metrics_monitoring_crn = var.existing_monitoring_crn + } + }] +} + +# Create bucket +module "buckets" { + providers = { + ibm = ibm.cos + } + count = var.existing_scc_instance_crn == null ? 1 : 0 + depends_on = [time_sleep.wait_for_authorization_policy[0]] + source = "terraform-ibm-modules/cos/ibm//modules/buckets" + version = "8.19.8" + bucket_configs = local.bucket_config +} + +####################################################################################################################### +# SCC Instance +####################################################################################################################### + +module "existing_scc_crn_parser" { + count = var.existing_scc_instance_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_scc_instance_crn +} + +locals { + scc_instance_region = var.existing_scc_instance_crn == null ? var.scc_region : module.existing_scc_crn_parser[0].region +} + +module "scc" { + source = "../.." + existing_scc_instance_crn = var.existing_scc_instance_crn + resource_group_id = module.resource_group.resource_group_id + region = var.scc_region + instance_name = local.scc_instance_name + plan = var.scc_service_plan + cos_bucket = local.scc_cos_bucket_name + cos_instance_crn = var.existing_cos_instance_crn + enable_event_notifications_integration = var.existing_event_notifications_crn == null ? false : true + en_instance_crn = var.existing_event_notifications_crn + en_source_name = var.event_notifications_source_name + en_source_description = var.event_notifications_source_description + skip_cos_iam_authorization_policy = var.skip_scc_cos_iam_auth_policy + resource_tags = var.scc_instance_resource_tags + attach_wp_to_scc_instance = var.existing_scc_workload_protection_instance_crn != null ? true : false + wp_instance_crn = var.existing_scc_workload_protection_instance_crn + skip_scc_wp_auth_policy = var.skip_scc_workload_protection_iam_auth_policy + cbr_rules = var.scc_instance_cbr_rules + custom_integrations = var.custom_integrations + access_tags = var.scc_instance_access_tags +} + +####################################################################################################################### +# SCC scopes +####################################################################################################################### + +resource "ibm_scc_scope" "scc_scopes" { + for_each = var.scopes + description = each.value.description + environment = each.value.environment + instance_id = module.scc.guid + name = each.value.name + properties = each.value.properties + + dynamic "exclusions" { + for_each = each.value.exclusions + content { + scope_id = exclusions.value.scope_id + scope_type = exclusions.value.scope_type + } + } +} + +####################################################################################################################### +# SCC attachments +####################################################################################################################### + +module "scc_attachment" { + + for_each = { + for index, attachment in var.attachments : + attachment.attachment_name => attachment + } + + source = "../../modules/attachment" + profile_name = each.value.profile_name + profile_version = each.value.profile_version + scc_instance_id = module.scc.guid + attachment_name = each.value.attachment_name + attachment_description = each.value.attachment_description + attachment_schedule = each.value.attachment_schedule + # lookup the scope ID created using 'scope_key_references' value + # concat the 'scope_key_references' computed IDs with the IDs listed in the 'scope_ids' attribute + scope_ids = concat([for s in each.value.scope_key_references : ibm_scc_scope.scc_scopes[s].scope_id], each.value.scope_ids) +} + +####################################################################################################################### +# SCC Event Notifications Configuration +####################################################################################################################### + +module "existing_en_crn_parser" { + count = var.existing_event_notifications_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_event_notifications_crn +} + +locals { + existing_en_guid = var.existing_event_notifications_crn != null ? module.existing_en_crn_parser[0].service_instance : null + existing_en_region = var.existing_event_notifications_crn != null ? module.existing_en_crn_parser[0].region : null +} + +data "ibm_en_destinations" "en_destinations" { + provider = ibm.en + count = var.existing_event_notifications_crn != null ? 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_scc" { + count = var.existing_event_notifications_crn != null && var.existing_scc_instance_crn == null ? 1 : 0 + depends_on = [module.scc] + + create_duration = "60s" +} + +resource "ibm_en_topic" "en_topic" { + provider = ibm.en + count = var.existing_event_notifications_crn != null && var.existing_scc_instance_crn == null ? 1 : 0 + depends_on = [time_sleep.wait_for_scc] + instance_guid = local.existing_en_guid + name = "Topic for SCC instance ${module.scc.guid}" + description = "Topic for SCC events routing" + sources { + id = module.scc.crn + rules { + enabled = true + event_type_filter = "$.*" + } + } +} + +resource "ibm_en_subscription_email" "email_subscription" { + provider = ibm.en + count = var.existing_event_notifications_crn != null && var.existing_scc_instance_crn == null && length(var.scc_event_notifications_email_list) > 0 ? 1 : 0 + instance_guid = local.existing_en_guid + name = "Subscription email for SCC instance ${module.scc.guid}" + description = "Subscription for Security and Compliance Center Events" + destination_id = [for s in toset(data.ibm_en_destinations.en_destinations[count.index].destinations) : s.id if s.type == "smtp_ibm"][0] + topic_id = ibm_en_topic.en_topic[count.index].topic_id + attributes { + add_notification_payload = true + reply_to_mail = var.scc_event_notifications_reply_to_email + reply_to_name = "SCC Event Notifications Bot" + from_name = var.scc_event_notifications_from_email + invited = var.scc_event_notifications_email_list + } +} diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf new file mode 100644 index 0000000..abf5798 --- /dev/null +++ b/solutions/fully-configurable/outputs.tf @@ -0,0 +1,57 @@ +######################################################################################################################## +# Outputs +######################################################################################################################## + +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 "scc_id" { + description = "SCC instance ID" + value = module.scc.id +} + +output "scc_guid" { + description = "SCC instance guid" + value = module.scc.guid +} + +output "scc_crn" { + description = "SCC instance CRN" + value = module.scc.crn +} + +output "scc_name" { + description = "SCC instance name" + value = module.scc.name +} + +######################################################################################################################## +# SCC COS +######################################################################################################################## + +output "scc_cos_kms_key_crn" { + description = "SCC COS KMS Key CRN" + value = local.scc_cos_kms_key_crn +} + +output "scc_cos_bucket_name" { + description = "SCC COS bucket name" + value = var.existing_scc_instance_crn == null ? local.scc_cos_bucket_name : null +} + +output "scc_cos_bucket_config" { + description = "List of buckets created" + value = var.existing_scc_instance_crn == null ? module.buckets[0].buckets[local.created_scc_cos_bucket_name] : null +} + +output "scc_cos_instance_crn" { + description = "SCC COS instance CRN" + value = var.existing_cos_instance_crn +} diff --git a/solutions/fully-configurable/provider.tf b/solutions/fully-configurable/provider.tf new file mode 100644 index 0000000..9438293 --- /dev/null +++ b/solutions/fully-configurable/provider.tf @@ -0,0 +1,30 @@ +######################################################################################################################## +# Provider config +######################################################################################################################## + +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = local.scc_instance_region + visibility = var.provider_visibility +} + +provider "ibm" { + alias = "kms" + ibmcloud_api_key = var.ibmcloud_kms_api_key != null ? var.ibmcloud_kms_api_key : var.ibmcloud_api_key + region = local.kms_region + visibility = var.provider_visibility +} + +provider "ibm" { + alias = "cos" + ibmcloud_api_key = var.ibmcloud_api_key + region = local.scc_cos_bucket_region + visibility = var.provider_visibility +} + +provider "ibm" { + alias = "en" + ibmcloud_api_key = var.ibmcloud_api_key + region = local.existing_en_region + visibility = var.provider_visibility +} diff --git a/solutions/fully-configurable/scopes_attachments.md b/solutions/fully-configurable/scopes_attachments.md new file mode 100644 index 0000000..0d60b75 --- /dev/null +++ b/solutions/fully-configurable/scopes_attachments.md @@ -0,0 +1,101 @@ +# Creating Scopes and Attachments + +It is possible to create multiple scopes and multiple attachments in a Security and Compliance Centre instance using the `scopes` and `attachments` inputs. + +## Scopes +The `scopes` input can be used to create multiple scopes, which can then be used in 1 or more attachments. The input is an object map type, and each scope must be created using a map key identifier with the following attributes: + +- `name` (required): The name of the scope +- `description` (required): The description of the scope +- `environment` (optional, default = "ibm-cloud"): The scope environment +- `properties` (required): A map of properties containing: + - `scope_type` (required): The type of target the scope will cover. Acceptable values are: `account`, `account.resource_group`, `enterprise.account_group`, `enterprise` + - `scope_id` (required): The ID of the target defined in `scope_type` +- `exclusions` (optional): A list exclusions in the format of a map of strings containing: + - `scope_type` (required): The type of target the exclusion will cover. Acceptable values are: `account`, `account.resource_group`, `enterprise.account_group`, `enterprise` + - `scope_id` (required): The ID of the exclusion target defined in `scope_type` + +- The following example shows how to create a scope for the full account: + ``` + { + all = { + name = "Full account" + description = "Scope to scan the whole account" + environment = "ibm-cloud" + properties = { + scope_id = "abac0df06b644a9cabc6e44f55b3880e" + scope_type = "account" + } + } + } + ``` + - The key identifier for above example is `all`. This can be referenced when creating attachments using the `scope_key_references` attribute of the [attachments](#attachments) input. + +- The following example shows how to exclude a resource group from the scope: + ``` + { + exclude = { + name = "Exclude Default resource group" + description = "Scope to exclude the Default resource group" + environment = "ibm-cloud" + properties = { + scope_id = "abac0df06b644a9cabc6e44f55b3880e" + scope_type = "account" + } + exclusions = [{ + scope_id = "07b6d899988a4631841e3bc7d0307dcf" + scope_type = "account.resource_group" + }] + } + } + ``` + - The key identifier for above example is `exclude`. This can be referenced when creating attachments using the `scope_key_references` attribute of the [attachments](#attachments) input. + +## Attachments +The `attachments` input can be used to create multiple attachments using either pre-existing scopes, or scopes that were created with the [scopes](#scopes) input. The input type is a list of objects with the following attributes: + +- `profile_name` (required): The name of an existing profile to use for the attachments creation. +- `profile_version` (required): The version to use of the profile specified in `profile_name`. Supports the string `latest` to use the latest version. +- `attachment_name` (required): The name to give the attachment. +- `attachment_description` (required): The description of the attachment. +- `attachment_schedule` (required): The attachment schedule. Acceptable values are: `daily`, `every_7_days`, `every_30_days`, `none`. +- `scope_key_references` (optional): A list of key identifier strings for scopes that were created using the [attachments](#attachments) input that you want to include in the attachment. To use a pre-existing scope, you can use the `scope_ids` option below. Both inputs cannot be an empty list. +- `scope_ids` (optional): A list of pre-existing scope IDs to include in the attachment. To use a scope that was created with the `scopes input variable, use the `scope_key_references` option above. Both inputs cannot be an empty list. +- `notifications` (optional): A map of notification settings containing: + - `enabled` (optional, default: `true`): true of false to enable or disable notifications + - `failed_control_ids` (optional, default: `[]`): A list of failed control IDs to be notified of + - `threshold_limit` (optional, default: `10`): The threshold limit + +- The following example shows how to create an attachment with a daily schedule using a scope that was created with the first `scopes` example above: + ``` + [{ + profile_name = "SOC 2" + profile_version = "latest" + attachment_name = "SOC 2 full account" + attachment_description = "SOC 2 full account" + attachment_schedule = "daily" + scope_key_references = ["all"] + notifications = { + enabled = true + failed_control_ids = ["c51c5094-6f6b-4fee-b0f6-ad51ca68e18a"] + threshold_limit = 10 + } + }] + ``` + +- The following example shows how to create an attachment with a 30 day schedule using a scope IDs that already exist: + ``` + [{ + profile_name = "SOC 2" + profile_version = "latest" + attachment_name = "SOC 2" + attachment_description = "SOC 2" + attachment_schedule = "every_30_days" + scope_ids = ["e59990c6-ddec-49b7-9656-0dcfcdcaa6cf", "e8be9035-6c43-4035-b8a3-37ef63786e5c"] + notifications = { + enabled = true + failed_control_ids = ["c51c5094-6f6b-4fee-b0f6-ad51ca68e18a"] + threshold_limit = 10 + } + }] + ``` diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf new file mode 100644 index 0000000..a70fee0 --- /dev/null +++ b/solutions/fully-configurable/variables.tf @@ -0,0 +1,422 @@ +######################################################################################################################## +# 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" +} + +variable "prefix" { + type = string + description = "The prefix to add to all resources that this solution creates (e.g `prod`, `test`, `dev`). To not use any prefix value, you can set this value to `null` or an empty string." + nullable = true + 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." + } +} + +variable "provider_visibility" { + description = "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)." + type = string + default = "private" + + validation { + condition = contains(["public", "private", "public-and-private"], var.provider_visibility) + error_message = "Invalid value for 'provider_visibility'. Allowed values are 'public', 'private', or 'public-and-private'." + } +} + +######################################################################################################################## +# SCC variables +######################################################################################################################## + +variable "scc_instance_name" { + type = string + default = "scc" + description = "The name for the Security and Compliance Center instance provisioned by this solution. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_region" { + type = string + default = "us-south" + description = "The region to provision Security and Compliance Center resources in. If passing a value for `existing_scc_instance_crn`, ensure to select the region of the existing instance. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_service_plan" { + type = string + description = "The pricing plan to use when creating a new Security Compliance Center instance. Possible values: `security-compliance-center-standard-plan`, `security-compliance-center-trial-plan`." + default = "security-compliance-center-standard-plan" + validation { + condition = contains(["security-compliance-center-standard-plan", "security-compliance-center-trial-plan"], var.scc_service_plan) + error_message = "Allowed values for scc_service_plan are \"security-compliance-center-standard-plan\" and \"security-compliance-center-trial-plan\"." + } +} + +variable "scc_instance_resource_tags" { + type = list(string) + description = "The list of tags to add to the Security and Compliance Center instance. Applies only if `existing_scc_instance_crn` is not provided." + default = [] +} + +variable "scc_instance_access_tags" { + type = list(string) + description = "The list of access tags to add to the Security and Compliance Center instance." + default = [] +} + +variable "existing_scc_instance_crn" { + type = string + default = null + description = "The CRN of an existing Security and Compliance Center instance. If not supplied, a new instance will be created." +} + +variable "custom_integrations" { + type = list(object({ + attributes = optional(map(string), {}) + provider_name = string + integration_name = string + })) + description = "A list of custom provider integrations to associate with the Security and Compliance Center instance. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/custom_integrations.md)." + default = [] + # Since this list is used in a for_each, add nullable = false to prevent error if user passes null + nullable = false +} + +variable "scopes" { + type = map(object({ + name = string + description = string + environment = optional(string, "ibm-cloud") + properties = object({ + scope_id = string + scope_type = string + }) + exclusions = optional(list(object({ + scope_id = string + scope_type = string + })), []) + })) + default = {} + nullable = false + description = "A key map of scopes to create. The key name of each scope can be referenced in the attachments input using the 'scope_key_references' attribute. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md)." +} + +variable "attachments" { + type = list(object({ + profile_name = string + profile_version = string + attachment_name = string + attachment_description = string + attachment_schedule = string + scope_key_references = optional(list(string), []) + scope_ids = optional(list(string), []) + notifications = object({ + enabled = optional(bool, true) + failed_control_ids = optional(list(string), []) + threshold_limit = optional(number, 10) + }) + })) + default = [] + description = "A list of attachments to create. A value must be passed for 'scope_ids' (to use pre-existing scopes) and/or 'scope_key_references' (to use scopes created in the 'scopes' input). [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md)." + + validation { + condition = alltrue([for attachments in var.attachments : + length(attachments.scope_ids) > 0 || length(attachments.scope_key_references) > 0 + ]) + error_message = "At least one value needs to be added to the 'scope_ids' or 'scope_key_references' list inputs." + } + # TODO: Add validation to validate values in scope_key_references match the key identifier in the keys(var.scopes) +} + +######################################################################################################################## +# Workload Protection +######################################################################################################################## + +variable "existing_scc_workload_protection_instance_crn" { + type = string + description = "The CRN of an existing Workload Protection instance to associate with the Security and Compliance Center instance." + default = null +} + +variable "skip_scc_workload_protection_iam_auth_policy" { + type = bool + default = false + description = "Set to `true` to skip creating an IAM authorization policy that permits the Security and Compliance Center instance to read from the Workload Protection instance. Applies only if a value is passed for `existing_scc_workload_protection_instance_crn`." +} + +######################################################################################################################## +# KMS variables +######################################################################################################################## + +variable "kms_encryption_enabled_bucket" { + description = "Set to true to enable KMS encryption on the Object Storage bucket created for the Security and Compliance Center instance. When set to true, a value must be passed for either `existing_kms_key_crn` or `existing_kms_instance_crn` (to create a new key). Can not be set to true if passing a value for `existing_scc_instance_crn`." + type = bool + default = false + + validation { + condition = var.kms_encryption_enabled_bucket ? var.existing_scc_instance_crn == null : true + error_message = "'kms_encryption_enabled_bucket' should be false if passing a value for 'existing_scc_instance_crn' as existing SCC instance will already have a bucket attached." + } + + validation { + condition = var.existing_kms_instance_crn != null ? var.kms_encryption_enabled_bucket : true + error_message = "If passing a value for 'existing_kms_instance_crn', you should set 'kms_encryption_enabled_bucket' to true." + } + + validation { + condition = var.existing_kms_key_crn != null ? var.kms_encryption_enabled_bucket : true + error_message = "If passing a value for 'existing_kms_key_crn', you should set 'kms_encryption_enabled_bucket' to true." + } + + validation { + condition = var.kms_encryption_enabled_bucket ? ((var.existing_kms_key_crn != null || var.existing_kms_instance_crn != null) ? true : false) : true + error_message = "Either 'existing_kms_key_crn' or 'existing_kms_instance_crn' is required if 'kms_encryption_enabled_bucket' is set to true." + } +} + +variable "existing_kms_instance_crn" { + type = string + default = null + description = "The CRN of an existing KMS instance (Hyper Protect Crypto Services or Key Protect). Used to create a new KMS key unless an existing key is passed using the `existing_scc_cos_kms_key_crn` input. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`. A value should not be passed passing existing SCC instance using the `existing_scc_instance_crn` input." + + 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_scc_instance_crn == null : true + error_message = "A value should not be passed for 'existing_kms_instance_crn' when passing an existing SCC instance using the 'existing_scc_instance_crn' input." + } +} + +variable "force_delete_kms_key" { + type = bool + default = false + description = "If creating a new KMS key, toggle whether is should be force deleted or not on undeploy." +} + +variable "existing_kms_key_crn" { + type = string + default = null + description = "The CRN of an existing KMS key to use to encrypt the Security and Compliance Center Object Storage bucket. If no value is set for this variable, specify a value for either the `existing_kms_instance_crn` variable to create a key ring and key, or for the `existing_scc_cos_bucket_name` variable to use an existing bucket." + + 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}:key:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", var.existing_kms_key_crn)), + var.existing_kms_key_crn == null, + ]) + error_message = "The provided KMS key CRN in the input 'existing_kms_key_crn' in not valid." + } + + validation { + condition = var.existing_kms_key_crn != null ? var.existing_scc_instance_crn == null : true + error_message = "A value should not be passed for 'existing_kms_key_crn' when passing an existing SCC instance using the 'existing_scc_instance_crn' input." + } + + validation { + condition = var.existing_kms_key_crn != null ? var.existing_kms_instance_crn == null : true + error_message = "A value should not be passed for 'existing_kms_instance_crn' when passing an existing key value using the 'existing_kms_key_crn' input." + } + +} + +variable "kms_endpoint_type" { + type = string + description = "The endpoint for communicating with the KMS instance. Possible values: `public`, `private`. Applies only if `kms_encryption_enabled_bucket` is true" + default = "private" + validation { + condition = can(regex("public|private", var.kms_endpoint_type)) + error_message = "The kms_endpoint_type value must be 'public' or 'private'." + } +} + +variable "scc_cos_key_ring_name" { + type = string + default = "scc-cos-key-ring" + description = "The name for the key ring created for the Security and Compliance Center Object Storage bucket key. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format." +} + +variable "scc_cos_key_name" { + type = string + default = "scc-cos-key" + description = "The name for the key created for the Security and Compliance Center Object Storage bucket. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` 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 Security and Compliance Centre instance. Leave this input empty if the same account owns both instances." + sensitive = true + default = null + + validation { + condition = var.ibmcloud_kms_api_key != null ? var.existing_scc_instance_crn == null : true + error_message = "A value should not be passed for 'ibmcloud_kms_api_key' when passing an existing SCC instance using the 'existing_scc_instance_crn' input." + } +} + +######################################################################################################################## +# COS variables +######################################################################################################################## + +variable "existing_cos_instance_crn" { + type = string + nullable = true + default = null + description = "The CRN of an existing Object Storage instance. Not required if passing an existing SCC instance using the `existing_scc_instance_crn` input." + + validation { + condition = var.existing_cos_instance_crn == null ? var.existing_scc_instance_crn != null : true + error_message = "A value must be passed for 'existing_cos_instance_crn' when creating a new instance." + } +} + +variable "scc_cos_bucket_region" { + type = string + default = null + description = "The region to create the Object Storage bucket used by SCC. If not provided, the region specified in the `scc_region` input will be used. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_cos_bucket_name" { + type = string + default = "scc-cos-bucket" + description = "The name for the Security and Compliance Center Object Storage bucket. Bucket names must globally unique. If `add_bucket_name_suffix` is true, a 4-character string is added to this name to ensure it's globally unique. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "add_bucket_name_suffix" { + type = bool + description = "Whether to add a generated 4-character suffix to the created Security and Compliance Center Object Storage bucket name. Applies only if not specifying an existing bucket. Set to `false` not to add the suffix to the bucket name in the `scc_cos_bucket_name` variable. Applies only if `existing_scc_instance_crn` is not provided." + default = true +} + +variable "scc_cos_bucket_access_tags" { + type = list(string) + default = [] + description = "The list of access tags to add to the Security and Compliance Center Object Storage bucket. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_cos_bucket_class" { + type = string + default = "smart" + description = "The storage class of the newly provisioned Security and Compliance Center Object Storage bucket. Possible values: `standard`, `vault`, `cold`, `smart`, `onerate_active`. [Learn more](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-classes). Applies only if `existing_scc_instance_crn` is not provided." + validation { + condition = contains(["standard", "vault", "cold", "smart", "onerate_active"], var.scc_cos_bucket_class) + error_message = "Allowed values for cos_bucket_class are \"standard\", \"vault\",\"cold\", \"smart\", or \"onerate_active\"." + } +} + +variable "skip_scc_cos_iam_auth_policy" { + type = bool + default = false + description = "Set to `true` to skip creation of an IAM authorization policy that permits the Security and Compliance Center to write to the Object Storage instance created by this solution. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "skip_cos_kms_iam_auth_policy" { + type = bool + description = "Set to `true` to skip the creation of an IAM authorization policy that permits the Object Storage instance created 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. Applies only if `existing_scc_instance_crn` is not provided." + default = false +} + +variable "management_endpoint_type_for_bucket" { + description = "The type of endpoint for the IBM Terraform provider to use to manage Object Storage buckets. Possible values: `public`, `private`m `direct`. If you specify `private`, enable virtual routing and forwarding in your account, and the Terraform runtime must have access to the the IBM Cloud private network. Applies only if `existing_scc_instance_crn` is not provided." + type = string + default = "private" + validation { + condition = contains(["public", "private", "direct"], var.management_endpoint_type_for_bucket) + error_message = "The specified management_endpoint_type_for_bucket is not a valid selection!" + } +} + +variable "existing_monitoring_crn" { + type = string + nullable = true + default = null + description = "The CRN of an IBM Cloud Monitoring instance to to send Security and Compliance Object Storage bucket metrics to. If no value passed, metrics are sent to the instance associated to the container's location unless otherwise specified in the Metrics Router service configuration. Applies only if `existing_scc_instance_crn` is not provided." +} + +######################################################################################################################## +# Event Notifications +######################################################################################################################## + +variable "existing_event_notifications_crn" { + type = string + nullable = true + default = null + description = "The CRN of an Event Notification instance. Used to integrate with Security and Compliance Center." + + validation { + condition = var.existing_event_notifications_crn != null ? var.existing_scc_instance_crn == null : true + error_message = "You cannot pass a value for 'existing_event_notifications_crn' when passing a value for 'existing_scc_instance_crn'. Event Notifications integration can only be configured when creating a new instance." + } +} + +variable "event_notifications_source_name" { + type = string + default = "compliance" + description = "The source name to use for the Event Notifications integration. Required if a value is passed for `event_notifications_instance_crn`. This name must be unique per SCC instance that is integrated with the Event Notifications instance." +} + +variable "event_notifications_source_description" { + type = string + default = null + description = "Optional description to give for the Event Notifications integration source. Only used if a value is passed for `event_notifications_instance_crn`." +} + +variable "scc_event_notifications_from_email" { + type = string + description = "The `from` email address used in any Security and Compliance Center events coming via Event Notifications." + default = "compliancealert@ibm.com" +} + +variable "scc_event_notifications_reply_to_email" { + type = string + description = "The `reply_to` email address used in any Security and Compliance Center events coming via Event Notifications." + default = "no-reply@ibm.com" +} + +variable "scc_event_notifications_email_list" { + type = list(string) + description = "The list of email addresses to notify when Security and Compliance Center triggers an event." + default = [] +} + +############################################################## +# Context-based restriction (CBR) +############################################################## + +variable "scc_instance_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 context-based restrictions rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/DA-cbr_rules.md)." + default = [] +} diff --git a/solutions/fully-configurable/version.tf b/solutions/fully-configurable/version.tf new file mode 100644 index 0000000..3f749ca --- /dev/null +++ b/solutions/fully-configurable/version.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.9.0" + # Lock DA into an exact provider version - renovate automation will keep it updated + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "1.76.1" + } + time = { + source = "hashicorp/time" + version = "0.12.1" + } + } +} diff --git a/solutions/security-enforced/README.md b/solutions/security-enforced/README.md new file mode 100644 index 0000000..54efc77 --- /dev/null +++ b/solutions/security-enforced/README.md @@ -0,0 +1,91 @@ +# Cloud automation for Security and Compliance Center (Security-enforced) + +## Prerequisites +- An existing resource group +- An existing COS instance +- An existing KMS instance (or key) to encrypt the COS bucket created for use with SCC + +This solution supports provisioning and configuring the following infrastructure: +- A Security and Compliance Center instance. +- A Cloud Object Storage bucket which is required to store Security and Compliance Center data. +- Security and Compliance Center scopes and attachments. +- Integrations with Workload Protection and other custom providers + +:exclamation: **Important:** This solution is not intended to be called by other modules because it contains a provider configuration and 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). + +![scc-deployable-architecture](../../reference-architecture/scc.svg) + + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [scc\_da](#module\_scc\_da) | ../fully-configurable | n/a | + +### Resources + +No resources. + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [add\_bucket\_name\_suffix](#input\_add\_bucket\_name\_suffix) | Whether to add a generated 4-character suffix to the created Security and Compliance Center Object Storage bucket name. Applies only if not specifying an existing bucket. Set to `false` not to add the suffix to the bucket name in the `scc_cos_bucket_name` variable. Applies only if `existing_scc_instance_crn` is not provided. | `bool` | `true` | no | +| [attachments](#input\_attachments) | A list of attachments to create. A value must be passed for 'scope\_ids' (to use pre-existing scopes) and/or 'scope\_key\_references' (to use scopes created in the 'scopes' input). [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md). |
list(object({
profile_name = string
profile_version = string
attachment_name = string
attachment_description = string
attachment_schedule = string
scope_key_references = optional(list(string), [])
scope_ids = optional(list(string), [])
notifications = object({
enabled = optional(bool, true)
failed_control_ids = optional(list(string), [])
threshold_limit = optional(number, 10)
})
}))
| `[]` | no | +| [custom\_integrations](#input\_custom\_integrations) | A list of custom provider integrations to associate with the Security and Compliance Center instance. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/custom_integrations.md). |
list(object({
attributes = optional(map(string), {})
provider_name = string
integration_name = string
}))
| `[]` | no | +| [event\_notifications\_source\_description](#input\_event\_notifications\_source\_description) | Optional description to give for the Event Notifications integration source. Only used if a value is passed for `event_notifications_instance_crn`. | `string` | `null` | no | +| [event\_notifications\_source\_name](#input\_event\_notifications\_source\_name) | The source name to use for the Event Notifications integration. Required if a value is passed for `event_notifications_instance_crn`. This name must be unique per SCC instance that is integrated with the Event Notifications instance. | `string` | `"compliance"` | no | +| [existing\_cos\_instance\_crn](#input\_existing\_cos\_instance\_crn) | The CRN of an existing Object Storage instance. Not required if passing an existing SCC instance using the `existing_scc_instance_crn` input. | `string` | `null` | no | +| [existing\_event\_notifications\_crn](#input\_existing\_event\_notifications\_crn) | The CRN of an Event Notification instance. Used to integrate with Security and Compliance Center. | `string` | `null` | no | +| [existing\_kms\_instance\_crn](#input\_existing\_kms\_instance\_crn) | The CRN of an existing KMS instance (Hyper Protect Crypto Services or Key Protect). Used to create a new KMS key unless an existing key is passed using the `existing_scc_cos_kms_key_crn` input. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`. A value should not be passed passing existing SCC instance using the `existing_scc_instance_crn` input. | `string` | `null` | no | +| [existing\_kms\_key\_crn](#input\_existing\_kms\_key\_crn) | The CRN of an existing KMS key to use to encrypt the Security and Compliance Center Object Storage bucket. If no value is set for this variable, specify a value for either the `existing_kms_instance_crn` variable to create a key ring and key, or for the `existing_scc_cos_bucket_name` variable to use an existing bucket. | `string` | `null` | no | +| [existing\_monitoring\_crn](#input\_existing\_monitoring\_crn) | The CRN of an IBM Cloud Monitoring instance to to send Security and Compliance Object Storage bucket metrics to. If no value passed, metrics are sent to the instance associated to the container's location unless otherwise specified in the Metrics Router service configuration. Applies only if `existing_scc_instance_crn` is not provided. | `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\_scc\_instance\_crn](#input\_existing\_scc\_instance\_crn) | The CRN of an existing Security and Compliance Center instance. If not supplied, a new instance will be created. | `string` | `null` | no | +| [existing\_scc\_workload\_protection\_instance\_crn](#input\_existing\_scc\_workload\_protection\_instance\_crn) | The CRN of an existing Workload Protection instance to associate with the Security and Compliance Center instance. | `string` | `null` | no | +| [force\_delete\_kms\_key](#input\_force\_delete\_kms\_key) | If creating a new KMS key, toggle whether is should be force deleted or not on undeploy. | `bool` | `false` | 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 Security and Compliance Centre instance. Leave this input empty if the same account owns both instances. | `string` | `null` | no | +| [prefix](#input\_prefix) | The prefix to add to all resources that this solution creates (e.g `prod`, `test`, `dev`). To not use any prefix value, you can set this value to `null` or an empty string. | `string` | n/a | yes | +| [scc\_cos\_bucket\_access\_tags](#input\_scc\_cos\_bucket\_access\_tags) | The list of access tags to add to the Security and Compliance Center Object Storage bucket. Applies only if `existing_scc_instance_crn` is not provided. | `list(string)` | `[]` | no | +| [scc\_cos\_bucket\_class](#input\_scc\_cos\_bucket\_class) | The storage class of the newly provisioned Security and Compliance Center Object Storage bucket. Possible values: `standard`, `vault`, `cold`, `smart`, `onerate_active`. [Learn more](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-classes). Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"smart"` | no | +| [scc\_cos\_bucket\_name](#input\_scc\_cos\_bucket\_name) | The name for the Security and Compliance Center Object Storage bucket. Bucket names must globally unique. If `add_bucket_name_suffix` is true, a 4-character string is added to this name to ensure it's globally unique. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"scc-cos-bucket"` | no | +| [scc\_cos\_bucket\_region](#input\_scc\_cos\_bucket\_region) | The region to create the Object Storage bucket used by SCC. If not provided, the region specified in the `scc_region` input will be used. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `null` | no | +| [scc\_cos\_key\_name](#input\_scc\_cos\_key\_name) | The name for the key created for the Security and Compliance Center Object Storage bucket. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format. | `string` | `"scc-cos-key"` | no | +| [scc\_cos\_key\_ring\_name](#input\_scc\_cos\_key\_ring\_name) | The name for the key ring created for the Security and Compliance Center Object Storage bucket key. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format. | `string` | `"scc-cos-key-ring"` | no | +| [scc\_event\_notifications\_email\_list](#input\_scc\_event\_notifications\_email\_list) | The list of email addresses to notify when Security and Compliance Center triggers an event. | `list(string)` | `[]` | no | +| [scc\_event\_notifications\_from\_email](#input\_scc\_event\_notifications\_from\_email) | The `from` email address used in any Security and Compliance Center events coming via Event Notifications. | `string` | `"compliancealert@ibm.com"` | no | +| [scc\_event\_notifications\_reply\_to\_email](#input\_scc\_event\_notifications\_reply\_to\_email) | The `reply_to` email address used in any Security and Compliance Center events coming via Event Notifications. | `string` | `"no-reply@ibm.com"` | no | +| [scc\_instance\_access\_tags](#input\_scc\_instance\_access\_tags) | The list of access tags to add to the Security and Compliance Center instance. | `list(string)` | `[]` | no | +| [scc\_instance\_cbr\_rules](#input\_scc\_instance\_cbr\_rules) | (Optional, list) List of context-based restrictions rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/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 | +| [scc\_instance\_name](#input\_scc\_instance\_name) | The name for the Security and Compliance Center instance provisioned by this solution. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"scc"` | no | +| [scc\_instance\_resource\_tags](#input\_scc\_instance\_resource\_tags) | The list of tags to add to the Security and Compliance Center instance. Applies only if `existing_scc_instance_crn` is not provided. | `list(string)` | `[]` | no | +| [scc\_region](#input\_scc\_region) | The region to provision Security and Compliance Center resources in. If passing a value for `existing_scc_instance_crn`, ensure to select the region of the existing instance. Applies only if `existing_scc_instance_crn` is not provided. | `string` | `"us-south"` | no | +| [scc\_service\_plan](#input\_scc\_service\_plan) | The pricing plan to use when creating a new Security Compliance Center instance. Possible values: `security-compliance-center-standard-plan`, `security-compliance-center-trial-plan`. | `string` | `"security-compliance-center-standard-plan"` | no | +| [scopes](#input\_scopes) | A key map of scopes to create. The key name of each scope can be referenced in the attachments input using the 'scope\_key\_references' attribute. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md). |
map(object({
name = string
description = string
environment = optional(string, "ibm-cloud")
properties = object({
scope_id = string
scope_type = string
})
exclusions = optional(list(object({
scope_id = string
scope_type = string
})), [])
}))
| `{}` | no | +| [skip\_cos\_kms\_iam\_auth\_policy](#input\_skip\_cos\_kms\_iam\_auth\_policy) | Set to `true` to skip the creation of an IAM authorization policy that permits the Object Storage instance created 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. Applies only if `existing_scc_instance_crn` is not provided. | `bool` | `false` | no | +| [skip\_scc\_cos\_iam\_auth\_policy](#input\_skip\_scc\_cos\_iam\_auth\_policy) | Set to `true` to skip creation of an IAM authorization policy that permits the Security and Compliance Center to write to the Object Storage instance created by this solution. Applies only if `existing_scc_instance_crn` is not provided. | `bool` | `false` | no | +| [skip\_scc\_workload\_protection\_iam\_auth\_policy](#input\_skip\_scc\_workload\_protection\_iam\_auth\_policy) | Set to `true` to skip creating an IAM authorization policy that permits the Security and Compliance Center instance to read from the Workload Protection instance. Applies only if a value is passed for `existing_scc_workload_protection_instance_crn`. | `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 | +| [scc\_cos\_bucket\_config](#output\_scc\_cos\_bucket\_config) | List of buckets created | +| [scc\_cos\_bucket\_name](#output\_scc\_cos\_bucket\_name) | SCC COS bucket name | +| [scc\_cos\_instance\_crn](#output\_scc\_cos\_instance\_crn) | SCC COS instance CRN | +| [scc\_cos\_kms\_key\_crn](#output\_scc\_cos\_kms\_key\_crn) | SCC COS KMS Key CRN | +| [scc\_crn](#output\_scc\_crn) | SCC instance CRN | +| [scc\_guid](#output\_scc\_guid) | SCC instance guid | +| [scc\_id](#output\_scc\_id) | SCC instance ID | +| [scc\_name](#output\_scc\_name) | SCC instance name | + diff --git a/solutions/security-enforced/catalogValidationValues.json.template b/solutions/security-enforced/catalogValidationValues.json.template new file mode 100644 index 0000000..cc38ce8 --- /dev/null +++ b/solutions/security-enforced/catalogValidationValues.json.template @@ -0,0 +1,7 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "prefix": $PREFIX, + "existing_resource_group_name": "geretain-test-resources", + "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN, + "existing_cos_instance_crn": $COS_INSTANCE_CRN +} diff --git a/solutions/security-enforced/main.tf b/solutions/security-enforced/main.tf new file mode 100644 index 0000000..2c6c978 --- /dev/null +++ b/solutions/security-enforced/main.tf @@ -0,0 +1,47 @@ +####################################################################################################################### +# Wrapper around fully-configurable variation +####################################################################################################################### + +module "scc_da" { + 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" + scc_instance_name = var.scc_instance_name + scc_region = var.scc_region + scc_service_plan = var.scc_service_plan + scc_instance_resource_tags = var.scc_instance_resource_tags + scc_instance_access_tags = var.scc_instance_access_tags + existing_scc_instance_crn = var.existing_scc_instance_crn + custom_integrations = var.custom_integrations + scopes = var.scopes + attachments = var.attachments + existing_scc_workload_protection_instance_crn = var.existing_scc_workload_protection_instance_crn + skip_scc_workload_protection_iam_auth_policy = var.skip_scc_workload_protection_iam_auth_policy + kms_encryption_enabled_bucket = true + existing_kms_instance_crn = var.existing_kms_instance_crn + force_delete_kms_key = var.force_delete_kms_key + existing_kms_key_crn = var.existing_kms_key_crn + kms_endpoint_type = "private" + scc_cos_key_ring_name = var.scc_cos_key_ring_name + scc_cos_key_name = var.scc_cos_key_name + ibmcloud_kms_api_key = var.ibmcloud_kms_api_key + existing_cos_instance_crn = var.existing_cos_instance_crn + scc_cos_bucket_region = var.scc_cos_bucket_region + scc_cos_bucket_name = var.scc_cos_bucket_name + add_bucket_name_suffix = var.add_bucket_name_suffix + scc_cos_bucket_access_tags = var.scc_cos_bucket_access_tags + scc_cos_bucket_class = var.scc_cos_bucket_class + skip_scc_cos_iam_auth_policy = var.skip_scc_cos_iam_auth_policy + skip_cos_kms_iam_auth_policy = var.skip_cos_kms_iam_auth_policy + management_endpoint_type_for_bucket = "private" + existing_monitoring_crn = var.existing_monitoring_crn + existing_event_notifications_crn = var.existing_event_notifications_crn + event_notifications_source_name = var.event_notifications_source_name + event_notifications_source_description = var.event_notifications_source_description + scc_event_notifications_from_email = var.scc_event_notifications_from_email + scc_event_notifications_reply_to_email = var.scc_event_notifications_reply_to_email + scc_event_notifications_email_list = var.scc_event_notifications_email_list + scc_instance_cbr_rules = var.scc_instance_cbr_rules +} diff --git a/solutions/security-enforced/outputs.tf b/solutions/security-enforced/outputs.tf new file mode 100644 index 0000000..09cdd24 --- /dev/null +++ b/solutions/security-enforced/outputs.tf @@ -0,0 +1,57 @@ +######################################################################################################################## +# Outputs +######################################################################################################################## + +output "resource_group_name" { + description = "Resource group name" + value = module.scc_da.resource_group_name +} + +output "resource_group_id" { + description = "Resource group ID" + value = module.scc_da.resource_group_id +} + +output "scc_id" { + description = "SCC instance ID" + value = module.scc_da.scc_id +} + +output "scc_guid" { + description = "SCC instance guid" + value = module.scc_da.scc_guid +} + +output "scc_crn" { + description = "SCC instance CRN" + value = module.scc_da.scc_crn +} + +output "scc_name" { + description = "SCC instance name" + value = module.scc_da.scc_name +} + +######################################################################################################################## +# SCC COS +######################################################################################################################## + +output "scc_cos_kms_key_crn" { + description = "SCC COS KMS Key CRN" + value = module.scc_da.scc_cos_kms_key_crn +} + +output "scc_cos_bucket_name" { + description = "SCC COS bucket name" + value = module.scc_da.scc_cos_bucket_name +} + +output "scc_cos_bucket_config" { + description = "List of buckets created" + value = module.scc_da.scc_cos_bucket_config +} + +output "scc_cos_instance_crn" { + description = "SCC COS instance CRN" + value = module.scc_da.scc_cos_instance_crn +} diff --git a/solutions/security-enforced/provider.tf b/solutions/security-enforced/provider.tf new file mode 100644 index 0000000..4c6add2 --- /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 0000000..89a50d5 --- /dev/null +++ b/solutions/security-enforced/variables.tf @@ -0,0 +1,374 @@ +######################################################################################################################## +# 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" +} + +variable "prefix" { + type = string + description = "The prefix to add to all resources that this solution creates (e.g `prod`, `test`, `dev`). To not use any prefix value, you can set this value to `null` or an empty string." + nullable = true + 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." + } +} + +######################################################################################################################## +# SCC variables +######################################################################################################################## + +variable "scc_instance_name" { + type = string + default = "scc" + description = "The name for the Security and Compliance Center instance provisioned by this solution. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_region" { + type = string + default = "us-south" + description = "The region to provision Security and Compliance Center resources in. If passing a value for `existing_scc_instance_crn`, ensure to select the region of the existing instance. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_service_plan" { + type = string + description = "The pricing plan to use when creating a new Security Compliance Center instance. Possible values: `security-compliance-center-standard-plan`, `security-compliance-center-trial-plan`." + default = "security-compliance-center-standard-plan" + validation { + condition = contains(["security-compliance-center-standard-plan", "security-compliance-center-trial-plan"], var.scc_service_plan) + error_message = "Allowed values for scc_service_plan are \"security-compliance-center-standard-plan\" and \"security-compliance-center-trial-plan\"." + } +} + +variable "scc_instance_resource_tags" { + type = list(string) + description = "The list of tags to add to the Security and Compliance Center instance. Applies only if `existing_scc_instance_crn` is not provided." + default = [] +} + +variable "scc_instance_access_tags" { + type = list(string) + description = "The list of access tags to add to the Security and Compliance Center instance." + default = [] +} + +variable "existing_scc_instance_crn" { + type = string + default = null + description = "The CRN of an existing Security and Compliance Center instance. If not supplied, a new instance will be created." +} + +variable "custom_integrations" { + type = list(object({ + attributes = optional(map(string), {}) + provider_name = string + integration_name = string + })) + description = "A list of custom provider integrations to associate with the Security and Compliance Center instance. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/custom_integrations.md)." + default = [] + # Since this list is used in a for_each, add nullable = false to prevent error if user passes null + nullable = false +} + +variable "scopes" { + type = map(object({ + name = string + description = string + environment = optional(string, "ibm-cloud") + properties = object({ + scope_id = string + scope_type = string + }) + exclusions = optional(list(object({ + scope_id = string + scope_type = string + })), []) + })) + default = {} + nullable = false + description = "A key map of scopes to create. The key name of each scope can be referenced in the attachments input using the 'scope_key_references' attribute. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md)." +} + +variable "attachments" { + type = list(object({ + profile_name = string + profile_version = string + attachment_name = string + attachment_description = string + attachment_schedule = string + scope_key_references = optional(list(string), []) + scope_ids = optional(list(string), []) + notifications = object({ + enabled = optional(bool, true) + failed_control_ids = optional(list(string), []) + threshold_limit = optional(number, 10) + }) + })) + default = [] + description = "A list of attachments to create. A value must be passed for 'scope_ids' (to use pre-existing scopes) and/or 'scope_key_references' (to use scopes created in the 'scopes' input). [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/scopes_attachments.md)." + + validation { + condition = alltrue([for attachments in var.attachments : + length(attachments.scope_ids) > 0 || length(attachments.scope_key_references) > 0 + ]) + error_message = "At least one value needs to be added to the 'scope_ids' or 'scope_key_references' list inputs." + } + # TODO: Add validation to validate values in scope_key_references match the key identifier in the keys(var.scopes) +} + +######################################################################################################################## +# Workload Protection +######################################################################################################################## + +variable "existing_scc_workload_protection_instance_crn" { + type = string + description = "The CRN of an existing Workload Protection instance to associate with the Security and Compliance Center instance." + default = null +} + +variable "skip_scc_workload_protection_iam_auth_policy" { + type = bool + default = false + description = "Set to `true` to skip creating an IAM authorization policy that permits the Security and Compliance Center instance to read from the Workload Protection instance. Applies only if a value is passed for `existing_scc_workload_protection_instance_crn`." +} + +######################################################################################################################## +# KMS variables +######################################################################################################################## + +variable "existing_kms_instance_crn" { + type = string + default = null + description = "The CRN of an existing KMS instance (Hyper Protect Crypto Services or Key Protect). Used to create a new KMS key unless an existing key is passed using the `existing_scc_cos_kms_key_crn` input. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`. A value should not be passed passing existing SCC instance using the `existing_scc_instance_crn` input." + + 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_scc_instance_crn == null : true + error_message = "A value should not be passed for 'existing_kms_instance_crn' when passing an existing SCC instance using the 'existing_scc_instance_crn' input." + } + + validation { + condition = var.existing_kms_instance_crn == null ? var.existing_kms_instance_crn != null : true + error_message = "A value must be passed for either 'existing_kms_instance_crn' (to create a new key) or 'existing_kms_key_crn' (to use existing key) to encrypt the COS bucket." + } +} + +variable "force_delete_kms_key" { + type = bool + default = false + description = "If creating a new KMS key, toggle whether is should be force deleted or not on undeploy." +} + +variable "existing_kms_key_crn" { + type = string + default = null + description = "The CRN of an existing KMS key to use to encrypt the Security and Compliance Center Object Storage bucket. If no value is set for this variable, specify a value for either the `existing_kms_instance_crn` variable to create a key ring and key, or for the `existing_scc_cos_bucket_name` variable to use an existing bucket." + + 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}:key:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", var.existing_kms_key_crn)), + var.existing_kms_key_crn == null, + ]) + error_message = "The provided KMS key CRN in the input 'existing_kms_key_crn' in not valid." + } + + validation { + condition = var.existing_kms_key_crn != null ? var.existing_scc_instance_crn == null : true + error_message = "A value should not be passed for 'existing_kms_key_crn' when passing an existing SCC instance using the 'existing_scc_instance_crn' input." + } + + validation { + condition = var.existing_kms_key_crn != null ? var.existing_kms_instance_crn == null : true + error_message = "A value should not be passed for 'existing_kms_instance_crn' when passing an existing key value using the 'existing_kms_key_crn' input." + } + + validation { + condition = var.existing_kms_key_crn == null ? var.existing_kms_instance_crn != null : true + error_message = "A value must be passed for either 'existing_kms_instance_crn' (to create a new key) or 'existing_kms_key_crn' (to use existing key) to encrypt the COS bucket." + } +} + +variable "scc_cos_key_ring_name" { + type = string + default = "scc-cos-key-ring" + description = "The name for the key ring created for the Security and Compliance Center Object Storage bucket key. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format." +} + +variable "scc_cos_key_name" { + type = string + default = "scc-cos-key" + description = "The name for the key created for the Security and Compliance Center Object Storage bucket. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` 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 Security and Compliance Centre instance. Leave this input empty if the same account owns both instances." + sensitive = true + default = null + + validation { + condition = var.ibmcloud_kms_api_key != null ? var.existing_scc_instance_crn == null : true + error_message = "A value should not be passed for 'ibmcloud_kms_api_key' when passing an existing SCC instance using the 'existing_scc_instance_crn' input." + } +} + +######################################################################################################################## +# COS variables +######################################################################################################################## + +variable "existing_cos_instance_crn" { + type = string + nullable = true + default = null + description = "The CRN of an existing Object Storage instance. Not required if passing an existing SCC instance using the `existing_scc_instance_crn` input." + + validation { + condition = var.existing_cos_instance_crn == null ? var.existing_scc_instance_crn != null : true + error_message = "A value must be passed for 'existing_cos_instance_crn' when creating a new instance." + } +} + +variable "scc_cos_bucket_region" { + type = string + default = null + description = "The region to create the Object Storage bucket used by SCC. If not provided, the region specified in the `scc_region` input will be used. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_cos_bucket_name" { + type = string + default = "scc-cos-bucket" + description = "The name for the Security and Compliance Center Object Storage bucket. Bucket names must globally unique. If `add_bucket_name_suffix` is true, a 4-character string is added to this name to ensure it's globally unique. If a prefix input variable is specified, the prefix is added to the name in the `-` format. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "add_bucket_name_suffix" { + type = bool + description = "Whether to add a generated 4-character suffix to the created Security and Compliance Center Object Storage bucket name. Applies only if not specifying an existing bucket. Set to `false` not to add the suffix to the bucket name in the `scc_cos_bucket_name` variable. Applies only if `existing_scc_instance_crn` is not provided." + default = true +} + +variable "scc_cos_bucket_access_tags" { + type = list(string) + default = [] + description = "The list of access tags to add to the Security and Compliance Center Object Storage bucket. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "scc_cos_bucket_class" { + type = string + default = "smart" + description = "The storage class of the newly provisioned Security and Compliance Center Object Storage bucket. Possible values: `standard`, `vault`, `cold`, `smart`, `onerate_active`. [Learn more](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-classes). Applies only if `existing_scc_instance_crn` is not provided." + validation { + condition = contains(["standard", "vault", "cold", "smart", "onerate_active"], var.scc_cos_bucket_class) + error_message = "Allowed values for cos_bucket_class are \"standard\", \"vault\",\"cold\", \"smart\", or \"onerate_active\"." + } +} + +variable "skip_scc_cos_iam_auth_policy" { + type = bool + default = false + description = "Set to `true` to skip creation of an IAM authorization policy that permits the Security and Compliance Center to write to the Object Storage instance created by this solution. Applies only if `existing_scc_instance_crn` is not provided." +} + +variable "skip_cos_kms_iam_auth_policy" { + type = bool + description = "Set to `true` to skip the creation of an IAM authorization policy that permits the Object Storage instance created 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. Applies only if `existing_scc_instance_crn` is not provided." + default = false +} + +variable "existing_monitoring_crn" { + type = string + nullable = true + default = null + description = "The CRN of an IBM Cloud Monitoring instance to to send Security and Compliance Object Storage bucket metrics to. If no value passed, metrics are sent to the instance associated to the container's location unless otherwise specified in the Metrics Router service configuration. Applies only if `existing_scc_instance_crn` is not provided." +} + +######################################################################################################################## +# Event Notifications +######################################################################################################################## + +variable "existing_event_notifications_crn" { + type = string + nullable = true + default = null + description = "The CRN of an Event Notification instance. Used to integrate with Security and Compliance Center." + + validation { + condition = var.existing_event_notifications_crn != null ? var.existing_scc_instance_crn == null : true + error_message = "You cannot pass a value for 'existing_event_notifications_crn' when passing a value for 'existing_scc_instance_crn'. Event Notifications integration can only be configured when creating a new instance." + } +} + +variable "event_notifications_source_name" { + type = string + default = "compliance" + description = "The source name to use for the Event Notifications integration. Required if a value is passed for `event_notifications_instance_crn`. This name must be unique per SCC instance that is integrated with the Event Notifications instance." +} + +variable "event_notifications_source_description" { + type = string + default = null + description = "Optional description to give for the Event Notifications integration source. Only used if a value is passed for `event_notifications_instance_crn`." +} + +variable "scc_event_notifications_from_email" { + type = string + description = "The `from` email address used in any Security and Compliance Center events coming via Event Notifications." + default = "compliancealert@ibm.com" +} + +variable "scc_event_notifications_reply_to_email" { + type = string + description = "The `reply_to` email address used in any Security and Compliance Center events coming via Event Notifications." + default = "no-reply@ibm.com" +} + +variable "scc_event_notifications_email_list" { + type = list(string) + description = "The list of email addresses to notify when Security and Compliance Center triggers an event." + default = [] +} + +############################################################## +# Context-based restriction (CBR) +############################################################## + +variable "scc_instance_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 context-based restrictions rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-da/tree/main/solutions/fully-configurable/DA-cbr_rules.md)." + default = [] +} diff --git a/solutions/security-enforced/version.tf b/solutions/security-enforced/version.tf new file mode 100644 index 0000000..70b38b5 --- /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/tests/other_test.go b/tests/other_test.go index c84f79f..8892ce1 100644 --- a/tests/other_test.go +++ b/tests/other_test.go @@ -1,15 +1,49 @@ package test import ( + "math/rand" "testing" "github.com/stretchr/testify/assert" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" ) -func TestRunCompleteExample(t *testing.T) { +const basicExampleDir = "examples/basic" +const advancedExampleDir = "examples/advanced" + +func TestRunBasicExample(t *testing.T) { + t.Parallel() + + options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ + Testing: t, + TerraformDir: basicExampleDir, + Prefix: "scc-bsc", + ResourceGroup: resourceGroup, + Region: validRegions[rand.Intn(len(validRegions))], + TerraformVars: map[string]interface{}{ + "access_tags": permanentResources["accessTags"], + }, + }) + + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") +} + +func TestRunAdvancedExample(t *testing.T) { t.Parallel() - options := setupCompleteExampleOptions(t, "scc-cmp", completeExampleDir) + options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ + Testing: t, + TerraformDir: advancedExampleDir, + Prefix: "scc-adv", + Region: validRegions[rand.Intn(len(validRegions))], + /* + To prevent clashes, comment out the 'ResourceGroup' input in the tests to create a unique resource group, + as only one instance of Event Notification (Lite) is allowed per resource group. + */ + //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 4104fa8..6d4a729 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -1,4 +1,4 @@ -// 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 ( @@ -13,20 +13,37 @@ import ( "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/random" "github.com/gruntwork-io/terratest/modules/terraform" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common" - "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testschematic" ) -const resourceGroup = "geretain-test-resources" // Use existing resource group -const basicExampleDir = "examples/basic" -const completeExampleDir = "examples/complete" +const resourceGroup = "geretain-test-resources" +const fullyConfigFlavorDir = "solutions/fully-configurable" +const secEnforcedDir = "solutions/security-enforced" + +// 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" +// Combined list of supported SCC and EN regions +var validRegions = []string{ + "us-south", + "eu-de", + "eu-es", +} + var permanentResources map[string]interface{} +// Common variables re-used across tests +var preReqDir = "./resources/prereq-resources" +var customIntegrations = []map[string]interface{}{{"provider_name": "Caveonix", "integration_name": "caveonix-integration"}} +var scopeKey = "all" + +// NOTE: Currently scope is hard coded to GoldenEye Dev account +var scopes = map[string]interface{}{scopeKey: map[string]interface{}{"name": "Full account", "description": "Created by SCC DA test", "environment": "ibm-cloud", "properties": map[string]interface{}{"scope_id": "abac0df06b644a9cabc6e44f55b3880e", "scope_type": "account"}}} +var attachments = []map[string]interface{}{{"profile_name": "SOC 2", "profile_version": "latest", "attachment_name": "SOC 2", "attachment_description": "Created by SCC DA test", "attachment_schedule": "none", "scope_key_references": []string{scopeKey}, "notifications": map[string]interface{}{"enabled": "false"}}} + func TestMain(m *testing.M) { // Read the YAML file contents var err error @@ -38,81 +55,334 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func setupBasicExampleOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { - options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: dir, - Prefix: prefix, - ResourceGroup: resourceGroup, - BestRegionYAMLPath: "../common-dev-assets/common-go-assets/cloudinfo-region-scc-prefs.yaml", - TerraformVars: map[string]interface{}{ - "access_tags": permanentResources["accessTags"], +// Test the fully-configurable DA with defaults (no KMS encryption on bucket) +func TestFullyConfigurable(t *testing.T) { + t.Parallel() + + var region = validRegions[rand.Intn(len(validRegions))] + + // ------------------------------------------------------------------------------------ + // Provision COS, Sysdig, WP and EN first + // ------------------------------------------------------------------------------------ + + prefix := fmt.Sprintf("scc-da-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := preReqDir + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "resource_tags": tags, }, + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, }) - return options -} -func setupCompleteExampleOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { - // Use a region that is supported for both Event Notifications and SCC - validRegions := []string{ - "us-south", - "eu-de", - "eu-es", + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of pre-req resources failed in TestFullyConfigurable test") + } else { + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: region, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + "modules/*/*.tf", + fullyConfigFlavorDir + "/*.tf", + }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"scc-da-test"}, + 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: "existing_resource_group_name", Value: terraform.Output(t, existingTerraformOptions, "resource_group_name"), DataType: "string"}, + {Name: "scc_region", Value: terraform.Output(t, existingTerraformOptions, "region"), DataType: "string"}, + {Name: "scc_instance_resource_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "scc_instance_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "scc_cos_bucket_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_cos_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "cos_crn"), DataType: "string"}, + {Name: "existing_scc_workload_protection_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "wp_crn"), DataType: "string"}, + {Name: "existing_event_notifications_crn", Value: terraform.Output(t, existingTerraformOptions, "en_crn"), DataType: "string"}, + {Name: "event_notifications_source_name", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_monitoring_crn", Value: terraform.Output(t, existingTerraformOptions, "monitoring_crn"), DataType: "string"}, + {Name: "custom_integrations", Value: customIntegrations, DataType: "list(object)"}, + {Name: "scopes", Value: scopes, DataType: "map(object)"}, + {Name: "attachments", Value: attachments, DataType: "list(object)"}, + } + + err := options.RunSchematicTest() + assert.Nil(t, err, "This should not have errored") + } + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (prereq resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (prereq resources)") } - options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: dir, - Prefix: prefix, - Region: validRegions[rand.Intn(len(validRegions))], - /* - To prevent clashes, comment out the 'ResourceGroup' input in the tests to create a unique resource group, - as only one instance of Event Notification (Lite) is allowed per resource group. - */ - //ResourceGroup: resourceGroup, - }) - return options } -func TestRunBasicExample(t *testing.T) { +// Test the fully-configurable DA using existing KMS key +func TestExistingKeyFullyConfigurable(t *testing.T) { t.Parallel() - options := setupBasicExampleOptions(t, "scc", basicExampleDir) + var region = validRegions[rand.Intn(len(validRegions))] + + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: region, + Prefix: "scc-key", + TarIncludePatterns: []string{ + "*.tf", + "modules/*/*.tf", + fullyConfigFlavorDir + "/*.tf", + }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"scc-da-test"}, + 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: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, + {Name: "scc_region", Value: options.Region, DataType: "string"}, + {Name: "scc_instance_resource_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "existing_cos_instance_crn", Value: permanentResources["general_test_storage_cos_instance_crn"], DataType: "string"}, + {Name: "existing_kms_key_crn", Value: permanentResources["hpcs_south_root_key_crn"], DataType: "string"}, + {Name: "kms_encryption_enabled_bucket", Value: true, DataType: "bool"}, + } - output, err := options.RunTestConsistency() + err := options.RunSchematicTest() assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") } -func TestRunCompleteExampleUpgrade(t *testing.T) { +// Test the security-enforced DA with defaults (pass KMS instance details and create new key) +func TestSecurityEnforced(t *testing.T) { t.Parallel() - options := setupCompleteExampleOptions(t, "scc-upg", completeExampleDir) + var region = validRegions[rand.Intn(len(validRegions))] + + // ------------------------------------------------------------------------------------ + // Provision COS, Sysdig, WP and EN first + // ------------------------------------------------------------------------------------ + + prefix := fmt.Sprintf("scc-da-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := preReqDir + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "resource_tags": tags, + }, + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, + }) + + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of pre-req resources failed in TestFullyConfigurable test") + } else { + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: region, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + "modules/*/*.tf", + fullyConfigFlavorDir + "/*.tf", + secEnforcedDir + "/*.tf", + }, + TemplateFolder: secEnforcedDir, + Tags: []string{"scc-da-test"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + }) - output, err := options.RunTestUpgrade() - if !options.UpgradeTestSkipped { + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "existing_resource_group_name", Value: terraform.Output(t, existingTerraformOptions, "resource_group_name"), DataType: "string"}, + {Name: "scc_region", Value: terraform.Output(t, existingTerraformOptions, "region"), DataType: "string"}, + {Name: "scc_instance_resource_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "scc_instance_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, + {Name: "scc_cos_bucket_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_cos_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "cos_crn"), DataType: "string"}, + {Name: "existing_scc_workload_protection_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "wp_crn"), DataType: "string"}, + {Name: "existing_event_notifications_crn", Value: terraform.Output(t, existingTerraformOptions, "en_crn"), DataType: "string"}, + {Name: "event_notifications_source_name", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_monitoring_crn", Value: terraform.Output(t, existingTerraformOptions, "monitoring_crn"), DataType: "string"}, + {Name: "custom_integrations", Value: customIntegrations, DataType: "list(object)"}, + {Name: "scopes", Value: scopes, DataType: "map(object)"}, + {Name: "attachments", Value: attachments, DataType: "list(object)"}, + } + + err := options.RunSchematicTest() assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + } + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (prereq resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (prereq resources)") } } -// A test to pass existing SCC instances to complete example -func TestRunExistingResourcesInstances(t *testing.T) { +// Run upgrade test on security-enforced variation +func TestUpgradeSecurityEnforced(t *testing.T) { t.Parallel() + var region = validRegions[rand.Intn(len(validRegions))] + // ------------------------------------------------------------------------------------ - // Provision SCC instance and configure COS setting + // Provision COS, Sysdig, WP and EN first // ------------------------------------------------------------------------------------ - prefix := fmt.Sprintf("scc-existing-%s", strings.ToLower(random.UniqueId())) - realTerraformDir := ".." + prefix := fmt.Sprintf("scc-da-upg-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := preReqDir tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) tags := common.GetTagsFromTravis() - validRegions := []string{ - "us-south", - "eu-de", - "eu-es", + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "resource_tags": tags, + }, + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, + }) + + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of pre-req resources failed in TestFullyConfigurableUpgrade test") + } else { + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: region, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + "modules/*/*.tf", + fullyConfigFlavorDir + "/*.tf", + }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"scc-da-upg-test"}, + 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: "existing_resource_group_name", Value: terraform.Output(t, existingTerraformOptions, "resource_group_name"), DataType: "string"}, + {Name: "scc_region", Value: terraform.Output(t, existingTerraformOptions, "region"), DataType: "string"}, + {Name: "scc_instance_resource_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "scc_instance_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "scc_cos_bucket_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_cos_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "cos_crn"), DataType: "string"}, + {Name: "existing_scc_workload_protection_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "wp_crn"), DataType: "string"}, + {Name: "existing_event_notifications_crn", Value: terraform.Output(t, existingTerraformOptions, "en_crn"), DataType: "string"}, + {Name: "event_notifications_source_name", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_monitoring_crn", Value: terraform.Output(t, existingTerraformOptions, "monitoring_crn"), DataType: "string"}, + {Name: "custom_integrations", Value: customIntegrations, DataType: "list(object)"}, + {Name: "scopes", Value: scopes, DataType: "map(object)"}, + {Name: "attachments", Value: attachments, DataType: "list(object)"}, + } + + err := options.RunSchematicUpgradeTest() + if !options.UpgradeTestSkipped { + assert.Nil(t, err, "This should not have errored") + } + } + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (prereq resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (prereq resources)") } - region := validRegions[rand.Intn(len(validRegions))] +} + +// A test to pass existing resources to the Fully configurable DA variation +func TestExistingResourcesFullyConfigurable(t *testing.T) { + t.Parallel() + // ------------------------------------------------------------------------------------ + // Provision SCC, and WP first + // ------------------------------------------------------------------------------------ + + prefix := fmt.Sprintf("scc-exist-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := "./resources/existing-resources-test" + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + region := "us-south" // Verify ibmcloud_api_key variable is set checkVariable := "TF_VAR_ibmcloud_api_key" @@ -122,7 +392,7 @@ func TestRunExistingResourcesInstances(t *testing.T) { logger.Log(t, "Tempdir: ", tempTerraformDir) existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: tempTerraformDir + "/tests/existing-resources", + TerraformDir: tempTerraformDir, Vars: map[string]interface{}{ "prefix": prefix, "region": region, @@ -140,26 +410,38 @@ func TestRunExistingResourcesInstances(t *testing.T) { } else { // ------------------------------------------------------------------------------------ - // Deploy complete example using existing SCC instance + // Deploy DA passing existing SCC instance and WP instance // ------------------------------------------------------------------------------------ - - options := testhelper.TestOptionsDefault(&testhelper.TestOptions{ - Testing: t, - TerraformDir: completeExampleDir, - // Do not hard fail the test if the implicit destroy steps fail to allow a full destroy of resource to occur - ImplicitRequired: false, - Region: region, - TerraformVars: map[string]interface{}{ - "region": region, - "resource_group": terraform.Output(t, existingTerraformOptions, "resource_group_name"), - "existing_scc_instance_crn": terraform.Output(t, existingTerraformOptions, "crn"), - "prefix": terraform.Output(t, existingTerraformOptions, "prefix"), + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: region, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + "modules/*/*.tf", + fullyConfigFlavorDir + "/*.tf", }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"scc-da-test"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, }) - output, err := options.RunTestConsistency() + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "existing_resource_group_name", Value: terraform.Output(t, existingTerraformOptions, "resource_group_name"), DataType: "string"}, + {Name: "scc_region", Value: terraform.Output(t, existingTerraformOptions, "region"), DataType: "string"}, + {Name: "scc_instance_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_scc_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "scc_crn"), DataType: "string"}, + {Name: "existing_scc_workload_protection_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "wp_crn"), DataType: "string"}, + {Name: "custom_integrations", Value: customIntegrations, DataType: "list(object)"}, + {Name: "scopes", Value: scopes, DataType: "map(object)"}, + {Name: "attachments", Value: attachments, DataType: "list(object)"}, + } + + err := options.RunSchematicTest() assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") } diff --git a/tests/resources/existing-resources-test/README.md b/tests/resources/existing-resources-test/README.md new file mode 100644 index 0000000..4bb3621 --- /dev/null +++ b/tests/resources/existing-resources-test/README.md @@ -0,0 +1 @@ +The terraform code in this directory is used by the existing resource test in tests/pr_test.go diff --git a/tests/resources/existing-resources-test/main.tf b/tests/resources/existing-resources-test/main.tf new file mode 100644 index 0000000..4e3f291 --- /dev/null +++ b/tests/resources/existing-resources-test/main.tf @@ -0,0 +1,53 @@ +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.1.6" + # 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 +} + +############################################################################## +# COS +############################################################################## + +module "cos" { + source = "terraform-ibm-modules/cos/ibm" + version = "8.20.1" + cos_instance_name = "${var.prefix}-cos" + kms_encryption_enabled = false + retention_enabled = false + resource_group_id = module.resource_group.resource_group_id + bucket_name = "${var.prefix}-scc" +} + +############################################################################## +# SCC +############################################################################## + +module "scc" { + source = "terraform-ibm-modules/scc/ibm" + version = "2.0.1" + instance_name = var.prefix + region = var.region + resource_group_id = module.resource_group.resource_group_id + resource_tags = var.resource_tags + cos_bucket = module.cos.bucket_name + cos_instance_crn = module.cos.cos_instance_id +} + +############################################################################## +# Workload Protection +############################################################################## + +module "scc_wp" { + source = "terraform-ibm-modules/scc-workload-protection/ibm" + version = "1.4.3" + name = var.prefix + region = var.region + resource_group_id = module.resource_group.resource_group_id + resource_tags = var.resource_tags +} diff --git a/tests/resources/existing-resources-test/outputs.tf b/tests/resources/existing-resources-test/outputs.tf new file mode 100644 index 0000000..f8f72a4 --- /dev/null +++ b/tests/resources/existing-resources-test/outputs.tf @@ -0,0 +1,28 @@ +############################################################################## +# Outputs +############################################################################## + +output "resource_group_name" { + description = "Resource group name" + value = module.resource_group.resource_group_name +} + +output "prefix" { + value = var.prefix + description = "Prefix" +} + +output "region" { + value = var.region + description = "region" +} + +output "scc_crn" { + description = "Workload Protection CRN" + value = module.scc.crn +} + +output "wp_crn" { + description = "Workload Protection CRN" + value = module.scc_wp.crn +} diff --git a/tests/resources/existing-resources-test/provider.tf b/tests/resources/existing-resources-test/provider.tf new file mode 100644 index 0000000..df45ef5 --- /dev/null +++ b/tests/resources/existing-resources-test/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/tests/resources/existing-resources-test/variables.tf b/tests/resources/existing-resources-test/variables.tf new file mode 100644 index 0000000..441068e --- /dev/null +++ b/tests/resources/existing-resources-test/variables.tf @@ -0,0 +1,31 @@ +############################################################################## +# Input variables +############################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API Key" + sensitive = true +} + +variable "region" { + type = string + description = "Region" +} + +variable "prefix" { + type = string + description = "Prefix to append to all resources" +} + +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" + default = null +} + +variable "resource_tags" { + type = list(string) + description = "Optional list of tags to be added to created resources" + default = [] +} diff --git a/tests/resources/existing-resources-test/version.tf b/tests/resources/existing-resources-test/version.tf new file mode 100644 index 0000000..7db4be4 --- /dev/null +++ b/tests/resources/existing-resources-test/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + ibm = { + source = "ibm-cloud/ibm" + version = ">= 1.76.0" + } + } +} diff --git a/tests/resources/prereq-resources/README.md b/tests/resources/prereq-resources/README.md new file mode 100644 index 0000000..c731915 --- /dev/null +++ b/tests/resources/prereq-resources/README.md @@ -0,0 +1 @@ +The terraform code in this directory is used by the DA tests in tests/pr_test.go diff --git a/tests/resources/prereq-resources/main.tf b/tests/resources/prereq-resources/main.tf new file mode 100644 index 0000000..02ff9fa --- /dev/null +++ b/tests/resources/prereq-resources/main.tf @@ -0,0 +1,65 @@ +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.1.6" + # 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 +} + +############################################################################## +# Create Cloud Object Storage instance and a bucket +############################################################################## + +module "cos" { + source = "terraform-ibm-modules/cos/ibm" + version = "8.19.5" + resource_group_id = module.resource_group.resource_group_id + cos_instance_name = "${var.prefix}-cos" + cos_tags = var.resource_tags + create_cos_bucket = false +} + +############################################################################## +# Workload Protection +############################################################################## + +module "scc_wp" { + source = "terraform-ibm-modules/scc-workload-protection/ibm" + version = "1.4.3" + name = var.prefix + region = var.region + resource_group_id = module.resource_group.resource_group_id + resource_tags = var.resource_tags +} + +############################################################################## +# Cloud Monitoring +############################################################################## + +module "cloud_monitoring" { + source = "terraform-ibm-modules/observability-instances/ibm//modules/cloud_monitoring" + version = "3.4.3" + resource_group_id = module.resource_group.resource_group_id + region = var.region + instance_name = "${var.prefix}-mon" + enable_platform_metrics = false + tags = var.resource_tags +} + +############################################################################## +# EN +############################################################################## + +module "event_notifications" { + source = "terraform-ibm-modules/event-notifications/ibm" + version = "1.18.8" + resource_group_id = module.resource_group.resource_group_id + name = "${var.prefix}-en" + tags = var.resource_tags + plan = "lite" + region = var.region +} diff --git a/tests/resources/prereq-resources/outputs.tf b/tests/resources/prereq-resources/outputs.tf new file mode 100644 index 0000000..043f09d --- /dev/null +++ b/tests/resources/prereq-resources/outputs.tf @@ -0,0 +1,43 @@ +############################################################################## +# Outputs +############################################################################## + +output "resource_group_name" { + description = "Resource group name" + value = module.resource_group.resource_group_name +} + +output "prefix" { + value = var.prefix + description = "Prefix" +} + +output "region" { + value = var.region + description = "region" +} + +output "cos_crn" { + description = "COS CRN" + value = module.cos.cos_instance_crn +} + +output "bucket_name" { + description = "Bucket name" + value = module.cos.bucket_name +} + +output "monitoring_crn" { + value = module.cloud_monitoring.crn + description = "Monitoring CRN" +} + +output "en_crn" { + description = "Event Notification CRN" + value = module.event_notifications.crn +} + +output "wp_crn" { + description = "Workload Protection CRN" + value = module.scc_wp.crn +} diff --git a/tests/resources/prereq-resources/provider.tf b/tests/resources/prereq-resources/provider.tf new file mode 100644 index 0000000..df45ef5 --- /dev/null +++ b/tests/resources/prereq-resources/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/tests/resources/prereq-resources/variables.tf b/tests/resources/prereq-resources/variables.tf new file mode 100644 index 0000000..441068e --- /dev/null +++ b/tests/resources/prereq-resources/variables.tf @@ -0,0 +1,31 @@ +############################################################################## +# Input variables +############################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API Key" + sensitive = true +} + +variable "region" { + type = string + description = "Region" +} + +variable "prefix" { + type = string + description = "Prefix to append to all resources" +} + +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" + default = null +} + +variable "resource_tags" { + type = list(string) + description = "Optional list of tags to be added to created resources" + default = [] +} diff --git a/tests/resources/prereq-resources/version.tf b/tests/resources/prereq-resources/version.tf new file mode 100644 index 0000000..7db4be4 --- /dev/null +++ b/tests/resources/prereq-resources/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + ibm = { + source = "ibm-cloud/ibm" + version = ">= 1.76.0" + } + } +}