Let's say you've read my earlier post, Why Infrastructure as Code Matters, did some reading on Terraform, and have started working with Oracle Cloud Infrastructure (OCI) Resource Manager to build your OCI infrastructure. So far, so good...
There's plenty of great resources out there to help you get started, and hopefully this is another. This post is focused on how you can best leverage lists in Terraform using the OCI provider and will focus on how to use the splat expression and index function to find an item in a list of objects.
For our use case, we want to setup continuous log collection via REST APIs in OCI Logging Anlaytics (part of OCI Observability and Management) and there's a couple required policies we need to add via statements referring to a dynamic group. During configuration, the user can either select an existing dynamic group or choose to create a new one.
Let's get started.
First we need to optionally create a new dynamic group then add policies to either the new group or a group that was previously selected by the user during configuration. Here's how it looks when configuring the stack.
The schema.yaml contains the following settings that control how the dynamic group is displayed. It uses the oci:identity:dynamicgroups:id type to dynamically prepopulate the list. You can learn more about how to extend console pages using schema documents here.
setup_policies:
type: boolean
title: Setup default IAM policies
description: Uncheck if you already have all the required policies for Logging Analytics in place.
required: true
default: false
dynamic_group_ocid:
# Prepopulates available values for dynamic group
title: "Dynamic group"
type: oci:identity:dynamicgroups:id
# Determines values for dynamic group prepopulation from selected compartment
dependsOn:
# Specify the tenancy OCID as compartmentId
compartmentId: ${tenancy_ocid}
description: "Select an existing dynamic group to hold relevant policies for the integration. Leave blank to create a new dynamic group."
required: false
default: ""
# Show only when setup_policies is selected
visible: setup_policies
Once the stack is configured, the dynamic_group_ocid variable will contain either the OCID of an existing dynamic group in the compartment or an empty string. If the dynamic group is empty, we create a new one; otherwise use the selected dynamic group. When creating policy statements with dynamic groups we can use either the dynamic-group <dynamic-group_name> or dynamic-group id <dynamic-group_ocid>
syntax but, since we're not sure if we'll know the OCID, we want to use the dynamic-group <dynamic-group_name>
syntax.
To do so means we need to lookup the dynamic group name based on the selected OCID. Let's see how that is done.
The Terraform OCI provider includes a data source that lists the dynamic groups in your tenancy: oci_identity_dynamic_groups
data "oci_identity_dynamic_groups" "lookup_dynamic_groups" {
#Required
compartment_id = var.tenancy_ocid
}
The oci_identity_dynamic_groups exports a property named dynamic_groups which is a list of dynamic groups and has a number of attributes, including name.
To pull the correct name from the selected dynamic group, we to use both the Terraform splat expression and index function together. The splat expression provides a concise way to express the same functionality as a for expression, while the index function allows us to find the element index for a given value in a list. Combined they get us the dynamic group name.
locals {
# Set variable for list of dynamic_groups
dynamic_group_list = data.oci_identity_dynamic_groups.lookup_dynamic_groups.dynamic_groups
# Lookup name of selected dynamic group or create new dynamic group name if none selected. Use splat and index() to find element index for selected ocid in the dynamic group list.
dynamic_group_name = var.dynamic_group_ocid != "" ? local.dynamic_group_list[index(local.dynamic_group_list[*].id, var.dynamic_group_ocid)]["name"] : "mgmtagent-dynamic-group"
}
Now that we have the dynamic group name populated in the local.dynamic_group_name variable, we can write our policy statements without needing to know if the dynamic group exists or not. In our case we want to setup continuous log collection via REST APIs in OCI Logging Analytics (part of OCI Observability and Management) and there's a couple required policies we need to add as seen below.
locals {
policy_name = "${local.dynamic_group_name}-policies"
policy_desc = "Policies allow Management Agent to send data to OCI Logging Analytics."
# Set policy statements for management agent to upload logs to logging analytics
mgmtagent_metrics_stmt = ["Allow dynamic-group ${local.dynamic_group_name} to use METRICS IN COMPARTMENT ID ${var.compartment_ocid}"]
mgmtagent_upload_stmt = ["Allow dynamic-group ${local.dynamic_group_name} to {LOG_ANALYTICS_LOG_GROUP_UPLOAD_LOGS} IN COMPARTMENT ID ${var.compartment_ocid}"]
compiled_policy_statements = concat(local.mgmtagent_metrics_stmt, local.mgmtagent_upload_stmt)
}
Finally we need to create our dynamic group and policy resources. If there is no existing dynamic group selected, then we create a new dynamic group, as seen below. Use the count meta-argument to determine if any resource is created.
# Conditionally create dynamic group if none selected
resource "oci_identity_dynamic_group" "new_dynamic_group" {
count = try(var.dynamic_group_ocid == "") ? 1 : 0
compartment_id = var.tenancy_ocid
name = local.dynamic_group_name
matching_rule = "ALL {resource.type='managementagent', resource.compartment.id='${var.compartment_ocid}'}"
}
# Dynamic group policies
resource "oci_identity_policy" "mgmtagent_policies" {
compartment_id = var.tenancy_ocid
name = local.policy_name
description = local.policy_desc
statements = local.compiled_policy_statements
}
The splat expression and index function, when used together, are simple techniques for handling lists and retrieving the data you want from them. Keep these in mind when working with Resource Manager and Terraform to build your OCI infrastructure. Happy hunting!
~sn