A couple of years ago, we released the CIS OCI Landing Zone Quick Start, a Terraform configuration (or template) blending OCI architecture best practices with CIS (Center for Internet Security) OCI Benchmark recommendations, enabling our customers to quickly start their OCI implementations with a consistent and secure baseline. Since then, it has been widely adopted all over the world for different scenarios, from quick proof of concepts to complex production deployments.
From an architecture perspective, on the front end, the template provides an easy-to-use graphical interface via OCI Resource Manager, which makes it very compelling for teams who are starting with the Infrastructure as Code (IaC) paradigm. On the back end, the template leverages Terraform local modules that can be separately used by teams who want to provide their own configurations. Gluing these two ends together is a central Terraform module that enforces CIS recommendations and orchestrates the multiple underlying modules based on user inputs. The result is the ability to deploy a full-fledged operational tenancy design with the "push of a button." With the CIS Landing Zone in place, customers then deploy their workloads assured of a secure environment.
From a customization perspective, the template behavior can be influenced in a few ways:
From a permissions perspective, the user executing the template requires considerable grants on the tenancy, as it deploys various resource types, including IAM compartments, IAM policies, networking, security services, events, topics, alarms, and many others. To mitigate that, the template embeds a pre-config configuration to prepare the tenancy. It is executed by a user with higher permissions to deploy resources at the tenancy (Root compartment) level and to provision top (a.k.a enclosing) compartments for multiple Landing Zone deployments by lower privileged users.
This update to the CIS Landing Zone is motivated by advanced use case requirements requiring more architectural flexibility and modularity in the current CIS Landing Zone deployment model. These use cases include large customers operating multiple organization entities, complex lifecycle environment configurations, and complex networking and observability models, many of which could require a significant amount of time for the CIS Landing Zone Quick Start (abbreviated Quick Start hereafter) customization. These aspects are better addressed via a consistent module interface enabling the quick assembly of simple to very complex deployment architectures.
At the same time, we want to preserve the investment of the CIS Landing Zone customer base. As the Quick Start template adopts the enhanced modules in the backend, it will maintain backward compatibility, the same user deployment experience, and all principles that contribute to the solution's success.
The modules were once available locally in the same repo as the Quick Start but are now available in separate repositories, grouped into broader categories, to be easily consumed standalone or combined. With this, we hope to streamline the creation of different Landing Zone architectures through composition in a common fashion.
Next, the groupings and their locations. Each repository has a README.md file that mentions the other repositories in the collection.
1) Identity & Access Management: https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-iam
- Compartments
- Policies
- Groups
- Dynamic Groups
2) Networking: https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-networking
- Core networking
- Network Firewall
- ALB (Application Load Balancer)
3) Governance: https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-governance
- Tags
4) Security: https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-security
- Cloud Guard
- Security Zones
- Vaults (a.k.a KMS)
- Vulnerability Scanning
5) Observability & Monitoring: https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-observability
- Events
- Alarms
- Notifications
- Streams
- Service Connectors
Exposing the underlying modules as first-class citizens has two significant benefits:
It facilitates the composition of different Landing Zone architectures. And it does so through a consistent interface across all modules, which will be covered in the following sections.
It favors a provisioning model where permissions are scoped to specific responsibilities.
Clients must declare a JSON object describing the managed resources according to each module’s specification and then use minimal Terraform code to invoke the modules. The JSON object structure accounts for multiple resources indexed by unique and immutable keys. The modules expose a significant amount of OCI resource-type attributes, but many are optional and can be safely suppressed from module users. The attributes freeform_tags and defined_tags are good examples.
For example, the IAM compartments module expects an object like the below snippet. Object variable compartments_configuration fully describes the intended compartments configuration, where TOP-CMP, NETWORK-CMP and SECURITY-CMP are the compartment identifying keys that must not be changed once defined.
A structure like this makes it easier to visualize the actual resource hierarchy as it gets provisioned in the tenancy.
compartments_configuration = {
default_parent_ocid = "ocid1.tenancy.oc1..aaaaaaaa...nuq",
enable_delete = false
compartments = {
TOP-CMP = {
name = "cislz-top-cmp",
description = "CIS Landing Zone enclosing compartment",
children = {
NETWORK-CMP = {
name = "cislz-network-cmp",
description = "CIS Landing Zone Network compartment",
},
SECURITY-CMP = {
name = "cislz-security-cmp",
description = "CIS Landing Zone Security compartment",
}
}
}
}
}
In another example, the Topics module uses notifications_configuration object to describe the intended notifications configuration. Again, NETWORK-TOPIC and DATABASE-TOPIC are the topic identifying keys that must not be changed once defined.
notifications_configuration = {
default_compartment_id = null
topics = {
NETWORK-TOPIC = {
compartment_id = "<REPLACE-BY-COMPARTMENT-OCID-OR-IDENTIFYING-KEY>"
name = "cislz-network-topic"
subscriptions = [{
protocol = "EMAIL"
values = ["email.address1@example.com","email.address2@example.com"]
}]
}
SECURITY-TOPIC = {
compartment_id = "<REPLACE-BY-COMPARTMENT-OCID-OR-IDENTIFYING-KEY>"
name = "cislz-security-topic"
subscriptions = [{
protocol = "SMS"
values = ["+19999999999"]
}]
}
}
}
The identifying keys must be carefully chosen and must not change once defined, as they index the resources within the modules and are written in the module outputs as references to consuming modules.
The modules can be used alone to provide a very isolated and one-off deployment type. However, their real power comes to light when combined together.
The CIS Landing Zone Quick Start is an example. As mentioned before, it orchestrates the underlying modules by managing dependencies amongst them. It is this central entity that chains module outputs into inputs of other modules.
Another excellent orchestrator example is OCI Open LZ Orchestrator Module, that uses the re-engineered modules to assemble highly tailored Landing Zone implementations for complex use case scenarios. By the way, OCI Open LZ provides a generic framework for building landing zones of varying complexity degrees.
Yet another orchestrator example is the SIEM Integration Example Module, that implements a design pattern for integrating OCI logs and events to an external SIEM service.
But the orchestrator pattern is not the only way to combine the modules. They can be loosely associated without this orchestrator figure, which can become considerably complex. All that is needed is having the outputs of producing modules available and an agreed-upon interface for consuming these outputs. On the producing module end, managed resources are exposed as outputs, with each resource referred to by its identifying key. On the consuming module end, the producing module output is obtained, and the same identifying key is used where a dependency exists.
As an example, the IAM Compartments module (producing module) would output the following for the compartments_configuration snippet showed above:
compartments = {
"NETWORK-CMP" = {
"compartment_id" = "ocid1.compartment.oc1..aaaaaaaaa...7da"
"description" = "CIS Landing Zone Network compartment"
"enable_delete" = false
"freeform_tags" = tomap({
"cislz-terraform-module" = "iam-compartments/0.1.3"
})
"id" = "ocid1.compartment.oc1..aaaaaaaa...tgr"
"inactive_state" = tostring(null)
"is_accessible" = true
"name" = "cislz-network-cmp"
"state" = "ACTIVE"
"time_created" = "2023-07-04 18:27:04.377 +0000 UTC"
"timeouts" = null /* object */
}
"SECURITY-CMP" = {
"compartment_id" = "ocid1.compartment.oc1..aaaaaaaaa...7da"
"description" = "CIS Landing Zone Security compartment"
"enable_delete" = false
"freeform_tags" = tomap({
"cislz-terraform-module" = "iam-compartments/0.1.3"
})
"id" = "ocid1.compartment.oc1..aaaaaaaa...xuq"
"inactive_state" = tostring(null)
"is_accessible" = true
"name" = "cislz-security-cmp"
"state" = "ACTIVE"
"time_created" = "2023-07-04 18:27:04.993 +0000 UTC"
"timeouts" = null /* object */
}
"TOP-CMP" = {
"compartment_id" = "ocid1.tenancy.oc1..aaaaaaaa...nuq"
"description" = "CIS Landing Zone enclosing compartment"
"enable_delete" = false
"freeform_tags" = tomap({
"cislz-terraform-module" = "iam-compartments/0.1.3"
})
"id" = "ocid1.compartment.oc1..aaaaaaaaa...7da"
"inactive_state" = tostring(null)
"is_accessible" = true
"name" = "cislz-top-cmp"
"state" = "ACTIVE"
"time_created" = "2023-07-04 18:26:57.975 +0000 UTC"
"timeouts" = null /* object */
}
}
The Topics module (consuming module) would take a notifications_configuration object with a reference to identifying keys "NETWORK-CMP" and "SECURITY-CMP" for the Network and Security compartments, where the topics are expected to be created.
notifications_configuration = {
compartment_id = null
topics = {
NETWORK-TOPIC = {
compartment_id = "NETWORK-CMP"
name = "cislz-network-topic"
subscriptions = [{
protocol = "EMAIL"
values = ["email.address_1@example.com","email.address_2@example.com"]
}]
}
DATABASE-TOPIC = {
compartment_id = "SECURITY-CMP"
name = "cislz-security-topic"
subscriptions = [{
protocol = "SMS"
values = ["+19999999999"]
}]
}
}
}
Additionally, the Topics module also takes the JSON object shared by the IAM Compartments module, where the actual compartment OCIDs are indexed by identifying keys "NETWORK-CMP" and "SECURITY-CMP".
The modules declare external dependencies as a variable. For instance, the Topics module declares an optional compartments_dependency variable, which is expected to be assigned the IAM compartments module output.
variable compartments_dependency {
description = "A map of objects containing the externally managed compartments this module may depend on. All map objects must have the same type and must contain at least an 'id' attribute (representing the compartment OCID) of string type."
type = map(any)
default = null
}
During terraform plan, the Topics module client replaces "NETWORK-CMP" and "SECURITY-CMP" with the actual compartment OCIDs in compartments_dependency variable.
For instance, the modules would not allow the creation of an over permissively IAM policy or network security rule. Prior to these enhancements, such checks had to be enforced by the clients of the modules. By shifting the checks into the modules, solutions with module compositions can rest assured that improper configurations are not accepted. These checks are performed during terraform plan, preventing any bad configuration from being applied into the tenancy.
The snippet below shows a check in action in the IAM policy model. It prevents the creation of a policy statement like "allow group-a to manage all-resources in tenancy".
Error: Resource precondition failed
│
│ on ..\..\main.tf line 35, in resource "oci_identity_policy" "these":
│ 35: condition = local.enable_cis_benchmark_checks ? length([for s in each.value.statements : s if can(regex(local.cis_iam12_regex,lower(s)))]) == 0 : true
│ ├────────────────
│ │ each.value.statements is list of string with 5 elements
│ │ local.cis_iam12_regex is ".*to\\s*manage\\s*all-resources\\s*in\\s*tenancy\\s*$"
│ │ local.enable_cis_benchmark_checks is true
│
│ VALIDATION FAILURE (CIS IAM 1.2): Policy sample-policy has statements that allow a group to manage any resource in the tenancy: "allow group-a to manage all-resources in tenancy"
Aiming at quick and easy deployments, all modules are accompanied by examples with semi-ready input objects that can be put to use immediatelly, by simply replacing placeholders with tenancy specific information and running the typical Terraform workflow.
The enhanced modules are a significant milestone for CIS Landing Zone project. They enable the implementation of landing zones with varying degrees of breadth and complexity through a consistent interface. And by making the modules more visible in their own GitHub repositories, we hope to give our customers an easier path for building and deploying their own landing zones with the same confidence they have when deploying the CIS Landing Zone Quick Start template. For users of the Quick Start template, rest assured it keeps backwards compatibility and preserves the user experience as it adopts the modules.
Previous Post