diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml new file mode 100644 index 0000000..d21da67 --- /dev/null +++ b/.catalog-onboard-pipeline.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: v1 +offerings: + - name: deploy-arch-ibm-apprapp + kind: solution + catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd + offering_id: 045c1169-d15a-4046-ae81-aa3d3348421f + variations: + - name: fully-configurable + mark_ready: true + install_type: fullstack + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south + scope_resource_group_var_name: existing_resource_group_name diff --git a/.releaserc b/.releaserc index 708916f..4160e57 100644 --- a/.releaserc +++ b/.releaserc @@ -10,6 +10,9 @@ }], ["@semantic-release/exec", { "successCmd": "echo \"SEMVER_VERSION=${nextRelease.version}\" >> $GITHUB_ENV" + }], + ["@semantic-release/exec",{ + "publishCmd": "./ci/trigger-catalog-onboarding-pipeline.sh --version=v${nextRelease.version}" }] ] } diff --git a/README.md b/README.md index d66f341..cbbab74 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ For more information on access and permissions, see [app\_config\_collections](#input\_app\_config\_collections) | A list of collections to be added to the App Configuration instance |
list(object({
name = string
collection_id = string
description = optional(string, null)
tags = optional(string, null)
}))
| `[]` | no | | [app\_config\_name](#input\_app\_config\_name) | Name for the App Configuration service instance | `string` | n/a | yes | -| [app\_config\_plan](#input\_app\_config\_plan) | Plan for the App Configuration service instance, valid plans are lite, standardv2, and enterprise. | `string` | `"lite"` | no | +| [app\_config\_plan](#input\_app\_config\_plan) | Plan for the App Configuration service instance, valid plans are lite, basic, standardv2, and enterprise. | `string` | `"lite"` | no | | [app\_config\_service\_endpoints](#input\_app\_config\_service\_endpoints) | Service Endpoints for the App Configuration service instance, valid endpoints are public or public-and-private. | `string` | `"public-and-private"` | no | | [app\_config\_tags](#input\_app\_config\_tags) | Optional list of tags to be added to the App Config instance. | `list(string)` | `[]` | no | | [cbr\_rules](#input\_cbr\_rules) | The list of context-based restriction rules to create. |
list(object({
description = string
account_id = string
tags = optional(list(object({
name = string
value = string
})), [])
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
}))
| `[]` | no | @@ -116,7 +116,7 @@ For more information on access and permissions, see [config\_aggregator\_resource\_collection\_regions](#input\_config\_aggregator\_resource\_collection\_regions) | From which region do you want to collect configuration data? Only applies if `enable_config_aggregator` is set to true. | `list(string)` |
[
"all"
]
| no | | [config\_aggregator\_trusted\_profile\_name](#input\_config\_aggregator\_trusted\_profile\_name) | The name to give the trusted profile that will be created if `enable_config_aggregator` is set to `true`. | `string` | `"config-aggregator-trusted-profile"` | no | | [enable\_config\_aggregator](#input\_enable\_config\_aggregator) | Set to true to enable configuration aggregator. By setting to true a trusted profile will be created with the required access to record configuration data from all resources across regions in your account. [Learn more](https://cloud.ibm.com/docs/app-configuration?topic=app-configuration-ac-configuration-aggregator). | `bool` | `false` | no | -| [region](#input\_region) | The region to provision the App Configuration service, valid regions are us-south, us-east, eu-gb, and au-syd. | `string` | `"us-south"` | no | +| [region](#input\_region) | The region to provision the App Configuration service, valid regions are au-syd, jp-osa, jp-tok, eu-de, eu-gb, eu-es, us-east, us-south, ca-tor, br-sao. | `string` | `"us-south"` | no | | [resource\_group\_id](#input\_resource\_group\_id) | The resource group ID where resources will be provisioned. | `string` | n/a | yes | ### Outputs diff --git a/examples/advanced/main.tf b/examples/advanced/main.tf index 96c9a1f..3d7b3da 100644 --- a/examples/advanced/main.tf +++ b/examples/advanced/main.tf @@ -53,6 +53,7 @@ module "app_config" { app_config_name = "${var.prefix}-app-config" app_config_tags = var.resource_tags enable_config_aggregator = true # See https://cloud.ibm.com/docs/app-configuration?topic=app-configuration-ac-configuration-aggregator + app_config_plan = "standardv2" app_config_collections = [ { name = "${var.prefix}-collection", diff --git a/ibm_catalog.json b/ibm_catalog.json new file mode 100644 index 0000000..c592e6c --- /dev/null +++ b/ibm_catalog.json @@ -0,0 +1,252 @@ +{ + "products": [ + { + "name": "deploy-arch-ibm-apprapp", + "label": "Cloud automation for App Configuration", + "product_kind": "solution", + "tags": [ + "dev_ops", + "ibm_created", + "terraform", + "solution", + "support_ibm" + ], + "keywords": [ + "terraform", + "appconfig", + "app configuration", + "solution", + "IaC", + "infrastructure as code" + ], + "short_description": "Creates and configures an App Configuration service on IBM Cloud", + "long_description": "This deployable architecture automates the provisioning of IBM Cloud App Configuration along with initial collection to help you manage feature flags and dynamic properties at scale. It also includes support for configuration aggregators, enabling centralized monitoring and management of configurations across multiple App Configuration instances. It simplifies onboarding by preconfiguring key resources and provides support for defining context-based restrictions (CBR) to enhance security and control access based on network policies. Ideal for teams adopting feature flagging, experimentation, or remote configuration strategies in cloud-native applications, this solution accelerates setup while following IBM Cloud best practices. Refer [this](https://cloud.ibm.com/docs/app-configuration) for more information.", + "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/blob/main/README.md", + "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-app-configuration/main/images/app_config-icon.png", + "provider_name": "IBM", + "features": [ + { + "title": "Provision Collection", + "description": "Supports creation of collection to help manage feature flags and dynamic properties at scale." + }, + { + "title": "CBR Enhanced Security", + "description": "Provides support for defining context-based restrictions (CBR) to enhance security and control access based on network policies." + }, + { + "title": "Configuration Aggregator", + "description": "Supports creation and management of configuration aggregator to manage configurations across multiple App Configuration instances." + } + ], + "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-app-configuration/issues](https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/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 + }, + { + "key": "existing_resource_group_name", + "required": true, + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + } + }, + { + "key": "region", + "required": true, + "options": [ + { + "displayname": "Osaka (jp-osa)", + "value": "jp-osa" + }, + { + "displayname": "Sydney (au-syd)", + "value": "au-syd" + }, + { + "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 DC (us-east)", + "value": "us-east" + }, + { + "displayname": "Sao Paulo (br-sao)", + "value": "br-sao" + } + ] + }, + { + "key": "app_config_name" + }, + { + "key": "app_config_plan", + "required": true, + "options": [ + { + "displayname": "lite", + "value": "lite" + }, + { + "displayname": "basic", + "value": "basic" + }, + { + "displayname": "standard", + "value": "standardv2" + }, + { + "displayname": "enterprise", + "value": "enterprise" + } + ] + }, + { + "key": "app_config_service_endpoints", + "options": [ + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] + }, + { + "key": "app_config_collections" + }, + { + "key": "app_config_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "enable_config_aggregator" + }, + { + "key": "config_aggregator_trusted_profile_name" + }, + { + "key": "config_aggregator_resource_collection_regions" + }, + { + "key": "config_aggregator_enterprise_id" + }, + { + "key": "config_aggregator_enterprise_trusted_profile_name" + }, + { + "key": "config_aggregator_enterprise_trusted_profile_template_name" + }, + { + "key": "config_aggregator_enterprise_account_group_ids_to_assign" + }, + { + "key": "app_config_cbr_rules" + }, + { + "key": "provider_visibility", + "hidden": true, + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] + } + ], + "architecture": { + "descriptions": "This architecture supports creating and configuring an IBM Cloud App Configuration", + "features": [ + { + "title": "App Configuration instance with Collections", + "description": "Creates App Configuration instance. Collections can be created and configured for the instance" + }, + { + "title": "Use existing resource group", + "description": "Supports deployment into an existing IBM Cloud resource group." + }, + { + "title": "CBR Enhanced Security", + "description": "Enforces network-based access control through context-based restrictions (CBR) rules." + }, + { + "title": "Configuration Aggregator", + "description": "Enables the creation and management of configuration aggregator to consolidate and monitor configurations across multiple App Configuration instances." + } + ], + "diagrams": [ + { + "diagram": { + "caption": "App Configuration", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-app-configuration/main/reference-architecture/app_configuration.svg", + "type": "image/svg+xml" + }, + "description": "**App Configuration on IBM Cloud**

Description
This architecture automates the setup of IBM Cloud App Configuration. The modular design includes the creation of a collection to streamline the management of feature flags and properties, consolidation of multiple App Cpnfiguration instances via configuration aggregator and optionally integrates context-based restrictions (CBR) to improve access control and align with your network security policies." + } + ] + } + } + ] + } + ] +} diff --git a/images/app_config-icon.png b/images/app_config-icon.png new file mode 100644 index 0000000..7a8697b Binary files /dev/null and b/images/app_config-icon.png differ diff --git a/reference-architecture/app_configuration.svg b/reference-architecture/app_configuration.svg new file mode 100644 index 0000000..2d05474 --- /dev/null +++ b/reference-architecture/app_configuration.svg @@ -0,0 +1,4 @@ + + + +
IBM Cloud
Region
Resource Group
App Config
\ 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..0581f0f --- /dev/null +++ b/solutions/fully-configurable/DA-cbr_rules.md @@ -0,0 +1,55 @@ +# Configuring context-based restrictions (CBRs) + +The `app_config_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: `app_config_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 app_config_cbr_rules + + - `description` (required): The description of the rule to create. + - `account_id` (required): The IBM Cloud Account ID + - `tag` (optional): (List) The tags related to CBR rules + - `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. + + +### Example Rule For context-based restrictions configuration + +```hcl +[ + { + description = "Restrict access to App Config from trusted network" + account_id = "" + enforcement_mode = "enabled" + tags = [ + { + name = "env" + value = "dev" + } + ] + rule_contexts = [ + { + attributes = [ + { + name = "networkZoneId" + value = "" + }, + { + "name" : "endpointType", + "value" : "private" + } + ] + } + ] + } +] +``` diff --git a/solutions/fully-configurable/DA-collections.md b/solutions/fully-configurable/DA-collections.md new file mode 100644 index 0000000..a737219 --- /dev/null +++ b/solutions/fully-configurable/DA-collections.md @@ -0,0 +1,36 @@ +# Configuring collections + +The `app_config_collections` input variable allows you to define collections to be added to your IBM Cloud App Configuration instance. Collections are logical groupings of configuration items such as feature flags and properties. You can use collections to organize your configuration items based on environments, services, teams, or other criteria. + +- Variable name: `app_config_collections`. +- Type: A list of objects. One object per collection item. +- Default value: An empty list (`[]`). + +### Options for app_config_collections + + - `name` (required): The name of the collection. This should be a unique, descriptive name identifying the purpose or usage of the collection. + - `collection_id` (required): The unique ID for the collection. This must be unique within the App Configuration instance. + - `description` (optional): A brief description of the collection's purpose or contents. + - `tags` (optional): A string of comma-separated tags that can be used for categorization or filtering. + + +### Example Collection Configuration + +```hcl +[ + { + name = "feature-flags" + collection_id = "ff-collection-001" + description = "Feature flags for development environment" + tags = "env:dev,team:backend" + }, + { + name = "config-settings-ui" + collection_id = "cfg-ui-001" + description = "Configuration settings for UI components" + tags = "env:all,team:frontend" + } +] +``` + +* NOTE: When using the `lite` plan, you can define at most 1 App Configuration collection. diff --git a/solutions/fully-configurable/DA-prefix.md b/solutions/fully-configurable/DA-prefix.md new file mode 100644 index 0000000..822c7d1 --- /dev/null +++ b/solutions/fully-configurable/DA-prefix.md @@ -0,0 +1,38 @@ +# Prefix in Deployable Architecture + +The **`prefix`** input variable allows you to prepend a custom string to the names of all resources created by this automation. This is especially useful for: + +- **Avoiding naming collisions** when deploying the same solution multiple times within the same account. +- **Creating identical infrastructure** across multiple regions or environments. +- **Improving resource traceability** by embedding environment or region identifiers into resource names. + +If you do not wish to use a prefix, you may set the value to `null` or an empty string (`""`). + +**Important**: The automation automatically inserts a hyphen between the prefix and the resource name. Therefore, you do not need to include a hyphen in the prefix yourself. + +### Examples + +Here are some common patterns for using the prefix: + +- **Environment-based**: + - `dev`, `test`, `prod` +- **Environment + Region**: + - `dev-eu-gb`, `prod-us-south`, `test-jp-tok` +- **Project-specific**: + - `webapp-dev`, `ml-prod`, `iot-test` +- **Team or department identifiers**: + - `fin-dev`, `hr-prod`, `eng-test` +- **Date or version-based** (for temporary or experimental deployments): + - `exp-202505`, `v2-dev` + +These conventions help ensure that resources are clearly grouped and easily identifiable, especially in shared or multi-tenant accounts. + +### Naming Rules + +To ensure compatibility and consistency, the prefix must follow these rules: + +- Must begin with a **lowercase letter** +- May contain only **lowercase letters**, **digits**, and **hyphens (`-`)** +- Must **not end** with a hyphen (`-`) +- Must **not contain consecutive hyphens** (`--`) +- Maximum length: **16 characters** diff --git a/solutions/fully-configurable/README.md b/solutions/fully-configurable/README.md new file mode 100644 index 0000000..16c284a --- /dev/null +++ b/solutions/fully-configurable/README.md @@ -0,0 +1,65 @@ +# Cloud automation for App Configuration + +### Configuration + +This solution supports provisioning and configuring the following infrastructure: + +- App Config instance and collections +- Optional context-based restrictions (CBR) +- Configuration aggregator + +![app-configuration-deployable-architecture](../../reference-architecture/app_configuration.svg) + + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [ibm](#requirement\_ibm) | 1.77.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [app\_config](#module\_app\_config) | ../.. | n/a | +| [resource\_group](#module\_resource\_group) | terraform-ibm-modules/resource-group/ibm | 1.2.0 | + +### Resources + +No resources. + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [app\_config\_cbr\_rules](#input\_app\_config\_cbr\_rules) | (Optional, list) A list of context-based restrictions rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/tree/main/solutions/fully-configurable/DA-cbr_rules.md). |
list(object({
description = string
account_id = string
tags = optional(list(object({
name = string
value = string
})), [])
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
}))
| `[]` | no | +| [app\_config\_collections](#input\_app\_config\_collections) | (Optional, list) A list of collections to be added to the App Configuration instance. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/tree/main/solutions/fully-configurable/DA-collections.md). |
list(object({
name = string
collection_id = string
description = optional(string, null)
tags = optional(string, null)
}))
| `[]` | no | +| [app\_config\_name](#input\_app\_config\_name) | Name for the App Configuration service instance | `string` | `"app-config"` | no | +| [app\_config\_plan](#input\_app\_config\_plan) | Plan for the App Configuration service instance | `string` | `"standardv2"` | no | +| [app\_config\_service\_endpoints](#input\_app\_config\_service\_endpoints) | Service Endpoints for the App Configuration service instance, valid endpoints are public or public-and-private. | `string` | `"public-and-private"` | no | +| [app\_config\_tags](#input\_app\_config\_tags) | Optional list of tags to be added to the App Config instance. | `list(string)` | `[]` | no | +| [config\_aggregator\_enterprise\_account\_group\_ids\_to\_assign](#input\_config\_aggregator\_enterprise\_account\_group\_ids\_to\_assign) | A list of enterprise account group IDs to assign the trusted profile template to in order for the accounts to be scanned. Supports passing the string 'all' in the list to assign to all account groups. Only applies if `enable_config_aggregator` is true and a value is being passed for `config_aggregator_enterprise_id`. | `list(string)` |
[
"all"
]
| no | +| [config\_aggregator\_enterprise\_id](#input\_config\_aggregator\_enterprise\_id) | If the account is an enterprise account, this value should be set to the enterprise ID (NOTE: This is different to the account ID). | `string` | `null` | no | +| [config\_aggregator\_enterprise\_trusted\_profile\_name](#input\_config\_aggregator\_enterprise\_trusted\_profile\_name) | The name to give the enterprise viewer trusted profile with that will be created if `enable_config_aggregator` is set to `true` and a value is passed for `config_aggregator_enterprise_id`. | `string` | `"config-aggregator-enterprise-trusted-profile"` | no | +| [config\_aggregator\_enterprise\_trusted\_profile\_template\_name](#input\_config\_aggregator\_enterprise\_trusted\_profile\_template\_name) | The name to give the trusted profile template that will be created if `enable_config_aggregator` is set to `true` and a value is passed for `config_aggregator_enterprise_id`. | `string` | `"config-aggregator-trusted-profile-template"` | no | +| [config\_aggregator\_resource\_collection\_regions](#input\_config\_aggregator\_resource\_collection\_regions) | From which region do you want to collect configuration data? Only applies if `enable_config_aggregator` is set to true. | `list(string)` |
[
"all"
]
| no | +| [config\_aggregator\_trusted\_profile\_name](#input\_config\_aggregator\_trusted\_profile\_name) | The name to give the trusted profile that will be created if `enable_config_aggregator` is set to `true`. | `string` | `"config-aggregator-trusted-profile"` | no | +| [enable\_config\_aggregator](#input\_enable\_config\_aggregator) | Set to true to enable configuration aggregator. By setting to true a trusted profile will be created with the required access to record configuration data from all resources across regions in your account. [Learn more](https://cloud.ibm.com/docs/app-configuration?topic=app-configuration-ac-configuration-aggregator). | `bool` | `false` | no | +| [existing\_resource\_group\_name](#input\_existing\_resource\_group\_name) | The name of an existing resource group to provision resource in. | `string` | `"Default"` | no | +| [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key used to provision resources. | `string` | n/a | yes | +| [prefix](#input\_prefix) | The prefix to be added to all resources created by this solution. To skip using a prefix, set this value to null or an empty string. The prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It should not exceed 16 characters, must not end with a hyphen('-'), and can not contain consecutive hyphens ('--'). Example: prod-us-south. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/tree/main/solutions/fully-configurable/DA-prefix.md). | `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 | +| [region](#input\_region) | The region to provision resources to. | `string` | `"us-south"` | no | + +### Outputs + +| Name | Description | +|------|-------------| +| [app\_config\_account\_id](#output\_app\_config\_account\_id) | Account ID of the App Configuration instance | +| [app\_config\_collection\_ids](#output\_app\_config\_collection\_ids) | List of IDs for the collections in the App Configuration instance | +| [app\_config\_crn](#output\_app\_config\_crn) | CRN of the App Configuration instance | +| [app\_config\_guid](#output\_app\_config\_guid) | GUID of the App Configuration instance | +| [app\_config\_id](#output\_app\_config\_id) | ID of the App Configuration instance | + diff --git a/solutions/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template new file mode 100644 index 0000000..ea4a943 --- /dev/null +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -0,0 +1,7 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "app_config_tags": $TAGS, + "existing_resource_group_name": "geretain-test-resources", + "prefix": $PREFIX, + "region": "us-south" +} diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf new file mode 100644 index 0000000..95f0ce5 --- /dev/null +++ b/solutions/fully-configurable/main.tf @@ -0,0 +1,34 @@ +locals { + prefix = var.prefix != null ? (trimspace(var.prefix) != "" ? "${trimspace(var.prefix)}-" : "") : "" +} + +####################################################################################################################### +# 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 +} + +######################################################################################################################## +# App Config +######################################################################################################################## +module "app_config" { + source = "../.." + resource_group_id = module.resource_group.resource_group_id + region = var.region + app_config_name = "${local.prefix}${var.app_config_name}" + app_config_plan = var.app_config_plan + app_config_service_endpoints = var.app_config_service_endpoints + app_config_tags = var.app_config_tags + app_config_collections = var.app_config_collections + enable_config_aggregator = var.enable_config_aggregator + config_aggregator_trusted_profile_name = var.config_aggregator_trusted_profile_name + config_aggregator_resource_collection_regions = var.config_aggregator_resource_collection_regions + config_aggregator_enterprise_id = var.config_aggregator_enterprise_id + config_aggregator_enterprise_trusted_profile_name = var.config_aggregator_enterprise_trusted_profile_name + config_aggregator_enterprise_trusted_profile_template_name = var.config_aggregator_enterprise_trusted_profile_template_name + config_aggregator_enterprise_account_group_ids_to_assign = var.config_aggregator_enterprise_account_group_ids_to_assign + cbr_rules = var.app_config_cbr_rules +} diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf new file mode 100644 index 0000000..25d1bee --- /dev/null +++ b/solutions/fully-configurable/outputs.tf @@ -0,0 +1,28 @@ +######################################################################################################################## +# Outputs +######################################################################################################################## + +output "app_config_crn" { + description = "CRN of the App Configuration instance" + value = module.app_config.app_config_crn +} + +output "app_config_id" { + description = "ID of the App Configuration instance" + value = module.app_config.app_config_id +} + +output "app_config_guid" { + description = "GUID of the App Configuration instance" + value = module.app_config.app_config_guid +} + +output "app_config_account_id" { + description = "Account ID of the App Configuration instance" + value = module.app_config.app_config_id +} + +output "app_config_collection_ids" { + description = "List of IDs for the collections in the App Configuration instance" + value = module.app_config.app_config_collection_ids +} diff --git a/solutions/fully-configurable/provider.tf b/solutions/fully-configurable/provider.tf new file mode 100644 index 0000000..f69fb6d --- /dev/null +++ b/solutions/fully-configurable/provider.tf @@ -0,0 +1,5 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region + visibility = var.provider_visibility +} diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf new file mode 100644 index 0000000..59e2bc3 --- /dev/null +++ b/solutions/fully-configurable/variables.tf @@ -0,0 +1,166 @@ +######################################################################################################################## +# Common variables +######################################################################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key used to provision resources." + sensitive = true +} + +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" + nullable = false + + 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'." + } +} + +variable "existing_resource_group_name" { + type = string + description = "The name of an existing resource group to provision resource in." + default = "Default" + nullable = false +} + +variable "prefix" { + type = string + nullable = true + description = "The prefix to be added to all resources created by this solution. To skip using a prefix, set this value to null or an empty string. The prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It should not exceed 16 characters, must not end with a hyphen('-'), and can not contain consecutive hyphens ('--'). Example: prod-us-south. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/tree/main/solutions/fully-configurable/DA-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 (-) and must not exceed 16 characters in length: [a-z0-9-]{0,14} + # - Must not end with a hyphen (-): [a-z0-9] + condition = (var.prefix == null || var.prefix == "" ? true : + alltrue([ + can(regex("^[a-z][-a-z0-9]{0,14}[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 should not exceed 16 characters, must not end with a hyphen('-'), and cannot contain consecutive hyphens ('--')." + } +} + +variable "region" { + type = string + description = "The region to provision resources to." + default = "us-south" + nullable = false +} + +######################################################################################################################## +# App Config variables +######################################################################################################################## + + +variable "app_config_name" { + type = string + description = "Name for the App Configuration service instance" + default = "app-config" + nullable = false +} + +variable "app_config_plan" { + type = string + description = "Plan for the App Configuration service instance" + default = "standardv2" + nullable = false +} + +variable "app_config_service_endpoints" { + type = string + description = "Service Endpoints for the App Configuration service instance, valid endpoints are public or public-and-private." + default = "public-and-private" + nullable = false +} + +variable "app_config_collections" { + description = "(Optional, list) A list of collections to be added to the App Configuration instance. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/tree/main/solutions/fully-configurable/DA-collections.md)." + type = list(object({ + name = string + collection_id = string + description = optional(string, null) + tags = optional(string, null) + })) + default = [] +} + +variable "app_config_tags" { + type = list(string) + description = "Optional list of tags to be added to the App Config instance." + default = [] +} + +variable "enable_config_aggregator" { + description = "Set to true to enable configuration aggregator. By setting to true a trusted profile will be created with the required access to record configuration data from all resources across regions in your account. [Learn more](https://cloud.ibm.com/docs/app-configuration?topic=app-configuration-ac-configuration-aggregator)." + type = bool + default = false + nullable = false +} + +variable "config_aggregator_trusted_profile_name" { + description = "The name to give the trusted profile that will be created if `enable_config_aggregator` is set to `true`." + type = string + default = "config-aggregator-trusted-profile" +} + +variable "config_aggregator_resource_collection_regions" { + type = list(string) + description = "From which region do you want to collect configuration data? Only applies if `enable_config_aggregator` is set to true." + default = ["all"] +} + +variable "config_aggregator_enterprise_id" { + type = string + description = "If the account is an enterprise account, this value should be set to the enterprise ID (NOTE: This is different to the account ID). " + default = null +} + +variable "config_aggregator_enterprise_trusted_profile_name" { + description = "The name to give the enterprise viewer trusted profile with that will be created if `enable_config_aggregator` is set to `true` and a value is passed for `config_aggregator_enterprise_id`." + type = string + default = "config-aggregator-enterprise-trusted-profile" +} + +variable "config_aggregator_enterprise_trusted_profile_template_name" { + description = "The name to give the trusted profile template that will be created if `enable_config_aggregator` is set to `true` and a value is passed for `config_aggregator_enterprise_id`." + type = string + default = "config-aggregator-trusted-profile-template" +} + +variable "config_aggregator_enterprise_account_group_ids_to_assign" { + type = list(string) + default = ["all"] + description = "A list of enterprise account group IDs to assign the trusted profile template to in order for the accounts to be scanned. Supports passing the string 'all' in the list to assign to all account groups. Only applies if `enable_config_aggregator` is true and a value is being passed for `config_aggregator_enterprise_id`." + nullable = false +} + +############################################################## +# Context-based restriction (CBR) +############################################################## + +variable "app_config_cbr_rules" { + type = list(object({ + description = string + account_id = string + tags = optional(list(object({ + name = string + value = string + })), []) + rule_contexts = list(object({ + attributes = optional(list(object({ + name = string + value = string + }))) })) + enforcement_mode = string + })) + description = "(Optional, list) A list of context-based restrictions rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-app-configuration/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..4fe2697 --- /dev/null +++ b/solutions/fully-configurable/version.tf @@ -0,0 +1,10 @@ +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.77.0" + } + } +} diff --git a/tests/pr_test.go b/tests/pr_test.go index 3c0dec6..92d254a 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -3,29 +3,48 @@ package test import ( "math/rand" + "os" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testschematic" ) // Use existing resource group +const resourceGroup = "geretain-test-resources" const advancedExampleDir = "examples/advanced" +const fullyConfigFlavorDir = "solutions/fully-configurable" + +var validRegions = []string{ + "au-syd", + "jp-osa", + "jp-tok", + "eu-de", + "eu-gb", + "eu-es", + "us-east", + "us-south", + "ca-tor", + "br-sao", +} + func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { - validRegions := []string{ - "us-south", - "us-east", - "eu-gb", - "au-syd", - } + rand.New(rand.NewSource(time.Now().Unix())) options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ Testing: t, TerraformDir: dir, Prefix: prefix, Region: validRegions[rand.Intn(len(validRegions))], + /* + Comment out the 'ResourceGroup' input to force this tests to create a unique resource group. This is because + there is a restriction with the Event Notification service, which allows only one Lite plan instance per resource group. + */ + // ResourceGroup: resourceGroup, }) return options } @@ -51,3 +70,111 @@ func TestRunUpgradeExample(t *testing.T) { assert.NotNil(t, output, "Expected some output") } } + +func TestFullyConfigurable(t *testing.T) { + t.Parallel() + // 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") + region := validRegions[rand.Intn(len(validRegions))] + prefix := "app-da" + + appConfigCollection := []map[string]any{ + { + "name": "feature-flags", + "collection_id": "feature-flags-001", + "description": "Feature flags for dev environment", + "tags": "type:feature", + }, + } + appConfigTags := []string{"owner:goldeneye", "resource:app-config"} + + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: region, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + "modules/*/*.tf", + fullyConfigFlavorDir + "/*.tf", + }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"app-config-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: "app_config_name", Value: "test-app-config", DataType: "string"}, + {Name: "app_config_plan", Value: "standardv2", DataType: "string"}, + {Name: "app_config_service_endpoints", Value: "public", DataType: "string"}, + {Name: "app_config_collections", Value: appConfigCollection, DataType: "list(object)"}, + {Name: "app_config_tags", Value: appConfigTags, DataType: "list(string)"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "enable_config_aggregator", Value: true, DataType: "bool"}, + } + err := options.RunSchematicTest() + assert.Nil(t, err, "This should not have errored") +} + +func TestUpgradeFullyConfigurable(t *testing.T) { + t.Parallel() + // 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") + region := validRegions[rand.Intn(len(validRegions))] + prefix := "app-upg" + appConfigCollection := []map[string]any{ + { + "name": "feature-flags", + "collection_id": "feature-flags-001", + "description": "Feature flags for dev environment", + "tags": "type:feature", + }, + } + appConfigTags := []string{"owner:goldeneye", "resource:app-config"} + + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: region, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + "modules/*/*.tf", + fullyConfigFlavorDir + "/*.tf", + }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"app-config-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: "app_config_name", Value: "test-app-config", DataType: "string"}, + {Name: "app_config_plan", Value: "standardv2", DataType: "string"}, + {Name: "app_config_service_endpoints", Value: "public", DataType: "string"}, + {Name: "app_config_collections", Value: appConfigCollection, DataType: "list(object)"}, + {Name: "app_config_tags", Value: appConfigTags, DataType: "list(string)"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "enable_config_aggregator", Value: true, DataType: "bool"}, + } + err := options.RunSchematicUpgradeTest() + if !options.UpgradeTestSkipped { + assert.Nil(t, err, "This should not have errored") + } + assert.Nil(t, err, "This should not have errored") +} diff --git a/variables.tf b/variables.tf index 9592724..6644554 100644 --- a/variables.tf +++ b/variables.tf @@ -8,13 +8,13 @@ variable "resource_group_id" { } variable "region" { - description = "The region to provision the App Configuration service, valid regions are us-south, us-east, eu-gb, and au-syd." + description = "The region to provision the App Configuration service, valid regions are au-syd, jp-osa, jp-tok, eu-de, eu-gb, eu-es, us-east, us-south, ca-tor, br-sao." type = string default = "us-south" validation { - condition = contains(["us-east", "us-south", "eu-gb", "au-syd"], var.region) - error_message = "Value for region must be one of the following: ${join(", ", ["us-east", "us-south", "eu-gb", "au-syd"])}" + condition = contains(["au-syd", "jp-osa", "jp-tok", "eu-de", "eu-gb", "eu-es", "us-east", "us-south", "ca-tor", "br-sao"], var.region) + error_message = "Value for region must be one of the following: ${join(", ", ["jp-osa", "au-syd", "jp-tok", "eu-de", "eu-gb", "eu-es", "us-east", "us-south", "ca-tor", "br-sao"])}" } } @@ -29,12 +29,12 @@ variable "app_config_name" { variable "app_config_plan" { type = string - description = "Plan for the App Configuration service instance, valid plans are lite, standardv2, and enterprise." + description = "Plan for the App Configuration service instance, valid plans are lite, basic, standardv2, and enterprise." default = "lite" validation { - condition = contains(["lite", "standardv2", "enterprise"], var.app_config_plan) - error_message = "Value for plan must be one of the following: \"lite\", \"standardv2\", or \"enterprise\"." + condition = contains(["lite", "standardv2", "basic", "enterprise"], var.app_config_plan) + error_message = "Value for plan must be one of the following: \"lite\", \"basic\", \"standardv2\", or \"enterprise\"." } } @@ -64,6 +64,14 @@ variable "app_config_collections" { tags = optional(string, null) })) default = [] + + validation { + condition = ( + var.app_config_plan != "lite" || + length(var.app_config_collections) <= 1 + ) + error_message = "When using the 'lite' plan, you can define at most 1 App Configuration collection." + } } ############################################################## @@ -75,6 +83,12 @@ variable "enable_config_aggregator" { type = bool default = false nullable = false + + # Lite plan does not support enabling Config Aggregator as mention in doc : https://cloud.ibm.com/docs/app-configuration?topic=app-configuration-ac-configuration-aggregator + validation { + condition = !(var.enable_config_aggregator && var.app_config_plan == "lite") + error_message = "The configuration aggregator cannot be enabled when the app_config_plan is set to 'lite'. Please use a different plan (e.g., 'basic', 'standardv2', or 'enterprise')." + } } variable "config_aggregator_trusted_profile_name" {