diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml new file mode 100644 index 00000000..3763bec7 --- /dev/null +++ b/.catalog-onboard-pipeline.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +offerings: + - name: deploy-arch-ibm-activity-tracker + kind: solution + catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd + offering_id: 918453c3-4f97-4583-8c4a-83ef12fc7916 + variations: + - name: fully-configurable + mark_ready: true + install_type: fullstack + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south diff --git a/activity-tracker-icon.svg b/activity-tracker-icon.svg new file mode 100644 index 00000000..6da0bcf6 --- /dev/null +++ b/activity-tracker-icon.svg @@ -0,0 +1,1001 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ibm_catalog.json b/ibm_catalog.json new file mode 100644 index 00000000..baa7018d --- /dev/null +++ b/ibm_catalog.json @@ -0,0 +1,513 @@ +{ + "products": [ + { + "name": "deploy-arch-ibm-activity-tracker", + "label": "Cloud automation for Activity Tracker Event Routing", + "product_kind": "solution", + "tags": [ + "ibm_created", + "target_terraform", + "terraform", + "solution", + "logging_monitoring" + ], + "keywords": [ + "IaC", + "infrastructure as code", + "terraform", + "solution", + "observability", + "ATracker" + ], + "short_description": "Create and configures IBM Cloud Acitivity Tracker Event Routing", + "long_description": "This architecture supports creating and configuring [IBM Cloud Activity Tracker Event Routing](https://cloud.ibm.com/docs/atracker?topic=atracker-getting-started) resources. Activity Tracker Event Routing can be used to manage auditing events at the account-level by configuring targets and routes that define where auditing data is routed.", + "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-activity-tracker/blob/main/solutions/fully-configurable/README.md", + "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-activity-tracker/main/images/activity-tracker-icon.svg", + "provider_name": "IBM", + "features": [ + { + "title": "Activity Tracker Event Routing", + "description": "Configures an IBM Cloud Activity Tracker route with Cloud Object Storage and Cloud Logs target." + }, + { + "title": "Cloud Logs", + "description": "Supports creating a new cloud logs instance as well as using an existing one for monitoring activity tracking events that are generated in an IBM Cloud account." + }, + { + "title": "Object Storage Buckets", + "description": "Creates buckets required for storing Activity Tracker events, Cloud Logs metrics and cloud logs data." + }, + { + "title": "Enable KMS Encryption", + "description": "Supports creating a new key, or using an existing one to encrypt the Object Storage buckets. For more details on KMS encryption, refer [this](https://cloud.ibm.com/docs/key-protect?topic=key-protect-about)." + } + ], + "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 [here](https://github.com/terraform-ibm-modules/terraform-ibm-activity-tracker/issues). Please note this product is not supported via the IBM Cloud Support Center.", + "flavors": [ + { + "label": "Fully configurable", + "name": "fully-configurable", + "index": 1, + "install_type": "fullstack", + "working_directory": "solutions/fully-configurable", + "iam_permissions": [ + { + "service_name": "atracker", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Writer", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "notes": "Required for configuring Activity Tracker event routing to cloud object storage bucket and cloud logs." + }, + { + "service_name": "logs", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "notes": "[Optional] Required for creating a new instance of cloud logs." + }, + { + "service_name": "cloud-object-storage", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "notes": "[Optional] Required for creating a Object Storage instance to store auditing events and cloud logs and metrics data." + }, + { + "service_name": "hs-crypto", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "notes": "[Optional] Required if KMS encryption is enabled and Hyper Protect Crypto Services is used for encryption." + }, + { + "service_name": "kms", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "notes": "[Optional] Required if KMS encryption is enabled and Key protect is used for encryption." + } + ], + "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 + }, + { + "key": "existing_resource_group_name" + }, + { + "key": "region", + "required": true, + "options": [ + { + "displayname": "Osaka (jp-osa)", + "value": "jp-osa" + }, + { + "displayname": "Tokyo (jp-tok)", + "value": "jp-tok" + }, + { + "displayname": "Frankfurt (eu-de)", + "value": "eu-de" + }, + { + "displayname": "London (eu-gb)", + "value": "eu-gb" + }, + { + "displayname": "Madrid (eu-es)", + "value": "eu-es" + }, + { + "displayname": "Dallas (us-south)", + "value": "us-south" + }, + { + "displayname": "Toronto (ca-tor)", + "value": "ca-tor" + }, + { + "displayname": "Washington (us-east)", + "value": "us-east" + }, + { + "displayname": "Sao Paulo (br-sao)", + "value": "br-sao" + } + ] + }, + { + "key": "cloud_logs_instance_name", + "required": true, + "type": "string", + "description": "Name of the cloud logs instance to be created.", + "virtual": true, + "default_value": "__NOT_SET__" + }, + { + "key": "existing_cloud_logs_instance_crn" + }, + { + "key": "enable_activity_tracker_event_routing_to_cloud_logs" + }, + { + "key": "cloud_logs_target_name" + }, + { + "key": "activity_tracker_cloud_logs_route_name" + }, + { + "key": "existing_cos_instance_crn" + }, + { + "key": "ibmcloud_cos_api_key" + }, + { + "key": "enable_activity_tracker_event_routing_to_cos_bucket" + }, + { + "key": "cos_target_name" + }, + { + "key": "activity_tracker_cos_route_name" + }, + { + "key": "cos_region" + }, + { + "key": "activity_tracker_cos_bucket_retention_policy" + }, + { + "key": "add_bucket_name_suffix" + }, + { + "key": "activity_tracker_cos_target_bucket_name" + }, + { + "key": "activity_tracker_cos_bucket_access_tags" + }, + { + "key": "activity_tracker_cos_target_bucket_class", + "options": [ + { + "displayname": "standard", + "value": "standard" + }, + { + "displayname": "vault", + "value": "vault" + }, + { + "displayname": "cold", + "value": "cold" + }, + { + "displayname": "smart", + "value": "smart" + }, + { + "displayname": "onerate_active", + "value": "onerate_active" + } + ] + }, + { + "key": "management_endpoint_type_for_bucket", + "hidden": true, + "options": [ + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "direct", + "value": "direct" + } + ] + }, + { + "key": "existing_activity_tracker_cos_target_bucket_name" + }, + { + "key": "existing_activity_tracker_cos_target_bucket_endpoint" + }, + { + "key": "skip_cos_kms_auth_policy" + }, + { + "key": "skip_activity_tracker_cos_auth_policy" + }, + { + "key": "kms_encryption_enabled_buckets" + }, + { + "key": "existing_cos_kms_key_crn" + }, + { + "key": "existing_monitoring_crn" + }, + { + "key": "existing_kms_instance_crn" + }, + { + "key": "cos_key_name" + }, + { + "key": "cos_key_ring_name" + }, + { + "key": "ibmcloud_kms_api_key" + }, + { + "key": "kms_endpoint_type", + "hidden": true, + "options": [ + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "private", + "value": "private" + } + ] + }, + { + "key": "provider_visibility", + "hidden": true, + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] + } + ], + "dependencies": [ + { + "name": "deploy-arch-ibm-cos", + "description": "Enable this to create an IBM Cloud Object Storage(COS) instance. The buckets to store events will be created by the Activity Tracker Event Routing deployable architecture.", + "id": "68921490-2778-4930-ac6d-bae7be6cd958-global", + "version": "v9.0.2", + "flavors": [ + "instance" + ], + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "optional": true, + "on_by_default": true, + "input_mapping": [ + { + "dependency_input": "existing_resource_group_name", + "version_input": "existing_resource_group_name", + "reference_version": true + }, + { + "dependency_output": "cos_instance_crn", + "version_input": "existing_cos_instance_crn" + }, + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + } + ] + }, + { + "name": "deploy-arch-ibm-kms", + "description": "Enable when you want to create your own managed keys to encrypt the buckets. Select only if existing KMS instance or Key is not provided. ", + "id": "2cad4789-fa90-4886-9c9e-857081c273ee-global", + "version": "v5.1.4", + "flavors": [ + "fully-configurable" + ], + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "optional": true, + "on_by_default": true, + "input_mapping": [ + { + "dependency_output": "kms_instance_crn", + "version_input": "existing_kms_instance_crn" + }, + { + "version_input": "kms_encryption_enabled_buckets", + "value": true + }, + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + }, + { + "dependency_input": "region", + "version_input": "region", + "reference_version": true + } + ] + }, + { + "name": "deploy-arch-ibm-account-infra-base", + "description": "Enable to create a resource groups by default where all the resources will be provisioned and, when you enable the “with Account Settings” option, it also applies baseline security and governance settings. When disabled, provide an existing resource group as input.", + "id": "63641cec-6093-4b4f-b7b0-98d2f4185cd6-global", + "version": "v3.0.7", + "flavors": [ + "resource-group-only", + "resource-groups-with-account-settings" + ], + "default_flavor": "resource-group-only", + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "optional": true, + "on_by_default": false, + "input_mapping": [ + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + }, + { + "dependency_output": "observability_resource_group_name", + "version_input": "existing_resource_group_name" + } + ] + }, + { + "name": "deploy-arch-ibm-cloud-logs", + "description": "Enable this to create an IBM Cloud Logs (ICL) Instance which can be used for storage and analysis of events ingested by Activity Tracker. ", + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "flavors": [ + "fully-configurable" + ], + "id": "63d8ae58-fbf3-41ce-b844-0fb5b85882ab-global", + "version": "v1.5.0", + "optional": true, + "on_by_default": true, + "input_mapping": [ + { + "dependency_input": "cloud_logs_instance_name", + "version_input": "cloud_logs_instance_name", + "reference_version": true + }, + { + "dependency_input": "existing_cos_instance_crn", + "version_input": "existing_cos_instance_crn", + "reference_version": true + }, + { + "dependency_input": "existing_monitoring_crn", + "version_input": "existing_monitoring_crn", + "reference_version": true + }, + { + "dependency_input": "existing_kms_instance_crn", + "version_input": "existing_kms_instance_crn", + "reference_version": true + }, + { + "dependency_input": "kms_encryption_enabled_buckets", + "version_input": "kms_encryption_enabled_buckets", + "reference_version": true + }, + { + "dependency_input": "existing_kms_key_crn", + "version_input": "existing_cos_kms_key_crn", + "reference_version": true + }, + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + }, + { + "dependency_input": "existing_resource_group_name", + "version_input": "existing_resource_group_name", + "reference_version": true + }, + { + "dependency_output": "cloud_logs_crn", + "version_input": "existing_cloud_logs_instance_crn" + } + ] + }, + { + "name": "deploy-arch-ibm-cloud-monitoring", + "description": "Enable IBM Cloud Monitoring to gain operational visibility into your cloud storage buckets. ", + "id": "73debdbf-894f-4c14-81c7-5ece3a70b67d-global", + "version": "v1.2.14", + "flavors": [ + "fully-configurable" + ], + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "optional": true, + "on_by_default": true, + "input_mapping": [ + { + "dependency_output": "cloud_monitoring_crn", + "version_input": "existing_monitoring_crn" + }, + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + }, + { + "dependency_input": "region", + "version_input": "region", + "reference_version": true + } + ] + } + ], + "dependency_version_2": true, + "terraform_version": "1.10.5", + "architecture": { + "features": [ + { + "title": " ", + "description": "Configured to use IBM secure by default standards, but can be edited to fit your use case." + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Activity Tracker Event Routing", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-activity-tracker/main/reference-architecture/activity-tracker.svg", + "type": "image/svg+xml" + }, + "description": "This architecture supports creating IBM Cloud Activity Tracker Event Routing target to an object storage bucket and cloud logs instance. You can provide an existing Cloud Object Storage (COS) instance or use [Cloud automation for Object Storage](https://cloud.ibm.com/catalog/7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3/architecture/deploy-arch-ibm-cos-68921490-2778-4930-ac6d-bae7be6cd958-global) dependency for creating COS instance. This architecutre will create object storage buckets inside the COS instance for storing the events ingested by Activity Tracker Event Routing.

In addition, it enables encryption for the object storage bucket by provisioning an IBM Key Protect service instance, where a Key Ring and associated key are created to manage encryption through IBM Cloud Key Management Services (KMS). You can choose to provide an existing KMS instance as well.

Additionally, you can use [Cloud automation for Cloud Logs](https://cloud.ibm.com/catalog/7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3/architecture/deploy-arch-ibm-cloud-logs-63d8ae58-fbf3-41ce-b844-0fb5b85882ab-global) to create a cloud logs instance or provide an existing cloud logs instance crn for setting it as event routing target. This architecture will automatically create the COS buckets to collect and store auditing events." + } + ] + } + } + ] + } + ] +} diff --git a/reference-architecture/activity-tracker.svg b/reference-architecture/activity-tracker.svg new file mode 100644 index 00000000..602c6ad0 --- /dev/null +++ b/reference-architecture/activity-tracker.svg @@ -0,0 +1,4 @@ + + + +
s
s
IBM Cloud
IBM Cloud
Region
Region
Resource Group
Object Storage

Object Storage
Atracker Events Bucket
Atracker Events...
Cloud Logs


Key Ring
Key Ring
bucket-encryption-key
bucket-encryption-key
[Optional] KMS
[Optional] KMS
Auditing Events
Auditing Events
Auditing Events
Auditing Events
Activity Tracker
Activity T...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/solutions/fully-configurable/DA-types.md b/solutions/fully-configurable/DA-types.md new file mode 100644 index 00000000..f4832ba2 --- /dev/null +++ b/solutions/fully-configurable/DA-types.md @@ -0,0 +1,29 @@ +# Configuring complex inputs for Cloud Automation for Observability + +* Activity Tracker Event Routing COS bucket retention policy (`activity_tracker_cos_bucket_retention_policy`) + +## activity_tracker_cos_bucket_retention_policy + +The `activity_tracker_cos_bucket_retention_policy` input variable allows you to provide the retention policy of the IBM Cloud Activity Tracker Event Routing COS target bucket that will be configured. Refer [here](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-immutable) for more information. + +* Variable name: `activity_tracker_cos_bucket_retention_policy`. +* Type: An object representing a retention policy. +* Default value: null (`null`). + +### Options for activity_tracker_cos_bucket_retention_policy + +* `default` (optional): The number of days that an object can remain unmodified in an Object Storage bucket. +* `maximum` (optional): The maximum number of days that an object can be kept unmodified in the bucket. +* `minimum` (optional): The minimum number of days that an object must be kept unmodified in the bucket. +* `permanent` (optional): Whether permanent retention status is enabled for the Object Storage bucket. + +### Example activity_tracker_cos_bucket_retention_policy + +```hcl +{ + default = 90 + maximum = 350 + minimum = 90 + permanent = false +} +``` diff --git a/solutions/fully-configurable/README.md b/solutions/fully-configurable/README.md new file mode 100644 index 00000000..8cc091e0 --- /dev/null +++ b/solutions/fully-configurable/README.md @@ -0,0 +1,3 @@ +# Cloud automation for Activity Tracker Event Routing (Fully configurable) + +: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). diff --git a/solutions/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template new file mode 100644 index 00000000..67ee17ba --- /dev/null +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -0,0 +1,9 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN, + "kms_encryption_enabled_buckets": true, + "region": "us-south", + "prefix": $PREFIX, + "existing_cos_instance_crn": $COS_INSTANCE_CRN, + "existing_resource_group_name": "geretain-test-resources" +} diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf new file mode 100644 index 00000000..d4fcc898 --- /dev/null +++ b/solutions/fully-configurable/main.tf @@ -0,0 +1,292 @@ +####################################################################################################################### +# Local Variables +####################################################################################################################### +locals { + prefix = var.prefix != null ? (var.prefix != "" ? var.prefix : null) : null +} + +locals { + + + default_cos_region = var.cos_region != null ? var.cos_region : var.region + + cos_key_ring_name = try("${local.prefix}-${var.cos_key_ring_name}", var.cos_key_ring_name) + cos_key_name = try("${local.prefix}-${var.cos_key_name}", var.cos_key_name) + activity_tracker_cos_target_bucket_name = try("${local.prefix}-${var.activity_tracker_cos_target_bucket_name}", var.activity_tracker_cos_target_bucket_name) + + cos_instance_guid = element(split(":", var.existing_cos_instance_crn), length(split(":", var.existing_cos_instance_crn)) - 3) + + use_kms_module = var.kms_encryption_enabled_buckets && var.existing_cos_kms_key_crn == null + existing_kms_guid = var.kms_encryption_enabled_buckets ? (var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_instance : module.existing_kms_key_crn_parser[0].service_instance) : null + kms_service = var.kms_encryption_enabled_buckets ? (var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_name : module.existing_kms_key_crn_parser[0].service_name) : null + + kms_region = var.kms_encryption_enabled_buckets && var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].region : null + + cos_kms_key_crn = var.existing_cos_kms_key_crn != null ? var.existing_cos_kms_key_crn : length(coalesce(local.buckets_config, [])) != 0 && var.kms_encryption_enabled_buckets ? module.kms[0].keys[format("%s.%s", local.cos_key_ring_name, local.cos_key_name)].crn : null + parsed_kms_key_crn = local.cos_kms_key_crn != null ? split(":", local.cos_kms_key_crn) : [] + cos_kms_key_id = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[9] : null + cos_kms_scope = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[6] : null + kms_account_id = length(local.parsed_kms_key_crn) > 0 ? split("/", local.cos_kms_scope)[1] : null + + cos_target_bucket_name = var.existing_activity_tracker_cos_target_bucket_name != null ? var.existing_activity_tracker_cos_target_bucket_name : var.enable_activity_tracker_event_routing_to_cos_bucket ? module.cos_bucket[0].buckets[local.activity_tracker_cos_target_bucket_name].bucket_name : null + cos_target_bucket_endpoint = var.existing_activity_tracker_cos_target_bucket_endpoint != null ? var.existing_activity_tracker_cos_target_bucket_endpoint : var.enable_activity_tracker_event_routing_to_cos_bucket ? module.cos_bucket[0].buckets[local.activity_tracker_cos_target_bucket_name].s3_endpoint_private : null + cos_target_name = var.cos_target_name != null ? var.cos_target_name : try("${local.prefix}-cos-target", "cos-target") + cloud_logs_target_name = var.cloud_logs_target_name != null ? var.cloud_logs_target_name : try("${local.prefix}-cloud-logs-target", "cloud-logs-target") + activity_tracker_cos_route_name = var.activity_tracker_cos_route_name != null ? var.activity_tracker_cos_route_name : try("${local.prefix}-at-cos-route", "at-cos-route") + activity_tracker_cloud_logs_route_name = var.activity_tracker_cloud_logs_route_name != null ? var.activity_tracker_cloud_logs_route_name : try("${local.prefix}-at-cloud-logs-route", "at-cloud-logs-route") + + + + activity_tracker_bucket_config = var.existing_activity_tracker_cos_target_bucket_name == null && var.enable_activity_tracker_event_routing_to_cos_bucket ? { + class = var.activity_tracker_cos_target_bucket_class + name = local.activity_tracker_cos_target_bucket_name + tag = var.activity_tracker_cos_bucket_access_tags + } : null + + + bucket_retention_configs = local.activity_tracker_bucket_config != null ? { (local.activity_tracker_cos_target_bucket_name) = var.activity_tracker_cos_bucket_retention_policy } : null + + buckets_config = local.activity_tracker_bucket_config != null ? [local.activity_tracker_bucket_config] : [] + + archive_rule = length(local.buckets_config) != 0 ? { + enable = true + days = 90 + type = "Glacier" + } : null + + expire_rule = length(local.buckets_config) != 0 ? { + enable = true + days = 366 + } : null + + activity_tracker_cos_route = var.enable_activity_tracker_event_routing_to_cos_bucket ? [{ + route_name = local.activity_tracker_cos_route_name + locations = ["*", "global"] + target_ids = [module.activity_tracker.activity_tracker_targets[local.cos_target_name].id] + }] : [] + + activity_tracker_cloud_logs_route = var.enable_activity_tracker_event_routing_to_cloud_logs && var.existing_cloud_logs_instance_crn != null ? [{ + route_name = local.activity_tracker_cloud_logs_route_name + locations = ["*", "global"] + target_ids = [module.activity_tracker.activity_tracker_targets[local.cloud_logs_target_name].id] + }] : [] + + create_cross_account_auth_policy = !var.skip_cos_kms_auth_policy && var.ibmcloud_kms_api_key != null ? 1 : 0 + activity_tracker_routes = concat(local.activity_tracker_cos_route, local.activity_tracker_cloud_logs_route) + +} + +####################################################################################################################### +# Resource Group +####################################################################################################################### + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.2.0" + existing_resource_group_name = var.existing_resource_group_name +} + + +####################################################################################################################### +# Activity Tracker +####################################################################################################################### + +data "ibm_iam_account_settings" "iam_account_settings" { +} + + + +module "activity_tracker" { + depends_on = [time_sleep.wait_for_atracker_cos_authorization_policy] + source = "../../" + + cos_targets = var.enable_activity_tracker_event_routing_to_cos_bucket ? [ + { + bucket_name = local.cos_target_bucket_name + endpoint = local.cos_target_bucket_endpoint + instance_id = var.existing_cos_instance_crn + target_region = local.default_cos_region + target_name = local.cos_target_name + skip_atracker_cos_iam_auth_policy = var.ibmcloud_cos_api_key != null ? true : var.skip_activity_tracker_cos_auth_policy + service_to_service_enabled = true + } + ] : [] + + cloud_logs_targets = var.enable_activity_tracker_event_routing_to_cloud_logs && var.existing_cloud_logs_instance_crn != null ? [ + { + instance_id = var.existing_cloud_logs_instance_crn + target_region = var.region + target_name = local.cloud_logs_target_name + } + ] : [] + + # Routes + activity_tracker_routes = local.activity_tracker_routes +} + +resource "time_sleep" "wait_for_atracker_cos_authorization_policy" { + count = var.ibmcloud_cos_api_key == null ? 0 : 1 + depends_on = [ibm_iam_authorization_policy.atracker_cos] + create_duration = "30s" +} + +resource "ibm_iam_authorization_policy" "atracker_cos" { + count = var.ibmcloud_cos_api_key != null && !var.skip_activity_tracker_cos_auth_policy ? 1 : 0 + provider = ibm.cos + source_service_account = data.ibm_iam_account_settings.iam_account_settings.account_id + source_service_name = "atracker" + target_service_name = "cloud-object-storage" + target_resource_instance_id = regex(".*:(.*)::", var.existing_cos_instance_crn)[0] + roles = ["Object Writer"] + description = "Permit AT service Object Writer access to COS instance ${var.existing_cos_instance_crn}" +} + +####################################################################################################################### +# KMS Key +####################################################################################################################### + +# If existing KMS intance CRN passed, parse details from it +module "kms_instance_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.kms_encryption_enabled_buckets ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = local.cos_kms_key_crn +} + +module "kms" { + providers = { + ibm = ibm.kms + } + count = (local.use_kms_module && (length(coalesce(local.buckets_config, [])) != 0)) ? 1 : 0 # no need to create any KMS resources if `kms_encryption_enabled_buckets` is false or `existing_cos_kms_key_crn` is provided or `buckets_config` length is 0 + source = "terraform-ibm-modules/kms-all-inclusive/ibm" + version = "5.1.7" + 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.cos_key_ring_name + existing_key_ring = false + force_delete_key_ring = true + keys = [ + { + key_name = local.cos_key_name + standard_key = false + rotation_interval_month = 3 + dual_auth_delete_enabled = false + force_delete = true + } + ] + } + ] +} + +####################################################################################################################### +# COS +####################################################################################################################### + +# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 +resource "time_sleep" "wait_for_authorization_policy" { + depends_on = [ibm_iam_authorization_policy.policy] + count = var.skip_cos_kms_auth_policy ? 0 : 1 + create_duration = "30s" +} + +# Data source to account settings for retrieving COS cross account id +data "ibm_iam_account_settings" "iam_cos_account_settings" { + provider = ibm.cos +} + + +# Create IAM Authorization Policy to allow COS to access KMS for the encryption key +resource "ibm_iam_authorization_policy" "policy" { + count = local.create_cross_account_auth_policy + # Conditionals with providers aren't possible, using ibm.kms as provider incase cross account is enabled + provider = ibm.kms + source_service_account = data.ibm_iam_account_settings.iam_cos_account_settings.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} key ${local.cos_kms_key_id} from the instance ${local.existing_kms_guid}" + resource_attributes { + name = "serviceName" + operator = "stringEquals" + value = local.kms_service + } + 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.cos_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 + } +} + + +module "cos_bucket" { + depends_on = [time_sleep.wait_for_authorization_policy] + providers = { + ibm = ibm.cos + } + count = length(coalesce(local.buckets_config, [])) != 0 ? 1 : 0 # no need to call COS module if consumer is using existing COS bucket + source = "terraform-ibm-modules/cos/ibm//modules/buckets" + version = "9.0.4" + bucket_configs = [ + for value in local.buckets_config : + { + access_tags = value.tag + bucket_name = value.name + add_bucket_name_suffix = var.add_bucket_name_suffix + kms_guid = local.existing_kms_guid + kms_encryption_enabled = var.kms_encryption_enabled_buckets + kms_key_crn = local.cos_kms_key_crn + skip_iam_authorization_policy = false + management_endpoint_type = var.management_endpoint_type_for_bucket + storage_class = value.class + resource_instance_id = var.existing_cos_instance_crn + region_location = local.default_cos_region + force_delete = true + archive_rule = local.archive_rule + expire_rule = local.expire_rule + retention_rule = lookup(local.bucket_retention_configs, value.name, null) + metrics_monitoring = { + usage_metrics_enabled = true + request_metrics_enabled = true + # If `existing_monitoring_crn` is not passed, metrics are sent to the instance associated to the container's location unless otherwise specified in the Metrics Router service configuration. + metrics_monitoring_crn = var.existing_monitoring_crn + } + activity_tracking = { + read_data_events = true + write_data_events = true + management_events = true + } + } + ] +} diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf new file mode 100644 index 00000000..7a0eb8a2 --- /dev/null +++ b/solutions/fully-configurable/outputs.tf @@ -0,0 +1,34 @@ +############################################################################## +# Outputs +############################################################################## + + +## COS Buckets + +output "activity_tracker_cos_target_bucket_name" { + value = var.existing_activity_tracker_cos_target_bucket_name == null ? var.enable_activity_tracker_event_routing_to_cos_bucket ? module.cos_bucket[0].buckets[local.activity_tracker_cos_target_bucket_name].bucket_name : null : var.existing_activity_tracker_cos_target_bucket_name + description = "he name of the object storage bucket which is set as activity tracker event routing target to collect audit events." +} + + +## Activity Tracker Event Routing +output "activity_tracker_targets" { + value = module.activity_tracker.activity_tracker_targets + description = "The map of created Activity Tracker Event Routing targets" +} + +output "activity_tracker_routes" { + value = module.activity_tracker.activity_tracker_routes + description = "The map of created Activity Tracker Event Routing routes" +} + +## KMS +output "kms_key_rings" { + description = "IDs of new KMS Key Rings created" + value = length(module.kms) > 0 ? module.kms[0].key_rings : null +} + +output "kms_keys" { + description = "IDs of new KMS Keys created" + value = length(module.kms) > 0 ? module.kms[0].keys : null +} diff --git a/solutions/fully-configurable/provider.tf b/solutions/fully-configurable/provider.tf new file mode 100644 index 00000000..586a0279 --- /dev/null +++ b/solutions/fully-configurable/provider.tf @@ -0,0 +1,27 @@ +######################################################################################################################## +# Provider config +######################################################################################################################## + +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region + visibility = var.provider_visibility + private_endpoint_type = (var.provider_visibility == "private" && var.region == "ca-mon") ? "vpe" : null +} + +provider "ibm" { + alias = "cos" + ibmcloud_api_key = var.ibmcloud_cos_api_key != null ? var.ibmcloud_cos_api_key : var.ibmcloud_api_key + region = local.default_cos_region + visibility = var.provider_visibility + private_endpoint_type = (var.provider_visibility == "private" && local.default_cos_region == "ca-mon") ? "vpe" : null +} + + +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 + private_endpoint_type = (var.provider_visibility == "private" && local.kms_region == "ca-mon") ? "vpe" : null +} diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf new file mode 100644 index 00000000..4873b2df --- /dev/null +++ b/solutions/fully-configurable/variables.tf @@ -0,0 +1,302 @@ +######################################################################################################################## +# Common variables +######################################################################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The API key to use for IBM Cloud." + sensitive = true +} + +variable "ibmcloud_cos_api_key" { + type = string + description = "The IBM Cloud API key that can create a Cloud Object Storage (COS) instance. If not specified, the 'ibmcloud_api_key' variable is used. Specify this key if the COS instance is in an account that's different from the one associated Observability resources. Leave empty if the same account owns all the instances." + sensitive = true + default = null +} + +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 Observability resources. Leave empty if the same account owns all the instances." + sensitive = true + default = null +} + + +variable "existing_resource_group_name" { + type = string + description = "The name of a new or existing resource group to provision resources in." + default = "Default" +} + + +variable "region" { + type = string + description = "The region to provision all resources in. [Learn more](https://terraform-ibm-modules.github.io/documentation/#/region) about how to select different regions for different services." + default = "us-south" +} + +variable "prefix" { + type = string + nullable = true + description = "The prefix to be added to all resources created by this solution. To skip using a prefix, set this value to null or an empty string. The prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It should not exceed 16 characters, must not end with a hyphen('-'), and can not contain consecutive hyphens ('--'). Example: at-0312. [Learn more](https://terraform-ibm-modules.github.io/documentation/#/prefix.md)." + + validation { + # - null and empty string is allowed + # - Must not contain consecutive hyphens (--): length(regexall("--", var.prefix)) == 0 + # - Starts with a lowercase letter: [a-z] + # - Contains only lowercase letters (a–z), digits (0–9), and hyphens (-) + # - Must not end with a hyphen (-): [a-z0-9] + condition = (var.prefix == null || var.prefix == "" ? true : + alltrue([ + can(regex("^[a-z][-a-z0-9]*[a-z0-9]$", var.prefix)), + length(regexall("--", var.prefix)) == 0 + ]) + ) + error_message = "Prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It must not end with a hyphen('-'), and cannot contain consecutive hyphens ('--')." + } + + validation { + # must not exceed 16 characters in length + condition = length(var.prefix) <= 16 + error_message = "Prefix must not exceed 16 characters." + } +} + + +variable "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 + # Defaulting this to public to workaround https://github.com/IBM-Cloud/terraform-provider-ibm/issues/5977 + default = "private" + + validation { + condition = contains(["public", "private", "public-and-private"], var.provider_visibility) + error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'." + } +} + +############################################################################## +# IBM Cloud Logs +############################################################################## + +variable "existing_cloud_logs_instance_crn" { + type = string + nullable = true + default = null + description = "The CRN of an existing Cloud logs instance." +} + + +############################################################################## +# Activity Tracker Event Routing Variables +############################################################################## + +variable "enable_activity_tracker_event_routing_to_cos_bucket" { + type = bool + description = "Whether to enable event routing from Activity Tracker to the Object Storage bucket." + default = true +} + +variable "enable_activity_tracker_event_routing_to_cloud_logs" { + type = bool + description = "Whether to enable event routing from Activity Tracker to Cloud Logs instance." + default = true +} + +variable "cos_target_name" { + type = string + description = "Name of the cos target for activity tracker event routing." + default = null +} + +variable "cloud_logs_target_name" { + type = string + description = "Name of the cloud logs target for activity tracker event routing." + default = null +} + +variable "activity_tracker_cos_route_name" { + type = string + description = "Name of the cos route for activity tracker event routing." + default = null +} + +variable "activity_tracker_cloud_logs_route_name" { + type = string + description = "Name of the cloud logs route for activity tracker event routing." + default = null +} + + +######################################################################################################################## +# COS variables +######################################################################################################################## + +variable "add_bucket_name_suffix" { + type = bool + description = "Add a randomly generated suffix that is 4 characters in length, to the name of the newly provisioned Cloud Object Storage bucket. Do not use this suffix if you are passing the existing Cloud Object Storage bucket. To manage the name of the Cloud Object Storage bucket manually, use the `activity_tracker_cos_target_bucket_name` variable." + default = true +} + +variable "cos_region" { + type = string + default = null + description = "The Cloud Object Storage region. If no value is provided, the value that is specified in the `region` input variable is used." +} + + +variable "activity_tracker_cos_bucket_retention_policy" { + type = object({ + default = optional(number, 90) + maximum = optional(number, 350) + minimum = optional(number, 90) + permanent = optional(bool, false) + }) + description = "The retention policy of the IBM Cloud Activity Tracker Event Routing COS target bucket. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-activity-tracker/blob/main/solutions/fully-configurable/DA-types.md#activity_tracker_cos_bucket_retention_policy-)" + default = null +} + + +variable "activity_tracker_cos_target_bucket_name" { + type = string + default = "at-events-cos-bucket" + description = "The name of the Cloud Object Storage bucket to create for the Cloud Object Storage target to store AT events. Cloud Object Storage bucket names are globally unique. If the `add_bucket_name_suffix` variable is set to `true`, 4 random characters are added to this name to ensure that the name of the bucket is globally unique. If the prefix input variable is passed, the name of the instance is prefixed to the value in the `-value` format." +} + + +variable "activity_tracker_cos_bucket_access_tags" { + type = list(string) + default = [] + description = "A list of optional access tags to add to the IBM Cloud Activity Tracker Event Routing Cloud Object Storage bucket." +} + + +variable "activity_tracker_cos_target_bucket_class" { + type = string + default = "smart" + description = "The storage class of the newly provisioned Cloud Object Storage bucket. Specify one of the following values for the storage class: `standard`, `vault`, `cold`, `smart` (default), or `onerate_active`." + validation { + condition = contains(["standard", "vault", "cold", "smart", "onerate_active"], var.activity_tracker_cos_target_bucket_class) + error_message = "Specify one of the following values for the `cos_bucket_class`: `standard`, `vault`, `cold`, `smart`, or `onerate_active`." + } +} + + +variable "existing_cos_instance_crn" { + type = string + description = "The CRN of an existing Cloud Object Storage instance." + nullable = false +} + + +variable "existing_activity_tracker_cos_target_bucket_name" { + type = string + nullable = true + default = null + description = "The name of an existing bucket within the Cloud Object Storage instance in which to store IBM Cloud Activity Tracker Event Routing. If an existing Cloud Object Storage bucket is not specified, a bucket is created." +} + +variable "existing_activity_tracker_cos_target_bucket_endpoint" { + type = string + nullable = true + default = null + description = "The name of an existing Cloud Object Storage bucket endpoint to use for setting up IBM Cloud Activity Tracker Event Routing. If an existing endpoint is not specified, the endpoint of the new Cloud Object Storage bucket is used." +} + +variable "skip_cos_kms_auth_policy" { + type = bool + description = "To skip creating an IAM authorization policy that allows the Cloud Object Storage instance to read the encryption key from the key management service (KMS) instance, set this variable to `true`. Before you can create an encrypted Cloud Object Storage bucket, an authorization policy must exist." + default = false +} + + +variable "skip_activity_tracker_cos_auth_policy" { + type = bool + description = "To skip creating an IAM authorization policy that allows the Activity Traker to write to the Cloud Object Storage instance, set this variable to `true`." + default = false +} + +variable "management_endpoint_type_for_bucket" { + description = "The type of endpoint for the IBM Terraform provider to use to manage Cloud Object Storage buckets (`public`, `private`, or `direct`). If you are using a private endpoint, make sure that you enable virtual routing and forwarding (VRF) in your account, and that the Terraform runtime can access the IBM Cloud Private network." + 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 valid. Specify a valid type of endpoint for the IBM Terraform provider to use to manage Cloud Object Storage buckets." + } +} + +variable "existing_monitoring_crn" { + type = string + nullable = true + default = null + description = "The CRN of an IBM Cloud Monitoring instance to to send IBM Cloud Logs buckets 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_activity_tracker_cos_target_bucket_name` is not provided." +} + +######################################################################################################################## +# KMS variables +######################################################################################################################## + +variable "kms_encryption_enabled_buckets" { + description = "Set to true to enable KMS encryption on the Object Storage buckets created for the Activity tracker events storage. When set to true, a value must be passed for either `existing_cos_kms_key_crn` or `existing_kms_instance_crn` (to create a new key)." + type = bool + default = false + nullable = false + + + validation { + condition = var.existing_kms_instance_crn != null ? var.kms_encryption_enabled_buckets : true + error_message = "If passing a value for 'existing_kms_instance_crn', you should set 'kms_encryption_enabled_buckets' to true." + } + + validation { + condition = var.existing_cos_kms_key_crn != null ? var.kms_encryption_enabled_buckets : true + error_message = "If passing a value for 'existing_cos_kms_key_crn', you should set 'kms_encryption_enabled_buckets' to true." + } + + validation { + condition = var.kms_encryption_enabled_buckets ? ((var.existing_cos_kms_key_crn != null || var.existing_kms_instance_crn != null) ? true : false) : true + error_message = "Either 'existing_cos_kms_key_crn' or 'existing_kms_instance_crn' is required if 'kms_encryption_enabled_buckets' is set to true." + } +} +variable "existing_kms_instance_crn" { + type = string + default = null + description = "The CRN of the key management service (KMS) that is used to create keys for encrypting the Cloud Object Storage bucket. If you are not using an existing KMS root key, you must specify this CRN. If the existing Cloud Object Storage bucket details are passed as an input, this value is not required." + 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." + } +} + +variable "existing_cos_kms_key_crn" { + type = string + default = null + description = "Optional. The CRN of an existing key management service (KMS) key to use to encrypt the Cloud Object Storage buckets that this solution creates. To create a key ring and key, pass a value for the `existing_kms_instance_crn` input variable. To use existing Cloud Object Storage buckets, pass a value for `existing_activity_tracker_cos_target_bucket_name` input variables." +} + +variable "kms_endpoint_type" { + type = string + description = "The type of endpoint to use for communicating with the Key Protect or Hyper Protect Crypto Services instance. Possible values: `public`, `private`. Applies only if `existing_cos_kms_key_crn` is not specified." + default = "private" + validation { + condition = can(regex("public|private", var.kms_endpoint_type)) + error_message = "Valid values for the `kms_endpoint_type_value` are `public` or `private`. " + } +} + +variable "cos_key_ring_name" { + type = string + default = "at-cos-key-ring" + description = "The name of the key ring to create for the Cloud Object Storage bucket. If an existing key is used, this variable is not required. If the prefix input variable is passed, the name of the key ring is prefixed to the value in the `prefix-value` format." +} + +variable "cos_key_name" { + type = string + default = "at-cos-key" + description = "The name of the key to create for encrypting the Cloud Object Storage bucket. If an existing key is used, this variable is not required. If the prefix input variable is passed, the name of the key is prefixed to the value in the `prefix-value` format." +} diff --git a/solutions/fully-configurable/version.tf b/solutions/fully-configurable/version.tf new file mode 100644 index 00000000..e0b5d7fb --- /dev/null +++ b/solutions/fully-configurable/version.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + # Lock DA into an exact provider version - renovate automation will keep it updated + ibm = { + source = "ibm-cloud/ibm" + version = "1.79.0" + } + time = { + source = "hashicorp/time" + version = "0.13.0" + } + } +} diff --git a/tests/other_test.go b/tests/other_test.go index 88d360d3..05c89ad8 100644 --- a/tests/other_test.go +++ b/tests/other_test.go @@ -1,2 +1,45 @@ -// Tests in this file are NOT run in the PR pipeline. They are run in the continuous testing pipeline along with the ones in pr_test.go package test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" +) + +// Ensure every example directory has a corresponding test +const advancedExampleDir = "examples/advanced" +const basicExampleDir = "examples/basic" + +func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { + options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ + Testing: t, + TerraformDir: dir, + Prefix: prefix, + ResourceGroup: resourceGroup, + Region: validRegions[rand.Intn(len(validRegions))], + }) + return options +} + +// Consistency test for the basic example +func TestRunBasicExample(t *testing.T) { + t.Parallel() + + options := setupOptions(t, "at-basic", basicExampleDir) + + 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 := setupOptions(t, "at-adv", advancedExampleDir) + + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") +} diff --git a/tests/pr_test.go b/tests/pr_test.go index f886f96c..d934b457 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -2,19 +2,27 @@ package test import ( + "fmt" + "log" "math/rand" + "os" + "strings" "testing" + "github.com/gruntwork-io/terratest/modules/files" + "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/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" + "github.com/stretchr/testify/require" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testschematic" ) // Use existing resource group const resourceGroup = "geretain-test-resources" - -// Ensure every example directory has a corresponding test -const advancedExampleDir = "examples/advanced" -const basicExampleDir = "examples/basic" +const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-resources.yaml" +const fullyConfigurableTerraformDir = "solutions/fully-configurable" var validRegions = []string{ "au-syd", @@ -29,47 +37,165 @@ var validRegions = []string{ "us-east", } -func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { - options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: dir, - Prefix: prefix, - ResourceGroup: resourceGroup, - Region: validRegions[rand.Intn(len(validRegions))], - }) - return options +var permanentResources map[string]interface{} + +func TestMain(m *testing.M) { + + // Read the YAML file contents + var err error + permanentResources, err = common.LoadMapFromYaml(yamlLocation) + if err != nil { + log.Fatal(err) + } + + os.Exit(m.Run()) } -// Consistency test for the basic example -func TestRunBasicExample(t *testing.T) { - t.Parallel() +func setupexistingOptions(t *testing.T, cloudLogsPrefix string) (preReqTfOptions *terraform.Options, err error) { - options := setupOptions(t, "at-basic", basicExampleDir) + realTerraformDir := "./resources" + tempTerraformDir, tempCopyErr := files.CopyTerraformFolderToTemp(realTerraformDir, cloudLogsPrefix) + require.NoError(t, tempCopyErr, fmt.Sprintf("error copying resources to temp folder: %s", tempCopyErr)) + + // 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": cloudLogsPrefix, + }, + // 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, cloudLogsPrefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of pre-req resources failed") + return existingTerraformOptions, existErr + } + + return existingTerraformOptions, nil - 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) { +func TestFullyConfigurableInSchematics(t *testing.T) { t.Parallel() - options := setupOptions(t, "at-adv", advancedExampleDir) + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: "at-fc", + TarIncludePatterns: []string{ + "*.tf", + fullyConfigurableTerraformDir + "/*.tf", + }, + ResourceGroup: resourceGroup, + TemplateFolder: fullyConfigurableTerraformDir, + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + }) + + cloudLogsPrefix := fmt.Sprintf("cloud-logs-%s", strings.ToLower(random.UniqueId())) - output, err := options.RunTestConsistency() + existingTerraformOptions, err := setupexistingOptions(t, cloudLogsPrefix) + + if err != nil { + assert.True(t, err == nil, "cloud logs instance creation failed") + return + } + + // Do not destroy pre-req resources if "DO_NOT_DESTROY_ON_FAILURE" is true + defer func() { + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + + // Do not destroy if tests failed and "DO_NOT_DESTROY_ON_FAILURE" is true + if options.Testing.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the Test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (pre-req resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, cloudLogsPrefix) + logger.Log(t, "END: Destroy (pre-req resources)") + } + }() + 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: "Default", DataType: "string"}, + {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, + {Name: "existing_cos_instance_crn", Value: permanentResources["general_test_storage_cos_instance_crn"], DataType: "string"}, + {Name: "existing_cloud_logs_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "icl_crn"), DataType: "string"}, + {Name: "kms_encryption_enabled_buckets", Value: true, DataType: "bool"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"}, + } + + err = options.RunSchematicTest() assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") } -// Upgrade test (using advanced example) -func TestRunUpgradeExample(t *testing.T) { +func TestFullyConfigurableUpgradeInSchematics(t *testing.T) { t.Parallel() - options := setupOptions(t, "at-adv-upg", advancedExampleDir) + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: "at-fc-upg", + TarIncludePatterns: []string{ + "*.tf", + fullyConfigurableTerraformDir + "/*.tf", + }, + ResourceGroup: resourceGroup, + TemplateFolder: fullyConfigurableTerraformDir, + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + }) + + cloudLogsPrefix := fmt.Sprintf("cloud-logs-%s", strings.ToLower(random.UniqueId())) + + existingTerraformOptions, err := setupexistingOptions(t, cloudLogsPrefix) + + if err != nil { + assert.True(t, err == nil, "cloud logs instance creation failed") + return + } + + // Do not destroy pre-req resources if "DO_NOT_DESTROY_ON_FAILURE" is true + defer func() { + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + + // Do not destroy if tests failed and "DO_NOT_DESTROY_ON_FAILURE" is true + if options.Testing.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the Test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (pre-req resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, cloudLogsPrefix) + logger.Log(t, "END: Destroy (pre-req resources)") + } + }() + + 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: "Default", DataType: "string"}, + {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, + {Name: "existing_cos_instance_crn", Value: permanentResources["general_test_storage_cos_instance_crn"], DataType: "string"}, + {Name: "existing_cloud_logs_instance_crn", Value: terraform.Output(t, existingTerraformOptions, "icl_crn"), DataType: "string"}, + {Name: "kms_encryption_enabled_buckets", Value: true, DataType: "bool"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "region", Value: validRegions[rand.Intn(len(validRegions))], DataType: "string"}, + } - output, err := options.RunTestUpgrade() + err = options.RunSchematicUpgradeTest() if !options.UpgradeTestSkipped { assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") } } diff --git a/tests/resources/README.md b/tests/resources/README.md new file mode 100644 index 00000000..36a84cbb --- /dev/null +++ b/tests/resources/README.md @@ -0,0 +1 @@ +The terraform code in this directory is used for creating cloud logs instance used by activity tracker tests. diff --git a/tests/resources/main.tf b/tests/resources/main.tf new file mode 100644 index 00000000..bc87293c --- /dev/null +++ b/tests/resources/main.tf @@ -0,0 +1,73 @@ +############################################################################## +# 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 instance +############################################################################## + +module "cos" { + source = "terraform-ibm-modules/cos/ibm" + version = "8.19.3" + resource_group_id = module.resource_group.resource_group_id + cos_instance_name = "${var.prefix}-cos" + cos_tags = var.resource_tags + create_cos_bucket = false +} + +############################################################################## +# COS bucket +############################################################################## + +locals { + logs_bucket_name = "${var.prefix}-logs-data" + metrics_bucket_name = "${var.prefix}-metrics-data" +} + +module "buckets" { + source = "terraform-ibm-modules/cos/ibm//modules/buckets" + version = "8.19.3" + bucket_configs = [ + { + bucket_name = local.logs_bucket_name + kms_encryption_enabled = false + region_location = var.region + resource_instance_id = module.cos.cos_instance_id + }, + { + bucket_name = local.metrics_bucket_name + kms_encryption_enabled = false + region_location = var.region + resource_instance_id = module.cos.cos_instance_id + } + ] +} + +############################################################################## +# Cloud Logs +############################################################################## + +module "cloud_logs" { + source = "terraform-ibm-modules/observability-instances/ibm//modules/cloud_logs" + version = "3.4.2" + instance_name = var.prefix + resource_group_id = module.resource_group.resource_group_id + region = var.region + resource_tags = var.resource_tags + enable_platform_logs = false + data_storage = { + logs_data = { + enabled = true + bucket_crn = module.buckets.buckets[local.logs_bucket_name].bucket_crn + bucket_endpoint = module.buckets.buckets[local.logs_bucket_name].s3_endpoint_direct + } + } +} diff --git a/tests/resources/outputs.tf b/tests/resources/outputs.tf new file mode 100644 index 00000000..48863361 --- /dev/null +++ b/tests/resources/outputs.tf @@ -0,0 +1,8 @@ +############################################################################## +# Outputs +############################################################################## + +output "icl_crn" { + description = "IBM Cloud Logs CRN" + value = module.cloud_logs.crn +} diff --git a/tests/resources/provider.tf b/tests/resources/provider.tf new file mode 100644 index 00000000..df45ef50 --- /dev/null +++ b/tests/resources/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/tests/resources/variables.tf b/tests/resources/variables.tf new file mode 100644 index 00000000..d9e01c2f --- /dev/null +++ b/tests/resources/variables.tf @@ -0,0 +1,32 @@ +############################################################################## +# Input variables +############################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key." + sensitive = true +} + +variable "prefix" { + type = string + description = "The prefix to add to all resources." +} + +variable "resource_group" { + type = string + description = "The name of an existing resource group to provision resources in. If not specified, a new resource group is created with the `prefix` variable." + default = null +} + +variable "region" { + type = string + description = "The IBM Cloud region." + default = "us-east" +} + +variable "resource_tags" { + type = list(string) + description = "The tags to add to the created resources." + default = [] +} diff --git a/tests/resources/version.tf b/tests/resources/version.tf new file mode 100644 index 00000000..dca27430 --- /dev/null +++ b/tests/resources/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + ibm = { + source = "ibm-cloud/ibm" + version = ">= 1.74.0" + } + } +}