diff --git a/.gitignore b/.gitignore index 103538f..7e8f435 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ +.idea/** + # Local .terraform directories **/.terraform/* +**/.terraform.lock.hcl # .tfstate files *.tfstate diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..35e442e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing + +Oracle welcomes contributions to this repository from anyone. + +If you want to submit a pull request to fix a bug or enhance an existing feature, please first open an issue and link to that issue when you submit your pull request. + +If you have any questions about a possible submission, feel free to open an issue too. + +## Contributing to the repository + +Pull requests can be made under [The Oracle Contributor Agreement](https://oca.opensource.oracle.com/). + +For pull requests to be accepted, the bottom of your commit message must have the following line using your name and e-mail address as it appears in the OCA Signatories list. + +``` +Signed-off-by: Your Name +``` + +This can be automatically added to pull requests by committing with: + +``` + git commit --signoff +``` + +Only pull requests from committers that can be verified as having +signed the OCA can be accepted. + +### Pull request process + +1. Fork this repository +1. Create a branch in your fork to implement the changes. We recommend using the issue number as part of your branch name, e.g. `1234-fixes` +1. Ensure that any documentation is updated with the changes that are required by your fix. +1. Ensure that any samples are updated if the base image has been changed. +1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly what your changes are meant to do and provide simple steps on how to validate your changes. Ensure that you reference the issue you created as well. We will assign the pull request to 2-3 people for review before it is merged. \ No newline at end of file diff --git a/README.md b/README.md index a590fcd..ecd992b 100644 --- a/README.md +++ b/README.md @@ -1,250 +1,173 @@ -# oci-quickstart-template +# OCI Multicloud Landing Zone for Google Cloud -The [Oracle Cloud Infrastructure (OCI) Quick Start](https://github.com/oracle-quickstart?q=oci-quickstart) is a collection of examples that allow Oracle Cloud Infrastructure users to get a quick start deploying advanced infrastructure on OCI. +![Landing Zone logo](./images/landing_zone_300.png) -The oci-quickstart-template repository contains the template that can be used for accelerating the construction of quickstarts that runs from local Terraform CLI, [OCI Resource Manager](https://docs.cloud.oracle.com/en-us/iaas/Content/ResourceManager/Concepts/resourcemanager.htm) and [OCI Cloud Shell](https://docs.cloud.oracle.com/en-us/iaas/Content/API/Concepts/cloudshellintro.htm). +The [Oracle Cloud Infrastructure (OCI) Quick Start](https://github.com/oracle-quickstart?q=oci-quickstart) is a collection of examples that allow Oracle Cloud Infrastructure users to get a quick start deploying advanced infrastructure on OCI. This repository contains Terraform scripts specific to the [Oracle Database@Google](TODO: link to ODBG) service. -Simple is a sample quickstart terraform template that deploys a virtual machine on a Virtual Cloud Network. -Simple can be customized to subscribe and launch Marketplace images, Platform images or Custom images. -This repo is under active development. Building open source software is a community effort. We're excited to engage with the community building this. +This repository is under active development. Building open source software is a community effort. We're excited to engage with the community building this. -## Resource Manager Deployment +## Overview -This Quick Start uses [OCI Resource Manager](https://docs.cloud.oracle.com/iaas/Content/ResourceManager/Concepts/resourcemanager.htm) to make deployment easy, sign up for an [OCI account](https://cloud.oracle.com/en_US/tryit) if you don't have one, and just click the button below: +A repository contains a collection of [terraform modules](https://developer.hashicorp.com/terraform/language/modules) and templates that helps an Google Cloud administrator configure an Google Cloud environment for Oracle Database@Google and provision database related components (Exadata hardware, Virtual Machine (VM) Clusters, and databases) in Google Cloud. -[![Deploy to Oracle Cloud](https://oci-resourcemanager-plugin.plugins.oci.oraclecloud.com/latest/deploy-to-oracle-cloud.svg)](https://cloud.oracle.com/resourcemanager/stacks/create?region=home&zipUrl=https://github.com/oracle-quickstart/oci-quickstart-template/archive/master.zip) +A user can apply the terraform plans from any computer that has connectivity to both Google Cloud and OCI. -After logging into the console you'll be taken through the same steps described -in the [Deploy](#deploy) section below. +## Prerequisites +To use the Terraform modules and templates in your environment, you must install the following software on the system from which you execute the terraform plans: -Note, if you use this template to create another repo you'll need to change the link for the button to point at your repo. +- [Terraform](https://developer.hashicorp.com/terraform/install) + - Alternate [OpenTofu](https://opentofu.org/docs/intro/) +- [Python 3.x](https://www.python.org/?downloads) (min version 3.4) with packages + - [pip](https://pypi.org/project/pip/) + - [venv](https://docs.python.org/3/library/venv.html) + - (venv) virtual env is recommended (not mandatory) to install python packages for [oci-identity-provider/scripts/requirements.txt](modules/oci-identity/oci-identity-provider/scripts/requirements.txt) +- gcloud CLI - [Install the gcloud CLI](https://cloud.google.com/sdk/docs/install) +- OCI CLI - [Quickstart](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm) + - Setup OCI-CLI to [authenticate to your tenancy](https://docs.oracle.com/en-us/iaas/tools/oci-cli/3.43.1/oci_cli_docs/cmdref/session/authenticate.html) + - Create a token auth profile in your oci config with `` -## Local Development +Dependent which cloud resources a module manages, it will use some subset of the terraform cloud providers: -First off we'll need to do some pre deploy setup. That's all detailed [here](https://github.com/oracle/oci-quickstart-prerequisites). +- [OCI terraform provider](https://registry.terraform.io/providers/oracle/oci/latest/docs) + - In this template example use OCI provider `SecurityToken` auth method, other acceptable provider implementation are described in [OCI terraform provider configuration doc](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm) +- [google terraform provider](https://registry.terraform.io/providers/hashicorp/google/latest/docs) -Note, the instructions below build a `.zip` file from you local copy for use in ORM. -If you want to not use ORM and deploy with the terraform CLI you need to rename -`provider.tf.cli -> provider.tf`. This is because authentication works slightly -differently in ORM vs the CLI. This file is ignored by the build process below. +## Provided Templates -Make sure you have terraform v0.14+ cli installed and accessible from your terminal. +TBD -### Build +*Please read the individual template documentation for more details*. -Simply `build` your package and follow the [Resource Manager instructions](https://docs.cloud.oracle.com/en-us/iaas/Content/ResourceManager/Tasks/managingstacksandjobs.htm#console) for how to create a stack. Prior to building the Stack, you may want to modify some parts of the deployment detailed below. +## Authentication -In order to `build` the zip file with the latest changes you made to this code, you can simply go to [build-orm](./build-orm) folder and use terraform to generate a new zip file: +### OCI Authentication -At first time, you are required to initialize the terraform modules used by the template with `terraform init` command: +You must [authenticate to your oci tenancy](https://docs.oracle.com/en-us/iaas/tools/oci-cli/3.43.1/oci_cli_docs/cmdref/session/authenticate.html) with config auth profile as ``. All available OCI regions are defined in [Regions and Availability Domains](https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm#top). -```bash -$ terraform init +``` shell +oci session authenticate --region= --profile_name= +``` + +Example: + +``` shell +oci session authenticate --region=us-ashburn-1 --profile_name=ONBOARDING +``` + +### gcloud Authentication + +Official gcloud documentation to [authorize the gcloud CLI](https://cloud.google.com/sdk/docs/authorizing) + +``` shell +gcloud auth login +``` + +## Execution -Initializing the backend... +Navigate into the `modules` directory. -Initializing provider plugins... -- Finding latest version of hashicorp/archive... -- Installing hashicorp/archive v2.1.0... -- Installed hashicorp/archive v2.1.0 (signed by HashiCorp) +**Note:** The Terraform state file writes to the directory from where you execute plans. You should keep this file in case you want to use Terraform to modify the environment configuration later. Refer to the Terraform documentation for more persistent and shareable ways to save state. -Terraform has created a lock file .terraform.lock.hcl to record the provider -selections it made above. Include this file in your version control repository -so that Terraform can guarantee to make the same selections by default when -you run "terraform init" in the future. +### Setting up your environment with Terraform -Terraform has been successfully initialized! +Input variable can be set in the [Variable Definitions file](https://developer.hashicorp.com/terraform/language/values/variables#variable-definitions-tfvars-files) (e.g. `terraform.tfvars`) or through the command line or environment variables: -You may now begin working with Terraform. Try running "terraform plan" to see -any changes that are required for your infrastructure. All Terraform commands -should now work. +``` terraform +config_file_profile="" +compartment_ocid="" +region="" +``` + +or via [Command Line](https://developer.hashicorp.com/terraform/language/values/variables#variables-on-the-command-line) + +``` shell +terraform plan -var="config_file_profile=" -var="compartment_ocid=" -var="region=" +``` + +or via [Environment Variables](https://developer.hashicorp.com/terraform/cli/config/environment-variables#tf_var_name) -If you ever set or change modules or backend configuration for Terraform, -rerun this command to reinitialize your working directory. If you forget, other -commands will detect it and remind you to do so if necessary. +``` shell +export TF_VAR_config_file_profile="" +export TF_VAR_compartment_ocid="" +export TF_VAR_region="" ``` -Once terraform is initialized, just run `terraform apply` to generate ORM zip file. +### Initialization -```bash -$ terraform apply +When running for first time, initialize the workspace directory using: -data.archive_file.generate_zip: Refreshing state... +Terraform: -Apply complete! Resources: 0 added, 0 changed, 0 destroyed. +``` shell +terraform init ``` -This command will package the content of `simple` folder into a zip and will store it in the `build-orm\dist` folder. You can check the content of the file by running `unzip -l dist/orm.zip`: - -```bash -$ unzip -l dist/orm.zip -Archive: dist/orm.zip - Length Date Time Name ---------- ---------- ----- ---- - 1140 01-01-2049 00:00 compute.tf - 680 01-01-2049 00:00 data_sources.tf - 1632 01-01-2049 00:00 image_subscription.tf - 1359 01-01-2049 00:00 locals.tf - 13548 01-01-2049 00:00 schema.yaml - 2001 01-01-2049 00:00 network.tf - 2478 01-01-2049 00:00 nsg.tf - 830 01-01-2049 00:00 oci_images.tf - 1092 01-01-2049 00:00 outputs.tf - 44 01-01-2049 00:00 scripts/example.sh - 4848 01-01-2049 00:00 variables.tf - 311 01-01-2049 00:00 versions.tf ---------- ------- - 29963 12 files +OpenTofu: + +``` shell +tofu init ``` -### Deploy +### Application -1. [Login](https://console.us-ashburn-1.oraclecloud.com/resourcemanager/stacks/create) to Oracle Cloud Infrastructure to import the stack - > `Home > Solutions & Platform > Resource Manager > Stacks > Create Stack` +To validate changes described without applying -2. Upload the `orm.zip` and provide a name and description for the stack -![Create Stack](./images/create_orm_stack.png) +Terraform: -3. Configure the Stack. The UI will present the variables to the user dynamically, based on their selections. These are the configuration options: +``` shell +terraform plan +``` -> Compute Configuration - -| VARIABLE | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|COMPUTE COMPARTMENT | Compartment for Compute resources, including Marketplace subscription | -|INSTANCE NAME | Compute instance name| -|DNS HOSTNAME LABEL | DNS Hostname| -|COMPUTE SHAPE | Compatible Compute shape| -|FLEX SHAPE OCPUS | Number of OCPUs, only available for VM.Standard.E3.Flex compute shape| -|AVAILABILITY DOMAIN | Availability Domain| -|PUBLIC SSH KEY STRING | RSA PUBLIC SSH key string used for sign in to the OS| - -> Virtual Cloud Network - -| VARIABLE | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|NETWORK COMPARTMENT | Compartment for all Virtual Cloud Network resources| -|NETWORK STRATEGY | `Create New VCN and Subnet`: Create new network resources during apply.
`Use Existing VCN and Subnet`: Let user select pre-existent network resources.| -|CONFIGURATION STRATEGY | `Use Recommended Configuration`: Use default configuration defined by the Terraform template.
`Customize Network Configuration`: Allow user to customize network configuration such as name, dns label, cidr block for VCN and Subnet.| - -> Virtual Cloud Network - Customize Network Configuration - -| VARIABLE | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|NAME | VCN Display Name| -|DNS LABEL | VCN DNS LABEL| -|CIDR BLOCK | The CIDR of the new Virtual Cloud Network (VCN). If you plan to peer this VCN with another VCN, the VCNs must not have overlapping CIDRs.| - -> Simple Subnet (visible only when `Customize Network Configuration` is selected) - -| VARIABLE | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|SUBNET TYPE | `Public Subnet` or `Private Subnet`| -|NAME | Subnet Display Name| -|DNS LABEL | Subnet DNS LABEL| -|CIDR BLOCK | The CIDR of the Subnet. Should not overlap with any other subnet CIDRs| -|NETWORK SECURITY GROUP CONFIGURATION| `Use Recommended Configuration`: Use default configuration defined by the Terraform template.
`Customize Network Security Group`: Allow user to customize some basic network security group settings.| - -> Network Security Group (visible only when `Customize Network Security Group` is selected) - -| VARIABLE | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|NAME | NSG Display Name| -|ALLOWED INGRESS TRAFFIC (CIDR BLOCK)| WHITELISTED CIDR BLOCK for ingress traffic| -|SSH PORT NUMBER | Default SSH PORT for ingress traffic| -|HTTP PORT NUMBER | Default HTTP PORT for ingress traffic| -|HTTPS PORT NUMBER | Default HTTPS PORT for ingress traffic| - -> Additional Configuration Options - -| VARIABLE | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|TAG KEY NAME | Free-form tag key name| -|TAG VALUE | Free-form tag value| - -4. Click Next and Review the configuration. -5. Click Create button to confirm and create your ORM Stack. -6. On Stack Details page, you can now run `Terraform` commands to manage your infrastructure. You typically start with a plan then run apply to create and make changes to the infrastructure. More details below: - -| TERRAFORM ACTIONS | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|Plan | `terraform plan` is used to create an execution plan. This command is a convenient way to check the execution plan prior to make any changes to the infrastructure resources.| -|Apply | `terraform apply` is used to apply the changes required to reach the desired state of the configuration described by the template.| -|Destroy | `terraform destroy` is used to destroy the Terraform-managed infrastructure.| - -## Customize for Marketplace - -In case you wanted to make changes to this template to use a Marketplace image rather than a platform image or custom image, you need to make the following changes. - -1. Configure Marketplace listing variables on [`variables.tf`](./variables.tf). - -| VARIABLES | DESCRIPTION | -|----------------------------|-----------------------------------------------------------------------| -|mp_subscription_enabled | Enable subscription to Marketplace.| -|mp_listing_id | Marketplace App Catalog Listing OCID.| -|mp_listing_resource_id | Marketplace Listing Image OCID.| -|mp_listing_resource_version | Marketplace Listing Package/Resource Version (Reference value)| - -2. Modify [`compute.tf`](./compute.tf) set `source_details` to refer to `local.compute_image_id` rather than `platform_image_id`. The `local.compute_image_id` holds the logic to either refer to the marketplace image or a custom image, based on the `mp_subscription_enabled` flag. - -```hcl -resource "oci_core_instance" "simple-vm" { - availability_domain = local.availability_domain - compartment_id = var.compute_compartment_ocid - display_name = var.vm_display_name - shape = var.vm_compute_shape - - dynamic "shape_config" { - for_each = local.is_flex_shape - content { - ocpus = shape_config.value - } - } - - - create_vnic_details { - subnet_id = local.use_existing_network ? var.subnet_id : oci_core_subnet.simple_subnet[0].id - display_name = var.subnet_display_name - assign_public_ip = local.is_public_subnet - hostname_label = var.hostname_label - skip_source_dest_check = false - nsg_ids = [oci_core_network_security_group.simple_nsg.id] - } - - source_details { - source_type = "image" - #use a marketplace image or custom image: - source_id = local.compute_image_id - } +OpenTofu: +``` shell +tofu plan ``` -2. Modify [`oci_images.tf`](./oci_images.tf) set `marketplace_source_images` map variable to refer to the marketplace images your Stack will launch. - -```hcl - -variable "marketplace_source_images" { - type = map(object({ - ocid = string - is_pricing_associated = bool - compatible_shapes = list(string) - })) - default = { - main_mktpl_image = { - ocid = "ocid1.image.oc1.." - is_pricing_associated = true - compatible_shapes = [] - } - #Remove comment and add as many marketplace images that your stack references be replicated to other realms - #supporting_image = { - # ocid = "ocid1.image.oc1.." - # is_pricing_associated = false - # compatible_shapes = ["VM.Standard2.2", "VM.Standard.E2.1.Micro"] - #} - } -} +To apply changes and create resources + +Terraform: + +``` shell +terraform apply ``` -2. Run your tests using the Terraform CLI or build a new package and deploy on ORM. +OpenTofu: + +``` shell +tofu apply +``` + +### Destruction + +To remove all resources created in above steps, run destroy: + +Terraform: + +``` shell +terraform destroy +``` + +OpenTofu: + +``` shell +tofu destroy +``` + +## Further Documentation + +- [Terraform OCI Provider](https://www.terraform.io/docs/providers/oci/index.html) +- [Oracle Cloud Infrastructure Provider](https://registry.terraform.io/providers/oracle/oci/latest/docs) +- [Google Provider](https://registry.terraform.io/providers/hashicorp/google/latest/docs) + +**Acknowledgement:** Code derived adapted from samples, examples and documentations provided by above mentioned providers. + +## Contributing + +Learn how to [contribute](./CONTRIBUTING.md). + +## License + +Copyright (c) 2017, 2024 Oracle Corporation and/or its affiliates. Licensed under the [Universal Permissive License 1.0](./LICENSE) as shown at [https://oss.oracle.com/licenses/upl](https://oss.oracle.com/licenses/upl/). \ No newline at end of file diff --git a/build-orm/install.tf b/build-orm/install.tf deleted file mode 100644 index 82de81e..0000000 --- a/build-orm/install.tf +++ /dev/null @@ -1,10 +0,0 @@ -variable "save_to" { - default = "" -} - -data "archive_file" "generate_zip" { - type = "zip" - output_path = (var.save_to != "" ? "${var.save_to}/orm.zip" : "${path.module}/dist/orm.zip") - source_dir = "../" - excludes = ["packer",".terraform.lock.hcl","terraform.tfstate", "terraform.tfvars.template", "terraform.tfvars", "provider.tf", ".terraform", "build-orm", "images", "README.md", "terraform.", "terraform.tfstate.backup", "test", "simple", ".git", "README", ".github", ".gitignore", ".DS_Store", "LICENSE","diagram",] -} diff --git a/compute.tf b/compute.tf deleted file mode 100644 index eb593d3..0000000 --- a/compute.tf +++ /dev/null @@ -1,43 +0,0 @@ -resource "oci_core_instance" "simple-vm" { - availability_domain = local.availability_domain - compartment_id = var.compartment_ocid - display_name = var.vm_display_name - shape = var.vm_compute_shape - - dynamic "shape_config" { - for_each = local.is_flex_shape - content { - ocpus = shape_config.value - } - } - - - create_vnic_details { - subnet_id = local.use_existing_network ? var.subnet_id : oci_core_subnet.simple_subnet[0].id - display_name = var.subnet_display_name - assign_public_ip = local.is_public_subnet - hostname_label = var.hostname_label - skip_source_dest_check = false - nsg_ids = [oci_core_network_security_group.simple_nsg.id] - } - - source_details { - source_type = "image" - source_id = local.platform_image_id - #use a marketplace image or custom image: - #source_id = local.compute_image_id - } - - lifecycle { - ignore_changes = [ - source_details[0].source_id - ] - } - - metadata = { - ssh_authorized_keys = var.ssh_public_key - user_data = base64encode(file("./scripts/example.sh")) - } - - freeform_tags = {(var.tag_key_name) = (var.tag_value)} -} diff --git a/data_sources.tf b/data_sources.tf deleted file mode 100644 index 1254610..0000000 --- a/data_sources.tf +++ /dev/null @@ -1,19 +0,0 @@ -data "oci_identity_availability_domain" "ad" { - compartment_id = var.tenancy_ocid - ad_number = var.availability_domain_number -} - -data "oci_core_images" "autonomous_ol7" { - compartment_id = var.compartment_ocid - operating_system = "Oracle Autonomous Linux" - sort_by = "TIMECREATED" - sort_order = "DESC" - state = "AVAILABLE" - - # filter restricts to OL 7 - filter { - name = "operating_system_version" - values = ["7\\.[0-9]"] - regex = true - } -} diff --git a/image_subscription.tf b/image_subscription.tf deleted file mode 100644 index 0af04df..0000000 --- a/image_subscription.tf +++ /dev/null @@ -1,37 +0,0 @@ -#Get Image Agreement -resource "oci_core_app_catalog_listing_resource_version_agreement" "mp_image_agreement" { - count = local.mp_subscription_enabled - - listing_id = local.listing_id - listing_resource_version = local.listing_resource_version -} - -#Accept Terms and Subscribe to the image, placing the image in a particular compartment -resource "oci_core_app_catalog_subscription" "mp_image_subscription" { - count = local.mp_subscription_enabled - - compartment_id = var.compartment_ocid - eula_link = oci_core_app_catalog_listing_resource_version_agreement.mp_image_agreement[0].eula_link - listing_id = oci_core_app_catalog_listing_resource_version_agreement.mp_image_agreement[0].listing_id - listing_resource_version = oci_core_app_catalog_listing_resource_version_agreement.mp_image_agreement[0].listing_resource_version - oracle_terms_of_use_link = oci_core_app_catalog_listing_resource_version_agreement.mp_image_agreement[0].oracle_terms_of_use_link - signature = oci_core_app_catalog_listing_resource_version_agreement.mp_image_agreement[0].signature - time_retrieved = oci_core_app_catalog_listing_resource_version_agreement.mp_image_agreement[0].time_retrieved - - timeouts { - create = "20m" - } -} - -# Gets the partner image subscription -data "oci_core_app_catalog_subscriptions" "mp_image_subscription" { - count = local.mp_subscription_enabled - - compartment_id = var.compartment_ocid - listing_id = local.listing_id - - filter { - name = "listing_resource_version" - values = [local.listing_resource_version] - } -} diff --git a/images/create_orm_stack.png b/images/create_orm_stack.png deleted file mode 100644 index 2583641..0000000 Binary files a/images/create_orm_stack.png and /dev/null differ diff --git a/images/landing_zone_300.png b/images/landing_zone_300.png new file mode 100644 index 0000000..e5ccd5a Binary files /dev/null and b/images/landing_zone_300.png differ diff --git a/locals.tf b/locals.tf deleted file mode 100644 index 7fa43bf..0000000 --- a/locals.tf +++ /dev/null @@ -1,29 +0,0 @@ -locals { - - # Logic to use AD name provided by user input on ORM or to lookup for the AD name when running from CLI - availability_domain = (var.availability_domain_name != "" ? var.availability_domain_name : data.oci_identity_availability_domain.ad.name) - - # local.use_existing_network referenced in network.tf - use_existing_network = var.network_strategy == var.network_strategy_enum["USE_EXISTING_VCN_SUBNET"] ? true : false - - # local.is_public_subnet referenced in compute.tf - is_public_subnet = var.subnet_type == var.subnet_type_enum["PUBLIC_SUBNET"] ? true : false - - # Logic to select Oracle Autonomous Linux 7 platform image (version pegged in data source filter) - platform_image_id = data.oci_core_images.autonomous_ol7.images[0].id - - # Logic to choose a custom image or a marketplace image. - compute_image_id = var.mp_subscription_enabled ? var.mp_listing_resource_id : var.custom_image_id - - # Local to control subscription to Marketplace image. - mp_subscription_enabled = var.mp_subscription_enabled ? 1 : 0 - - # Marketplace Image listing variables - required for subscription only - listing_id = var.mp_listing_id - listing_resource_id = var.mp_listing_resource_id - listing_resource_version = var.mp_listing_resource_version - - - is_flex_shape = var.vm_compute_shape == "VM.Standard.E3.Flex" ? [var.vm_flex_shape_ocpus]:[] - -} diff --git a/modules/gcp-identity/README.md b/modules/gcp-identity/README.md new file mode 100644 index 0000000..f17b1a7 --- /dev/null +++ b/modules/gcp-identity/README.md @@ -0,0 +1,56 @@ +# GCP Identity - RBAC +## Introduction +Setup Roles Based access control for ODB@G service. + +## Providers + +| Name | Version | +|---------------------------------------------------------------------------|----------| +| [google](https://registry.terraform.io/providers/hashicorp/google/latest) | ~> 5.0.0 | + + +## Inputs Variables +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:-----------------------|:------------------------------------------:|:--------:|--------------:|-------------------:| +| `gcp_org_id` | GCP Organization ID | YES | "" | | +| `gcp_project` | GCP Project ID. | YES | "" | | +| `gcp_region` | GCP Region. | NO | "" | "us-east-1" | +| `group_prefix` | Custom role prefix for all created groups. | NO | {} | | +| `initial_group_config` | Ownership of the newly created groups. | NO | WITH_INITIAL_OWNER | WITH_INITIAL_OWNER | + + +# Setup Roles based access +Setting up RBAC for Exa and ADB-S in Azure using default group names. + +### Authentication +There are several alternatives for GCP authentication, including +using a service account JSON credentials file or using a OAuth access +tokens. + +Details on authentication options can be found on the +[Google provider](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#authentication) details page. + +To run this module on a local workstation `gcloud auth` can be used: + +``` +# authenticate GCP CLI for execution on a local workstation +gcloud auth application-default login +``` + +### Initialize +``` +$ terraform init +``` +### Apply + +To create the default ODB@G roles using non-custom names + +``` +$ terraform apply -var="gcp_org_id=" -var="gcp_project=" +``` + +To create ODB@G roles using a custom prefix. + +``` +$ terraform apply -var="gcp_org_id=" -var="gcp_project=" -var="group_prefix=" +``` \ No newline at end of file diff --git a/modules/gcp-identity/gcp-rbac/README.md b/modules/gcp-identity/gcp-rbac/README.md new file mode 100644 index 0000000..ff1f503 --- /dev/null +++ b/modules/gcp-identity/gcp-rbac/README.md @@ -0,0 +1,46 @@ +# Azure Identity +## Introduction +Setup Roles Based access control for ODB@G service. + +## Providers + +| Name | Version | +|---------------------------------------------------------------------------|----------| +| [google](https://registry.terraform.io/providers/hashicorp/google/latest) | ~> 5.0.0 | + + +## Inputs Variables +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:-----------------------|:---------------------------------------------------------------:|:--------:|--------------:|-----------------------------------------------------------------------------------------------------------------------------------------| +| `gcp_org_id` | GCP Organization ID | YES | "" | | +| `gcp_project` | GCP Project ID. | YES | "" | | +| `gcp_region` | GCP Region. | NO | "" | "us-east-1" | +| `odbag_built_in_roles` | Map of Role ID to Role Description for the roles to be created. | NO | {} |
{
"admin" : {
description : "Group description"
role : "default_role@domain.com"
}
}
| +| `initial_group_config` | Ownership of the newly created groups. | NO | WITH_INITIAL_OWNER | WITH_INITIAL_OWNER | + + +# Setup Roles based access +See [parent project](../README.md) to run this module with the required groups and roles. + +### Authentication +There are several alternatives for GCP authentication, including +using a service account JSON credentials file or using a OAuth access +tokens. + +Details on authentication options can be found on the +[Google provider](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#authentication) details page. + +To run this module on a local workstation `gcloud auth` can be used: + +``` +# authenticate GCP CLI for execution on a local workstation +gcloud auth application-default login +``` + +### Initialize +``` +$ terraform init +``` +### Apply + +See [parent project](../README.md) for apply details. \ No newline at end of file diff --git a/modules/gcp-identity/gcp-rbac/main.tf b/modules/gcp-identity/gcp-rbac/main.tf new file mode 100644 index 0000000..c117543 --- /dev/null +++ b/modules/gcp-identity/gcp-rbac/main.tf @@ -0,0 +1,77 @@ +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +locals { + gcp_org_id = var.gcp_org_id + gcp_project = var.gcp_project + gcp_region = var.gcp_region + odbag_built_in_roles = var.odbag_built_in_roles +} + +/** +# Uncomment this section and the access token line on the google provider to run the terraform script by impersonating +# a service account. This would use the default login account to get a service account token usable by the terraform script. +provider "google" { + alias = "impersonate" + scopes = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email" + ] +} + +data "google_service_account_access_token" "default" { + provider = google.impersonate + target_service_account = "service-account@domain.com" + scopes = ["cloud-platform", "userinfo-email"] + lifetime = "3600s" +} +*/ + +provider "google" { + project = local.gcp_project + region = local.gcp_region +# access_token = data.google_service_account_access_token.default.access_token +# request_timeout = "60s" +} + +data "google_organization" "org" { + organization = var.gcp_org_id +} + +output "google_org" { + value = data.google_organization.org +} + +resource "google_cloud_identity_group" "groups" { + for_each = local.odbag_built_in_roles + + display_name = each.value.description + description = each.value.description + parent = "customers/${data.google_organization.org.directory_customer_id}" + + initial_group_config = var.initial_group_config + + group_key { + id = "${each.key}@${data.google_organization.org.domain}" + } + + labels = { + "cloudidentity.googleapis.com/groups.discussion_forum":"" + "cloudidentity.googleapis.com/groups.security":"" + } +} + +resource "google_project_iam_member" "project" { + depends_on = [google_cloud_identity_group.groups] + + role = "roles/${each.value}" + project = local.gcp_project + member = "group:${each.key}@${data.google_organization.org.domain}" + + for_each = {for group_role, group_config in local.odbag_built_in_roles : group_role => group_config.role if group_config.role != null } +} \ No newline at end of file diff --git a/modules/gcp-identity/gcp-rbac/variables.tf b/modules/gcp-identity/gcp-rbac/variables.tf new file mode 100644 index 0000000..704654f --- /dev/null +++ b/modules/gcp-identity/gcp-rbac/variables.tf @@ -0,0 +1,30 @@ +variable "gcp_org_id" { + description = "GCP Organization ID" + type = string +} + +variable "gcp_project" { + type = string + default = "" + description = "GCP project" +} + +variable "gcp_region" { + type = string + description = "GCP region" +} + +variable "odbag_built_in_roles" { + type = map(object({ + description = string + role = optional(string) + })) + default = {} + description = "Groups required on GCP" +} + +variable "initial_group_config" { + description = "Define the group configuration when it is initialized. Valid values are: WITH_INITIAL_OWNER, EMPTY and INITIAL_GROUP_CONFIG_UNSPECIFIED." + type = string + default = "WITH_INITIAL_OWNER" +} \ No newline at end of file diff --git a/modules/gcp-identity/main.tf b/modules/gcp-identity/main.tf new file mode 100644 index 0000000..8956dbe --- /dev/null +++ b/modules/gcp-identity/main.tf @@ -0,0 +1,59 @@ +locals { + gcp_org_id = var.gcp_org_id + gcp_project = var.gcp_project + gcp_region = var.gcp_region + + initial_group_config = var.initial_group_config + + odbg_db_family_administrators = "${var.group_prefix}odbg-db-family-administrators" + odbg_db_family_readers = "${var.group_prefix}odbg-db-family-readers" + odbg_vm_cluster_administrators = "${var.group_prefix}odbg-vm-cluster-administrators" + odbg_exa_infra_administrators = "${var.group_prefix}odbg-exa-infra-administrators" + odbg_exa_cdb_administrators = "${var.group_prefix}odbg-exa-cdb-administrators" + odbg_exa_pdb_administrators = "${var.group_prefix}odbg-exa-pdb-administrators" + odbg_adbs_db_administrators = "${var.group_prefix}odbg-adbs-db-administrators" + odbg_network_administrators = "${var.group_prefix}odbg-network-administrators" + odbg_costmgmt_administrators = "${var.group_prefix}odbg-costmgmt-administrators" + odbag_built_in_roles = tomap({ + (local.odbg_db_family_administrators) = { + description : "Oracle.Database Owner", + role : "oracledatabase.googleapis.com/admin" + } + (local.odbg_db_family_readers) = { + description : "Oracle.Database Reader" + role : "oracledatabase.googleapis.com/viewer" + } + (local.odbg_vm_cluster_administrators) = { + description : "Oracle.Database VmCluster Administrator" + role : "oracledatabase.googleapis.com/cloudExadataInfrastructureAdmin" + } + (local.odbg_exa_infra_administrators) = { + description : "Oracle.Database Exadata Infrastructure Administrator" + role : "oracledatabase.googleapis.com/cloudVmClusterAdmin " + } + (local.odbg_exa_cdb_administrators) = { + description : "Oracle.Database CDB Administrator" + } + (local.odbg_exa_pdb_administrators) = { + description : "Oracle.Database PDB Administrator" + } + (local.odbg_adbs_db_administrators) = { + description : "Oracle.Database Autonomous Administrator" + } + (local.odbg_network_administrators) = { + description : "Oracle.Database Network Administrator" + } + (local.odbg_costmgmt_administrators) = { + description : "Oracle.Database Cost Management" + } + }) +} + +module "gcp-rbac-setup" { + source = "./gcp-rbac" + gcp_org_id = local.gcp_org_id + gcp_project = local.gcp_project + gcp_region = local.gcp_region + initial_group_config = local.initial_group_config + odbag_built_in_roles = local.odbag_built_in_roles +} diff --git a/modules/gcp-identity/variables.tf b/modules/gcp-identity/variables.tf new file mode 100644 index 0000000..5838fab --- /dev/null +++ b/modules/gcp-identity/variables.tf @@ -0,0 +1,27 @@ +variable "gcp_org_id" { + description = "GCP Organization ID" + type = string +} + +variable "gcp_project" { + type = string + default = "" + description = "GCP project" +} + +variable "gcp_region" { + type = string + description = "GCP region" +} + +variable "group_prefix" { + type = string + default = "" + description = "Group name prefix in GCP" +} + +variable "initial_group_config" { + description = "Define the group configuration when it is initialized. Valid values are: WITH_INITIAL_OWNER, EMPTY and INITIAL_GROUP_CONFIG_UNSPECIFIED." + type = string + default = "WITH_INITIAL_OWNER" +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/.gitignore b/modules/oci-cis-lz-iam/.gitignore new file mode 100644 index 0000000..33462fb --- /dev/null +++ b/modules/oci-cis-lz-iam/.gitignore @@ -0,0 +1,15 @@ +**/*.tfstate* +**/*.out +**/.terraform/* +**/.terraform.* +**/.DS_Store +**/crash.log +**/*.pem +**/terraform_*.tfvars* +**/terraform.tfvars +**/input.auto.tfvars +**/private +**/*.xml +**/*.cert +**/*.ini +**/creds diff --git a/modules/oci-cis-lz-iam/README.md b/modules/oci-cis-lz-iam/README.md new file mode 100644 index 0000000..9f20665 --- /dev/null +++ b/modules/oci-cis-lz-iam/README.md @@ -0,0 +1,43 @@ +# CIS OCI Landing Zone IAM Modules + +![Landing Zone logo](./landing_zone_300.png) + +This repository contains Terraform OCI (Oracle Cloud Infrastructure) modules for IAM (Identity and Access Management) related resources that help customers align their OCI implementations with the CIS (Center for Internet Security) OCI Foundations Benchmark recommendations. + +The following modules are available: +- [Compartments](./compartments/) +- [Policies](./policies/) +- [Groups](./groups/) +- [Dynamic Groups](./dynamic-groups/) +- [Identity Domains](./identity-domains/) + +Within each module you find an *examples* folder. Each example is a fully runnable Terraform configuration that you can quickly test and put to use by modifying the input data according to your own needs. + +The modules support being a passed an object containing references to OCIDs (Oracle Cloud IDs) that they may depend on. Every input attribute that expects an OCID (typically, attribute names ending in _id or _ids) can be given either a literal OCID or a reference (a key) to the OCID. While these OCIDs can be literally obtained from their sources and pasted when setting the modules input attributes, a superior approach is automatically consuming the outputs of producing modules. For instance, the [Compartments](./compartments/) module may depend on tags for applying tag defaults. It can be passed a *tags_dependency* map with objects representing tags produced by the [Tags](https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-governance/tags) module. The external dependency approach helps with the creation of loosely coupled Terraform configurations with clearly defined dependencies between them, avoiding copying and pasting OCIDs. + +## CIS OCI Foundations Benchmark Modules Collection + +This repository is part of a broader collection of repositories containing modules that help customers align their OCI implementations with the CIS OCI Foundations Benchmark recommendations: +- [Identity & Access Management](https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-iam) - current repository +- [Networking](https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-networking) +- [Governance](https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-governance) +- [Security](https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-security) +- [Observability & Monitoring](https://github.com/oracle-quickstart/terraform-oci-cis-landing-zone-observability) +- [Secure Workloads](https://github.com/oracle-quickstart/terraform-oci-secure-workloads) + +The modules in this collection are designed for flexibility, are straightforward to use, and enforce CIS OCI Foundations Benchmark recommendations when possible. + +Using these modules does not require a user extensive knowledge of Terraform or OCI resource types usage. Users declare a JSON object describing the OCI resources according to each module’s specification and minimal Terraform code to invoke the modules. The modules generate outputs that can be consumed by other modules as inputs, allowing for the creation of independently managed operational stacks to automate your entire OCI infrastructure. + +## Contributing +See [CONTRIBUTING.md](./CONTRIBUTING.md). + +## License +Copyright (c) 2023, Oracle and/or its affiliates. + +Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +See [LICENSE](./LICENSE) for more details. + +## Known Issues +None. \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/README.md b/modules/oci-cis-lz-iam/identity-domains/README.md new file mode 100644 index 0000000..3f27cd1 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/README.md @@ -0,0 +1,203 @@ +# CIS OCI Landing Zone Identity Domains Module + +![Landing Zone logo](../landing_zone_300.png) + +This module manages Identity and Access Management (IAM) Identity Domains, Identity Domain Groups, Identity Domain Dynamic Groups, and SAML Identity Providers in Oracle Cloud Infrastructure (OCI) based on maps of objects. Identity Domains are a fundamental construct in OCI IAM, they represent a user a group population and its associated configurations and security settings (such as Federation, MFA). + +Check [module specification](./SPEC.md) for a full description of module requirements, supported variables, managed resources and outputs. + +Check the [examples](./examples/) folder for actual module usage. + +- [Requirements](#requirements) +- [How to Invoke the Module](#invoke) +- [Module Functioning](#functioning) +- [Related Documentation](#related) +- [Known Issues](#issues) + +## Requirements + +### IAM Permissions + +This module requires the following OCI IAM permission: +``` +Allow group to manage domains in tenancy +``` +### Terraform Version < 1.3.x and Optional Object Type Attributes +This module relies on [Terraform Optional Object Type Attributes feature](https://developer.hashicorp.com/terraform/language/expressions/type-constraints#optional-object-type-attributes), which is experimental from Terraform 0.14.x to 1.2.x. It shortens the amount of input values in complex object types, by having Terraform automatically inserting a default value for any missing optional attributes. The feature has been promoted and it is no longer experimental in Terraform 1.3.x. + +**As is, this module can only be used with Terraform versions up to 1.2.x**, because it can be consumed by other modules via [OCI Resource Manager service](https://docs.oracle.com/en-us/iaas/Content/ResourceManager/home.htm), that still does not support Terraform 1.3.x. + +Upon running *terraform plan* with Terraform versions prior to 1.3.x, Terraform displays the following warning: +``` +Warning: Experimental feature "module_variable_optional_attrs" is active +``` + +Note the warning is harmless. The code has been tested with Terraform 1.3.x and the implementation is fully compatible. + +If you really want to use Terraform 1.3.x, in [providers.tf](./providers.tf): +1. Change the terraform version requirement to: +``` +required_version = ">= 1.3.0" +``` +2. Remove the line: +``` +experiments = [module_variable_optional_attrs] +``` + +## How to Invoke the Module + +Terraform modules can be invoked locally or remotely. + +For invoking the module locally, just set the module *source* attribute to the module file path (relative path works). The following example assumes the module is two folders up in the file system. +``` + +module "identity_domains" { + source = "../../" + tenancy_ocid = var.tenancy_ocid + identity_domains_configuration = var.identity_domains_configuration + identity_domain_groups_configuration = var.identity_domain_groups_configuration + identity_domain_dynamic_groups_configuration = var.identity_domain_dynamic_groups_configuration + identity_domain_identity_providers_configuration = var.identity_domain_identity_providers_configuration +} +``` + +For invoking the module remotely, set the module *source* attribute to the groups module folder in this repository, as shown: +``` +module "identity_domains" { + source = "github.com/oracle-quickstart/terraform-oci-cis-landing-zone-iam/identity-domains" + tenancy_id = var.tenancy_id + identity_domains_configuration = var.identity_domains_configuration + identity_domain_groups_configuration = var.identity_domain_groups_configuration + identity_domain_dynamic_groups_configuration = var.identity_domain_dynamic_groups_configuration + identity_domain_identity_providers_configuration = var.identity_domain_identity_providers_configuration +} +``` +For referring to a specific module version, append *ref=\* to the *source* attribute value, as in: +``` + source = "github.com/oracle-quickstart/terraform-oci-cis-landing-zone-iam//identity-domains?ref=v0.1.0" +``` + +## Module Functioning + +The module defines four top-level input variables named *identity_domains_configuration*, *identity_domain_groups_configuration*, *identity_domain_dynamic_groups_configuration*, and *identity_domain_identity_providers_configuration* for identity domains related attributes. A fourth top-level input variable, *compartments_dependency*, is used for bringing in externally managed compartments into identity domains configuration. See [External Dependencies](#extdep) section. + +## Defining Identity Domains +Use *identity_domains_configuration* attribute. It supports the following attributes: + + - **default_compartment_id**: (Optional) defines the compartment for all identity domains, unless overriden by *compartment_id* attribute within each identity domain. This attribute is overloaded: it can be either a compartment OCID or a reference (a key) to the compartment OCID. *tenancy_ocid* is used if undefined. See [External Dependencies](#extdep) section. + - **default_defined_tags**: (Optional) defined tags to apply to all resources, unless overriden by *defined_tags* attribute within each resource. + - **default_freeform_tags**: (Optional) freeform tags to apply to all resources, unless overriden by *freeform_tags* attribute within each resource. + - **identity_domains**: (Optional) the map of objects that defines the identity domains, where each object corresponds to an identity domain resource. + - **compartment_id**: (Optional) The compartment for the identity domain. This attribute is overloaded: it can be either a compartment OCID or a reference (a key) to the compartment OCID. *default_compartment_id* is used if undefined. See [External Dependencies](#extdep). + - **display_name**: (Required) The mutable display name for the identity domain. + - **description**: (Required) The description of the identity domain. + - **home_region**: (Required) The region name of the identity domain. The tenancy home region name is used if undefined. Example: us-ashburn-1 + - **license_type**: (Required) The license type of the identity domain. Examples: free, oracle-apps-premium, premium, external-user. + - **admin_email**: (Optional) The email address of the identity domain administrator. + - **admin_first_name**: (Optional) The first name of the identity domain administrator. + - **admin_last_name**: (Optional) The last name of the identity domain administrator. + - **admin_user_name**: (Optional) The username for the identity domain administrator. + - **is_hidden_on_login**: (Optional) Indicates whether the identity domain is hidden on login screen or not. Example: true + - **is_notification_bypassed**: Indicates if admin user created in the Identity Domain would like to receive notification like welcome email or not. Required field only if admin information is provided, otherwise optional. + - **is_primary_email_required**: (Optional) Indicates whether users in the domain are required to have a primary email address or not. Example: true + - **defined_tags**: (Optional) defined tags to apply to the identity domain. *default_defined_tags* is used if undefined. + - **freeform_tags**: (Optional) free tags to apply to the identity domain. *default_freeform_tags* is used if undefined. + +## Defining Identity Domain Groups +Use *identity_domain_groups_configuration* attribute. It supports the following attributes: + + - **default_identity_domain_id**: (Optional) defines the identity domain for all groups, unless overriden by *identity_domain_id* attribute within each group. This attribute is overloaded: it can be either an existing identity domain OCID (if provisioning the group in an existing identity domain) or the identity domain reference (key) in identity_domains map. + - **default_defined_tags**: (Optional) defined tags to apply to all resources, unless overriden by *defined_tags* attribute within each resource. + - **default_freeform_tags**: (Optional) freeform tags to apply to all resources, unless overriden by *freeform_tags* attribute within each resource. + - **groups**: (Optional) the map of objects that defines groups of users, where each object corresponds to a group resource. + - **identity_domain_id**: (Optional) The identity domain for the group. This attribute is overloaded: it can be either an existing identity domain OCID (if provisioning the group in an existing identity domain) or the identity domain reference (key) in identity_domains map. + - **name**: (Required) The display name of the group. + - **description**: (Optional) The description of the group. + - **requestable**: (Optional) Flag controlling whether group membership can be requested by users through self service console. Example: true + - **members**: (Optional) List of existing user names to assign to the group. + - **defined_tags**: (Optional) defined tags to apply to the group. *default_defined_tags* is used if undefined. + - **freeform_tags**: (Optional) free tags to apply to the group. *default_freeform_tags* is used if undefined. + +## Defining Identity Domain Dynamic Groups +Use *identity_domain_dynamic_groups_configuration* attribute. It supports the following attributes: + + - **default_identity_domain_id**: (Optional) defines the identity domain for all dynamic groups, unless overriden by *identity_domain_id* attribute within each dynamic group. This attribute is overloaded: it can be either an identity domain OCID or a reference (a key) to the identity domain OCID. + - **default_defined_tags**: (Optional) defined tags to apply to all resources, unless overriden by *defined_tags* attribute within each resource. + - **default_freeform_tags**: (Optional) freeform tags to apply to all resources, unless overriden by *freeform_tags* attribute within each resource. + - **dynamic_groups**: (Optional) the map of objects that defines dynamic groups, where each object corresponds to a dynamic group resource. + - **identity_domain_id**: (Optional) The identity domain for the dynamic group. This attribute is overloaded: it can be either an existing identity domain OCID (if provisioning the dynamic group in an existing identity domain) or the identity domain reference (key) in identity_domains map. + - **name**: (Required) The display name of the dynamic group. + - **description**: (Optional) The description of the dynamic group. + - **matching_rule**: (Required) An expression that defines the principals assigned to the dynamic group resource. + - **defined_tags**: (Optional) defined tags to apply to the group. *default_defined_tags* is used if undefined. + - **freeform_tags**: (Optional) free tags to apply to the group. *default_freeform_tags* is used if undefined. + +## Defining Identity Domain Identity Providers +Use *identity_domain_identity_providers_configuration* attribute. It supports SAML Identity Providers which can be configured either by importing the IDP SAML Metadata (XML file) or by directly specifying the identity provider parameters. It supports the following attributes: + + - **default_identity_domain_id**: (Optional) defines the identity domain for all identity providers, unless overriden by *identity_domain_id* attribute within each identity provider. This attribute is overloaded: it can be either an identity domain OCID or a reference (a key) to the identity domain OCID. + - **identity_providers**: (Optional) the map of objects that defines identity providers, where each object corresponds to an identity provider resource. + - **identity_domain_id**: (Optional) The identity domain for the identity provider. This attribute is overloaded: it can be either an existing identity domain OCID (if provisioning the identity provider in an existing identity domain) or the identity domain reference (key) in identity_domains map. + - **name**: (Required) The display name of the identity provider. + - **description**: (Optional) The description of the identity provider. + - **enabled**: (Required) Flag controlling whether the identity provider is enabled or disabled. + - **name_id_format**: (Optional) The requested Name ID format. Possible values: *saml-emailaddress*, *saml-x509*, *saml-kerberos*, *saml-persistent*, *saml-transient*, *saml-unspecified*, *saml-windowsnamequalifier*. Default is *saml-emailaddress*. + - **user_mapping_method**: (Optional) The user identity mapping network for the identity provider. Possible values: *NameIDToUserAttribute*, *AssertionAttributeToUserAttribute*, or *CorrelationPolicyRule*. Default is *NameIDToUserAttribute*. + - **user_mapping_store_attribute**: (Optional) The identity domain user mapping attribute. Default is *username*. + - **assertion_attribute**: (Optional) The assertion attribute name from the IDP when using *user_mapping_method = AssertionAttributeToUserAttribute*. + - **signature_hash_algorithm**: (Optional) The signature has algorithm of the identity provider, either *SHA-256* (Default) or *SHA-1*. + - **send_signing_certificate**: (Optional) Flag controlling whether to send signing certificate with SAML message. Default is *false*. + - **idp_metadata_file**: (Optional) Full path in the local system to the xml file with the Identity Provider SAML metadata. If this parameter is null then the following parameters are used to configure the identity provider entry: *idp_issuer_uri*, *sso_service_url*, *sso_service_binding*, *idp_signing_certificate*, *enable_global_logout*, *idp_logout_request_url*, *idp_logout_response_url*, *idp_logout_binding*. + - **idp_issuer_uri**: The unique identifier of the IdP, also called its Entity ID or Provider ID. This will be the value of the Issuer field in SAML messages sent by this IdP. This parameter is ignored if *idp_metadata_file* is used. + - **sso_service_url**: The service endpoint URL at the Identity provider to which identity domain service will send SAML authentication requests. This parameter is ignored if *idp_metadata_file* is used. + - **sso_service_binding**: Specify either "Post" or "Redirect" whether the identity domain will send SAML authentication requests to the IdP using the HTTP Redirect or HTTP POST method. This must agree with the methods supported by the IdP for the configured IdP SSO service URL. This parameter is ignored if *idp_metadata_file* is used. + - **idp_signing_certificate**: The public key certificate that will be used to verify the signature on SAML messages sent by this IdP. This should be the text containing the base-64-encoded bytes of the certificate, also known as PEM format without the BEGIN CERTIFICATE and END CERTIFICATE lines. This parameter is ignored if *idp_metadata_file* is used. + - **enable_global_logout**: If true (Default value), identity domain will send a SAML logout request to the IdP when the user logs out. If false, no SAML logout request will be sent. This parameter is ignored if *idp_metadata_file* is used. + - **idp_logout_request_url**: The service endpoint URL at the Identity provider to which the identity domain will send SAML logout requests when the user logs out. This parameter is ignored if *idp_metadata_file* is used. + - **idp_logout_response_url**: The service endpoint URL at the Identity provider to which identity domain will send SAML logout responses, when the IdP initiates SAML logout. This parameter is ignored if *idp_metadata_file* is used. + - **idp_logout_binding**: Specify either "Post" or "Redirect" whether the identity domain will send SAML logout requests and responses to the IdP using the HTTP Redirect or HTTP POST method. This must agree with the method supported by the IdP for the configured IdP Logout Request and Response URLs. This parameter is ignored if *idp_metadata_file* is used. + +### Obtaining Identity Domain Metadata + +The SAML configuration requires that the OCI identity domain is also configured in the identity provider side. Generally, like OCI identity domains, identity providers can ingest an XML file containing the partner SAML metadata that sets up the basic configuration. Some identity providers may not support a metadata file, taking in individual metadata values instead. Regardless, OCI identity domain metadata can be easily obtained: + +- **Identity domain metadata file**: https://\.identity.oraclecloud.com/fed/v1/metadata. If the identity provider is ADFS, use this URL instead: https://\/fed/v1/metadata?adfsmode=true. Note however, that the metadata file is publicly available only if allowed by an administrator in identity domain settings. In OCI Console, navigate to the *identity domain of choice -> settings* and make sure the "Configure client access" option is enabled, as shown: + +![Identity Domain Settings](./images/identity-domain-settings.png) + +- **Identity domain metadata values**: while the metadata values are all available in the metadata file, they can be visualized and obtained quite easily from the identity domain in OCI Console. Typically, these values are the *Provider ID*, *Assertion Consumer Service URL*, *Logout Service Endpoint URL*, *Logout Service Return URL*, *Signing Certificate* and *Encryption Certificate*. In OCI Console, navigate to the *identity domain of choice -> Security -> Identity Providers* and click the "Export SAML metadata" button, as shown: + +![Export SAML Metadata](./images/export-saml-metadata.png) + +Then, select the "Manual export" tab: + +![SAML Manual Export](./images/saml-metadata-manual-export.png) + +Note that you can also obtain the metadata URL by clicking in the "Metadata" URL tab and making sure the "Access metadata URL or signing certificate" toggle is on. This is technically equivalent as enabling "Configure client access" in the identity domain settings, as shown previously. + +![SAML Metadata URL](./images/saml-metadata-url.png) + + +Check the [examples](./examples/) folder for overall module usage. See [vision](./examples/vision/README.md) example to deploy two identity domains including groups and dynamic_groups. See [identity-providers](./examples/identity-provider/) example to deploy identity providers. + +### External Dependencies + +An optional feature, external dependencies are resources managed elsewhere that resources managed by this module may depend on. The following dependencies are supported: + +- **compartments_dependency**: 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 with the compartment OCID. This mechanism allows for the usage of referring keys (instead of OCIDs) in identity domains *default_compartment_id* and *compartment_id* attributes. The module replaces the keys by the OCIDs provided within *compartments_dependency* map. Contents of *compartments_dependency is typically the output of a [Compartments module](../compartments/) client. + +## Related Documentation +- [Managing Identity Domains](https://docs.oracle.com/en-us/iaas/Content/Identity/domains/overview.htm) +- [Identity Domains in Terraform OCI Provider](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_domain) +- [Federating with Identity Providers](https://docs.oracle.com/en-us/iaas/Content/Identity/federating/federating_section.htm) +- [SSO Between OCI and Microsoft Azure](https://docs.oracle.com/en-us/iaas/Content/Identity/tutorials/azure_ad/sso_azure/azure_sso.htm) +- [SSO With OCI and Okta](https://docs.oracle.com/en-us/iaas/Content/Identity/tutorials/okta/sso_okta/sso_okta.htm) + + +## Known Issues +1. Terraform will not destroy identity domains. In order do destroy an identity domain, first run ```terraform destroy``` to destroy contained resources (groups, dynamic groups, identity providers...). The error ```"Error: 412-PreConditionFailed, Cannot perform DELETE_DOMAIN operation on Domain with Status CREATED"``` is returned. Then deactivate and delete the identity domain(s) using the OCI console or OCI CLI, as in: +``` + oci iam domain deactivate --domain-id + oci iam domain delete --domain-id +``` + diff --git a/modules/oci-cis-lz-iam/identity-domains/SPEC.md b/modules/oci-cis-lz-iam/identity-domains/SPEC.md new file mode 100644 index 0000000..2ea976b --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/SPEC.md @@ -0,0 +1,54 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | < 1.3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [http](#provider\_http) | n/a | +| [oci](#provider\_oci) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [oci_identity_domain.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_domain) | resource | +| [oci_identity_domains_dynamic_resource_group.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_domains_dynamic_resource_group) | resource | +| [oci_identity_domains_group.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_domains_group) | resource | +| [oci_identity_domains_identity_provider.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_domains_identity_provider) | resource | +| [http_http.saml_metadata](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | data source | +| [oci_identity_domain.dyngrp_domain](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_domain) | data source | +| [oci_identity_domain.grp_domain](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_domain) | data source | +| [oci_identity_domain.idp_domain](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_domain) | data source | +| [oci_identity_domains_users.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_domains_users) | data source | +| [oci_identity_regions.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_regions) | data source | +| [oci_identity_tenancy.this](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_tenancy) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [compartments\_dependency](#input\_compartments\_dependency) | 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. |
map(object({
id = string
}))
| `null` | no | +| [identity\_domain\_dynamic\_groups\_configuration](#input\_identity\_domain\_dynamic\_groups\_configuration) | The identity domain dynamic groups configuration. |
object({
default_identity_domain_id = optional(string)
default_defined_tags = optional(map(string))
default_freeform_tags = optional(map(string))
dynamic_groups = map(object({
identity_domain_id = optional(string),
name = string,
description = optional(string),
matching_rule = string,
defined_tags = optional(map(string)),
freeform_tags = optional(map(string))
}))
})
| `null` | no | +| [identity\_domain\_groups\_configuration](#input\_identity\_domain\_groups\_configuration) | The identity domain groups configuration. |
object({
default_identity_domain_id = optional(string)
default_defined_tags = optional(map(string))
default_freeform_tags = optional(map(string))
groups = map(object({
identity_domain_id = optional(string),
name = string,
description = optional(string),
requestable = optional(bool),
members = optional(list(string)),
defined_tags = optional(map(string)),
freeform_tags = optional(map(string))
}))
})
| `null` | no | +| [identity\_domain\_identity\_providers\_configuration](#input\_identity\_domain\_identity\_providers\_configuration) | The identity domain identity providers configuration. |
object({
default_identity_domain_id = optional(string)
#default_defined_tags = optional(map(string))
#default_freeform_tags = optional(map(string))
identity_providers = map(object({
identity_domain_id = optional(string),
name = string,
description = optional(string),
icon_file = optional(string),
enabled = bool,
name_id_format = optional(string),
user_mapping_method = optional(string),
user_mapping_store_attribute = optional(string),
assertion_attribute = optional(string),

idp_metadata_file = optional(string),

idp_issuer_uri = optional(string),
sso_service_url = optional(string),
sso_service_binding = optional(string),
idp_signing_certificate = optional(string),
idp_encryption_certificate = optional(string),
enable_global_logout = optional(bool),
idp_logout_request_url = optional(string),
idp_logout_response_url = optional(string),
idp_logout_binding = optional(string),

signature_hash_algorithm = optional(string),
send_signing_certificate = optional(bool),
#defined_tags = optional(map(string)),
#freeform_tags = optional(map(string))
}))
})
| `null` | no | +| [identity\_domains\_configuration](#input\_identity\_domains\_configuration) | The identity domains configuration. |
object({
default_compartment_id = optional(string)
default_defined_tags = optional(map(string))
default_freeform_tags = optional(map(string))
identity_domains = map(object({
compartment_id = optional(string),
display_name = string,
description = string,
home_region = optional(string),
license_type = string,
admin_email = optional(string),
admin_first_name = optional(string),
admin_last_name = optional(string),
admin_user_name = optional(string),
is_hidden_on_login = optional(bool),
is_notification_bypassed = optional(bool),
is_primary_email_required = optional(bool),
defined_tags = optional(map(string)),
freeform_tags = optional(map(string))
}))
})
| `null` | no | +| [module\_name](#input\_module\_name) | The module name. | `string` | `"iam-identity-domains"` | no | +| [tenancy\_ocid](#input\_tenancy\_ocid) | The OCID of the tenancy. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [identity\_domain\_dynamic\_groups](#output\_identity\_domain\_dynamic\_groups) | The identity domain groups | +| [identity\_domain\_groups](#output\_identity\_domain\_groups) | The identity domain groups | +| [identity\_domain\_identity\_providers](#output\_identity\_domain\_identity\_providers) | The identity domain groups | +| [identity\_domain\_saml\_metadata](#output\_identity\_domain\_saml\_metadata) | n/a | +| [identity\_domains](#output\_identity\_domains) | The identity domains. | diff --git a/modules/oci-cis-lz-iam/identity-domains/dynamic-groups.tf b/modules/oci-cis-lz-iam/identity-domains/dynamic-groups.tf new file mode 100644 index 0000000..c9d3f2e --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/dynamic-groups.tf @@ -0,0 +1,39 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +data "oci_identity_domain" "dyngrp_domain" { + for_each = (var.identity_domain_dynamic_groups_configuration != null ) ? (var.identity_domain_dynamic_groups_configuration["dynamic_groups"] != null ? var.identity_domain_dynamic_groups_configuration["dynamic_groups"] : {}) : {} + domain_id = each.value.identity_domain_id != null ? each.value.identity_domain_id : var.identity_domain_dynamic_groups_configuration.default_identity_domain_id +} + +resource "oci_identity_domains_dynamic_resource_group" "these" { + for_each = var.identity_domain_dynamic_groups_configuration != null ? var.identity_domain_dynamic_groups_configuration.dynamic_groups : {} + + idcs_endpoint = contains(keys(oci_identity_domain.these),coalesce(each.value.identity_domain_id,"None")) ? oci_identity_domain.these[each.value.identity_domain_id].url : (contains(keys(oci_identity_domain.these),coalesce(var.identity_domain_dynamic_groups_configuration.default_identity_domain_id,"None") ) ? oci_identity_domain.these[var.identity_domain_dynamic_groups_configuration.default_identity_domain_id].url : data.oci_identity_domain.dyngrp_domain[each.key].url) + + display_name = each.value.name + schemas = ["urn:ietf:params:scim:schemas:oracle:idcs:DynamicResourceGroup"] + description = each.value.description + matching_rule = each.value.matching_rule + + + urnietfparamsscimschemasoracleidcsextension_oci_tags { + + dynamic "defined_tags" { + for_each = each.value.defined_tags != null ? each.value.defined_tags : (var.identity_domain_groups_configuration.default_defined_tags !=null ? var.identity_domain_groups_configuration.default_defined_tags : {}) + content { + key = split(".",defined_tags["key"])[1] + namespace = split(".",defined_tags["key"])[0] + value = defined_tags["value"] + } + } + + dynamic "freeform_tags" { + for_each = each.value.freeform_tags != null ? each.value.freeform_tags : (var.identity_domain_groups_configuration.default_freeform_tags !=null ? var.identity_domain_groups_configuration.default_freeform_tags : {}) + content { + key = freeform_tags["key"] + value = freeform_tags["value"] + } + } + + } +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/README.md b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/README.md new file mode 100644 index 0000000..d9a8c17 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/README.md @@ -0,0 +1,16 @@ +# OCI Identity Domains Module Usage Example - OCI-GCP Identity Federation with SAML JIT +## Introduction + +This example shows how to federate existing OCI default Identity Domain with GCP via SAML JIT + +## Using this example +1. Rename *input.auto.tfvars.template* to *\.auto.tfvars*, where *\* is any name of your choice. + +2. Within *\*.auto.tfvars, provide tenancy connectivity information + +3. In this folder, run the typical Terraaform/OpenTofu workflow: +``` +tofu init +tofu plan -out plan.out +tofu apply plan.out +``` \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/input.auto.tfvars.template b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/input.auto.tfvars.template new file mode 100644 index 0000000..b871921 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/input.auto.tfvars.template @@ -0,0 +1,20 @@ +identity_domain_identity_providers_configuration = { + identity_providers = { + GCP = { + identity_domain_id = "" + name = "Google IdP", + description = "GCP Identity Provider", + enabled = true, + idp_metadata_file = "GoogleIDPMetadata.xml", + + signature_hash_algorithm = "SHA-256", + user_mapping_method = "NameIDToUserAttribute", + user_mapping_store_attribute = "emails.primary", + name_id_format = "saml-emailaddress", + + jit_user_prov_enabled = true, + jit_user_prov_attribute_update_enabled = true, + jit_user_prov_create_user_enabled = true, + } + } +} diff --git a/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/main.tf b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/main.tf new file mode 100644 index 0000000..2692182 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/main.tf @@ -0,0 +1,5 @@ +module "identity_domains" { + source = "../../" + tenancy_ocid = var.oci_tenancy_ocid + identity_domain_identity_providers_configuration = var.identity_domain_identity_providers_configuration +} diff --git a/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/outputs.tf b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/outputs.tf new file mode 100644 index 0000000..2cedb92 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/outputs.tf @@ -0,0 +1,9 @@ +output "identity-domain-identity-providers" { + description = "The identity domain identity providers." + value = module.identity_domains.identity_domain_identity_providers +} + +resource "local_file" "identity-domain-metadata" { + content = module.identity_domains.identity_domain_saml_metadata[keys(module.identity_domains.identity_domain_saml_metadata)[0]] + filename = "./identity-domain-metadata.xml" +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/providers.tf b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/providers.tf new file mode 100644 index 0000000..2d79bd0 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/providers.tf @@ -0,0 +1,20 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +terraform { + required_version = ">= 1.3.0" + required_providers { + oci = { + source = "oracle/oci" + } + } +} +provider "oci" { + region = var.oci_region + tenancy_ocid = var.oci_tenancy_ocid + user_ocid = var.oci_user_ocid + fingerprint = var.oci_fingerprint + private_key_path = var.oci_private_key_path + private_key_password = var.oci_private_key_password +} + diff --git a/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/variables.tf b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/variables.tf new file mode 100644 index 0000000..21a9715 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/examples/saml-jit/variables.tf @@ -0,0 +1,16 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# OCI Provider +variable "oci_tenancy_ocid" {} +variable "oci_region" {description = "Your tenancy home region"} +variable "oci_user_ocid" {default = ""} +variable "oci_fingerprint" {default = ""} +variable "oci_private_key_path" {default = ""} +variable "oci_private_key_password" {default = ""} + +# OCI Modules - "identity_domains" +variable "identity_domain_identity_providers_configuration" { + description = "The identity domain identity providers configuration." + type = any +} diff --git a/modules/oci-cis-lz-iam/identity-domains/groups.tf b/modules/oci-cis-lz-iam/identity-domains/groups.tf new file mode 100644 index 0000000..c981637 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/groups.tf @@ -0,0 +1,70 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +data "oci_identity_domain" "grp_domain" { + for_each = (var.identity_domain_groups_configuration != null ) ? (var.identity_domain_groups_configuration["groups"] != null ? var.identity_domain_groups_configuration["groups"] : {}) : {} + domain_id = each.value.identity_domain_id != null ? each.value.identity_domain_id : var.identity_domain_groups_configuration.default_identity_domain_id +} + +data "oci_identity_domains_users" "these" { + + for_each = var.identity_domain_groups_configuration != null ? (var.identity_domain_groups_configuration.groups != null ? var.identity_domain_groups_configuration.groups : {} ): {} + idcs_endpoint = contains(keys(oci_identity_domain.these),coalesce(each.value.identity_domain_id,"None")) ? oci_identity_domain.these[each.value.identity_domain_id].url : (contains(keys(oci_identity_domain.these),coalesce(var.identity_domain_groups_configuration.default_identity_domain_id,"None") ) ? oci_identity_domain.these[var.identity_domain_groups_configuration.default_identity_domain_id].url : data.oci_identity_domain.grp_domain[each.key].url) + + +} + +locals { + users = { for k,g in (var.identity_domain_groups_configuration != null ? var.identity_domain_groups_configuration["groups"]: {}) : k => + { for u in data.oci_identity_domains_users.these[k].users : u.user_name => u.id}} +} + + + +resource "oci_identity_domains_group" "these" { + for_each = var.identity_domain_groups_configuration != null ? var.identity_domain_groups_configuration.groups : {} + + idcs_endpoint = contains(keys(oci_identity_domain.these),coalesce(each.value.identity_domain_id,"None")) ? oci_identity_domain.these[each.value.identity_domain_id].url : (contains(keys(oci_identity_domain.these),coalesce(var.identity_domain_groups_configuration.default_identity_domain_id,"None") ) ? oci_identity_domain.these[var.identity_domain_groups_configuration.default_identity_domain_id].url : data.oci_identity_domain.grp_domain[each.key].url) + + display_name = each.value.name + schemas = ["urn:ietf:params:scim:schemas:core:2.0:Group","urn:ietf:params:scim:schemas:oracle:idcs:extension:group:Group","urn:ietf:params:scim:schemas:extension:custom:2.0:Group"] + + urnietfparamsscimschemasoracleidcsextensiongroup_group { + creation_mechanism = "api" + description = each.value.description + } + + urnietfparamsscimschemasoracleidcsextensionrequestable_group { + requestable = each.value.requestable + } + + dynamic "members" { + for_each = each.value.members != null ? each.value.members : [] + content { + type = "User" + value = local.users[each.key][members["value"]] + + } + } + urnietfparamsscimschemasoracleidcsextension_oci_tags { + + dynamic "defined_tags" { + for_each = each.value.defined_tags != null ? each.value.defined_tags : (var.identity_domain_groups_configuration.default_defined_tags !=null ? var.identity_domain_groups_configuration.default_defined_tags : {}) + content { + key = split(".",defined_tags["key"])[1] + namespace = split(".",defined_tags["key"])[0] + value = defined_tags["value"] + } + } + + + dynamic "freeform_tags" { + for_each = each.value.freeform_tags != null ? each.value.freeform_tags : (var.identity_domain_groups_configuration.default_freeform_tags !=null ? var.identity_domain_groups_configuration.default_freeform_tags : {}) + content { + key = freeform_tags["key"] + value = freeform_tags["value"] + } + } + + } +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/identity-providers.tf b/modules/oci-cis-lz-iam/identity-domains/identity-providers.tf new file mode 100644 index 0000000..8b352ac --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/identity-providers.tf @@ -0,0 +1,94 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +data "oci_identity_domain" "idp_domain" { + for_each = (var.identity_domain_identity_providers_configuration != null ) ? (var.identity_domain_identity_providers_configuration["identity_providers"] != null ? var.identity_domain_identity_providers_configuration["identity_providers"] : {}) : {} + domain_id = each.value.identity_domain_id != null ? each.value.identity_domain_id : var.identity_domain_identity_providers_configuration.default_identity_domain_id +} + +data "http" "saml_metadata" { + for_each = (var.identity_domain_identity_providers_configuration != null ) ? (var.identity_domain_identity_providers_configuration["identity_providers"] != null ? var.identity_domain_identity_providers_configuration["identity_providers"] : {}) : {} + url = contains(keys(oci_identity_domain.these),coalesce(each.value.identity_domain_id,"None")) ? join("",[oci_identity_domain.these[each.value.identity_domain_id].url,local.metadata_uri] ): (contains(keys(oci_identity_domain.these),coalesce(var.identity_domain_identity_providers_configuration.default_identity_domain_id,"None") ) ? join("",[oci_identity_domain.these[var.identity_domain_identity_providers_configuration.default_identity_domain_id].url,local.metadata_uri]) : join("",[data.oci_identity_domain.idp_domain[each.key].url,local.metadata_uri]) ) +} + +locals { + nameid_formats = ["saml-emailaddress", "saml-x509", "saml-kerberos", "saml-persistent", "saml-transient", "saml-unspecified", "saml-windowsnamequalifier","saml-none"] + user_mapping_methods = ["NameIDToUserAttribute", "AssertionAttributeToUserAttribute","CorrelationPolicyRule"] + hash_algorithms = ["SHA-256","SHA-1"] + binding_values = ["Redirect","Post"] + idp_parameter_list = ["idp_issuer_uri","sso_service_url","sso_service_binding","idp_signing_certificate","idp_logout_request_url","idp_logout_response_url"] + metadata_uri = "/fed/v1/metadata" +} + +resource "oci_identity_domains_identity_provider" "these" { + for_each = var.identity_domain_identity_providers_configuration.identity_providers != null ? var.identity_domain_identity_providers_configuration.identity_providers : {} + lifecycle { + ## Check 1: Valid Name ID format. + precondition { + condition = each.value.name_id_format != null ? contains(local.nameid_formats, each.value.name_id_format) : true + error_message = "VALIDATION FAILURE in identity provider \"${each.key}\": invalid value for \"name_id_format\" attribute. Valid values are ${join(",",local.nameid_formats)}." + } + ## Check 2: Valid User Mapping method. + precondition { + condition = each.value.user_mapping_method != null ? contains(local.user_mapping_methods, each.value.user_mapping_method) : true + error_message = "VALIDATION FAILURE in identity provider \"${each.key}\": invalid value for \"user_mapping_method\" attribute. Valid values are ${join(",",local.user_mapping_methods)}." + } + ## Check 3: Valid Signature Hash Algorithm. + precondition { + condition = each.value.signature_hash_algorithm != null ? contains(local.hash_algorithms, each.value.signature_hash_algorithm) : true + error_message = "VALIDATION FAILURE in identity provider \"${each.key}\": invalid value for \"signature_hash_algorithm\" attribute. Valid values are ${join(",",local.hash_algorithms)}." + } + ## Check 4: Valid SSO binding values. + precondition { + condition = each.value.sso_service_binding != null ? contains(local.binding_values, each.value.sso_service_binding) : true + error_message = "VALIDATION FAILURE in identity provider \"${each.key}\": invalid value for \"sso_service_binding\" attribute. Valid values are ${join(",",local.binding_values)}." + } + ## Check 5: Valid Logout binding values. + precondition { + condition = each.value.idp_logout_binding != null ? contains(local.binding_values, each.value.idp_logout_binding) : true + error_message = "VALIDATION FAILURE in identity provider \"${each.key}\": invalid value for \"idp_logout_binding\" attribute. Valid values are ${join(",",local.binding_values)}." + } + ## Check 6: Validate IDP parameters when not using idp metadata file + precondition { + condition = each.value.idp_metadata_file == null ? ( (each.value.idp_issuer_uri !=null && each.value.sso_service_url !=null && each.value.idp_signing_certificate !=null && each.value.idp_logout_request_url !=null && each.value.idp_logout_response_url !=null && each.value.sso_service_binding !=null) ? true : false) : true + error_message = "VALIDATION FAILURE in identity provider \"${each.key}\": when not using \"idp_metadata_file\" attribute, at least the following parameters should be provided: ${join(",",local.idp_parameter_list)}" + } + } + + idcs_endpoint = contains(keys(oci_identity_domain.these),coalesce(each.value.identity_domain_id,"None")) ? oci_identity_domain.these[each.value.identity_domain_id].url : (contains(keys(oci_identity_domain.these),coalesce(var.identity_domain_identity_providers_configuration.default_identity_domain_id,"None") ) ? oci_identity_domain.these[var.identity_domain_identity_providers_configuration.default_identity_domain_id].url : data.oci_identity_domain.idp_domain[each.key].url) + + partner_name = each.value.name + enabled = each.value.enabled + schemas = ["urn:ietf:params:scim:schemas:oracle:idcs:IdentityProvider"] + description = each.value.description + icon_url = each.value.icon_file != null ? file(each.value.icon_file) : null + name_id_format = coalesce(each.value.name_id_format,"saml-emailaddress") + user_mapping_method = coalesce(each.value.user_mapping_method, "NameIDToUserAttribute") + user_mapping_store_attribute = coalesce(each.value.user_mapping_store_attribute, "username") + + metadata = each.value.idp_metadata_file != null ? file(each.value.idp_metadata_file) : null + + partner_provider_id = each.value.idp_metadata_file != null ? null : each.value.idp_issuer_uri + idp_sso_url = each.value.idp_metadata_file != null ? null : each.value.sso_service_url + authn_request_binding = each.value.idp_metadata_file != null ? null : each.value.sso_service_binding + signing_certificate = each.value.idp_metadata_file != null ? null : each.value.idp_signing_certificate + encryption_certificate = each.value.idp_metadata_file != null ? null : each.value.idp_encryption_certificate + logout_enabled = each.value.idp_metadata_file != null ? null : each.value.enable_global_logout + logout_request_url = each.value.idp_metadata_file != null ? null : each.value.idp_logout_request_url + logout_response_url = each.value.idp_metadata_file != null ? null : each.value.idp_logout_response_url + logout_binding = each.value.idp_metadata_file != null ? null : each.value.idp_logout_binding + + signature_hash_algorithm = each.value.signature_hash_algorithm + include_signing_cert_in_signature = each.value.send_signing_certificate + + jit_user_prov_attribute_update_enabled = each.value.jit_user_prov_attribute_update_enabled + jit_user_prov_create_user_enabled = each.value.jit_user_prov_create_user_enabled + jit_user_prov_enabled = each.value.jit_user_prov_enabled + jit_user_prov_group_assertion_attribute_enabled = each.value.jit_user_prov_group_assertion_attribute_enabled + jit_user_prov_group_assignment_method = each.value.jit_user_prov_group_assignment_method + jit_user_prov_group_mapping_mode = each.value.jit_user_prov_group_mapping_mode + jit_user_prov_group_saml_attribute_name = each.value.jit_user_prov_group_saml_attribute_name + jit_user_prov_group_static_list_enabled = each.value.jit_user_prov_group_static_list_enabled + jit_user_prov_ignore_error_on_absent_groups = each.value.jit_user_prov_ignore_error_on_absent_groups + + #OCI Tags not supported +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/main.tf b/modules/oci-cis-lz-iam/identity-domains/main.tf new file mode 100644 index 0000000..27b094d --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/main.tf @@ -0,0 +1,40 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +data "oci_identity_regions" "these" {} + +data "oci_identity_tenancy" "this" { + tenancy_id = var.tenancy_ocid +} + +locals { + regions_map = { for r in data.oci_identity_regions.these.regions : r.key => r.name } + home_region_key = data.oci_identity_tenancy.this.home_region_key +} + + + +resource "oci_identity_domain" "these" { + for_each = var.identity_domains_configuration != null ? var.identity_domains_configuration.identity_domains : {} + #compartment_id = each.value.compartment_id != null ? (length(regexall("^ocid1.*$", each.value.compartment_id)) > 0 ? each.value.compartment_id : var.compartments_dependency[each.value.compartment_id].id) : (length(regexall("^ocid1.*$", var.identity_domains_configuration.default_compartment_id)) > 0 ? var.identity_domains_configuration.default_compartment_id : var.compartments_dependency[var.identity_domains_configuration.default_compartment_id].id) + compartment_id = each.value.compartment_id != null ? (length(regexall("^ocid1.*$", each.value.compartment_id)) > 0 ? each.value.compartment_id : var.compartments_dependency[each.value.compartment_id].id) : (var.identity_domains_configuration.default_compartment_id != null ? (length(regexall("^ocid1.*$", var.identity_domains_configuration.default_compartment_id)) > 0 ? var.identity_domains_configuration.default_compartment_id : var.compartments_dependency[var.identity_domains_configuration.default_compartment_id].id) : var.tenancy_ocid) + + + display_name = each.value.display_name + description = each.value.description + #home_region = each.value.home_region + home_region = each.value.home_region != null ? each.value.home_region : local.regions_map[local.home_region_key] + license_type = each.value.license_type + + admin_email = each.value.admin_email + admin_first_name = each.value.admin_first_name + admin_last_name = each.value.admin_last_name + admin_user_name = each.value.admin_user_name + + is_hidden_on_login = each.value.is_hidden_on_login + is_notification_bypassed = each.value.is_notification_bypassed + is_primary_email_required = each.value.is_primary_email_required + + defined_tags = each.value.defined_tags != null ? each.value.defined_tags : var.identity_domains_configuration.default_defined_tags != null ? var.identity_domains_configuration.default_defined_tags : null + freeform_tags = merge(local.cislz_module_tag, each.value.freeform_tags != null ? each.value.freeform_tags : var.identity_domains_configuration.default_freeform_tags != null ? var.identity_domains_configuration.default_freeform_tags : null) +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/metadata.tf b/modules/oci-cis-lz-iam/identity-domains/metadata.tf new file mode 100644 index 0000000..bf48fbe --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/metadata.tf @@ -0,0 +1,7 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +#-- Used to inform module and release number. +locals { + cislz_module_tag = {"cislz-terraform-module" : fileexists("${path.module}/../release.txt") ? "${var.module_name}/${file("${path.module}/../release.txt")}" : "${var.module_name}"} +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/outputs.tf b/modules/oci-cis-lz-iam/identity-domains/outputs.tf new file mode 100644 index 0000000..8cfa62b --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/outputs.tf @@ -0,0 +1,26 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +output "identity_domains" { + description = "The identity domains." + value = oci_identity_domain.these +} + +output "identity_domain_groups" { + description = "The identity domain groups" + value = oci_identity_domains_group.these +} + +output "identity_domain_dynamic_groups" { + description = "The identity domain groups" + value = oci_identity_domains_dynamic_resource_group.these +} + +output "identity_domain_identity_providers" { + description = "The identity domain groups" + value = oci_identity_domains_identity_provider.these +} + +output "identity_domain_saml_metadata" { + value = { for k,v in data.http.saml_metadata : k=> v.response_body } +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/providers.tf b/modules/oci-cis-lz-iam/identity-domains/providers.tf new file mode 100644 index 0000000..f7585e0 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/providers.tf @@ -0,0 +1,11 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +terraform { + required_version = ">= 1.3.0" + required_providers { + oci = { + source = "oracle/oci" + } + } +} \ No newline at end of file diff --git a/modules/oci-cis-lz-iam/identity-domains/variables.tf b/modules/oci-cis-lz-iam/identity-domains/variables.tf new file mode 100644 index 0000000..4f4d379 --- /dev/null +++ b/modules/oci-cis-lz-iam/identity-domains/variables.tf @@ -0,0 +1,134 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +variable "tenancy_ocid" { + type = string + description = "The OCID of the tenancy." +} + +variable "identity_domains_configuration" { + description = "The identity domains configuration." + type = object({ + default_compartment_id = optional(string) + default_defined_tags = optional(map(string)) + default_freeform_tags = optional(map(string)) + identity_domains = map(object({ + compartment_id = optional(string), + display_name = string, + description = string, + home_region = optional(string), + license_type = string, + admin_email = optional(string), + admin_first_name = optional(string), + admin_last_name = optional(string), + admin_user_name = optional(string), + is_hidden_on_login = optional(bool), + is_notification_bypassed = optional(bool), + is_primary_email_required = optional(bool), + defined_tags = optional(map(string)), + freeform_tags = optional(map(string)) + })) + }) + default = null +} + +variable "identity_domain_groups_configuration" { + description = "The identity domain groups configuration." + type = object({ + default_identity_domain_id = optional(string) + default_defined_tags = optional(map(string)) + default_freeform_tags = optional(map(string)) + groups = map(object({ + identity_domain_id = optional(string), + name = string, + description = optional(string), + requestable = optional(bool), + members = optional(list(string)), + defined_tags = optional(map(string)), + freeform_tags = optional(map(string)) + })) + }) + default = null +} + +variable "identity_domain_dynamic_groups_configuration" { + description = "The identity domain dynamic groups configuration." + type = object({ + default_identity_domain_id = optional(string) + default_defined_tags = optional(map(string)) + default_freeform_tags = optional(map(string)) + dynamic_groups = map(object({ + identity_domain_id = optional(string), + name = string, + description = optional(string), + matching_rule = string, + defined_tags = optional(map(string)), + freeform_tags = optional(map(string)) + })) + }) + default = null +} + +variable "identity_domain_identity_providers_configuration" { + description = "The identity domain identity providers configuration." + type = object({ + default_identity_domain_id = optional(string) + #default_defined_tags = optional(map(string)) + #default_freeform_tags = optional(map(string)) + identity_providers = map(object({ + identity_domain_id = optional(string), + name = string, + description = optional(string), + icon_file = optional(string), + enabled = bool, + name_id_format = optional(string), + user_mapping_method = optional(string), + user_mapping_store_attribute = optional(string), + assertion_attribute = optional(string), + + idp_metadata_file = optional(string), + + idp_issuer_uri = optional(string), + sso_service_url = optional(string), + sso_service_binding = optional(string), + idp_signing_certificate = optional(string), + idp_encryption_certificate = optional(string), + enable_global_logout = optional(bool), + idp_logout_request_url = optional(string), + idp_logout_response_url = optional(string), + idp_logout_binding = optional(string), + + signature_hash_algorithm = optional(string), + send_signing_certificate = optional(bool), + + jit_user_prov_attribute_update_enabled = optional(bool), + jit_user_prov_create_user_enabled = optional(bool), + jit_user_prov_enabled = optional(bool), + jit_user_prov_group_assertion_attribute_enabled = optional(bool), + jit_user_prov_group_assignment_method = optional(string), + jit_user_prov_group_mapping_mode = optional(string), + jit_user_prov_group_saml_attribute_name = optional(string), + jit_user_prov_group_static_list_enabled = optional(bool), + jit_user_prov_ignore_error_on_absent_groups = optional(string), + jit_user_prov_attributes = optional(string), + + #defined_tags = optional(map(string)), + #freeform_tags = optional(map(string)) + })) + }) + default = null +} + +variable module_name { + description = "The module name." + type = string + default = "iam-identity-domains" +} + +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(object({ + id = string + })) + default = null +} diff --git a/modules/oci-cis-lz-iam/release.txt b/modules/oci-cis-lz-iam/release.txt new file mode 100644 index 0000000..0c62199 --- /dev/null +++ b/modules/oci-cis-lz-iam/release.txt @@ -0,0 +1 @@ +0.2.1 diff --git a/modules/oci-identity-policies/README.md b/modules/oci-identity-policies/README.md new file mode 100644 index 0000000..2a6f891 --- /dev/null +++ b/modules/oci-identity-policies/README.md @@ -0,0 +1,100 @@ +# Terraform Template to setup custom federation policies on OCI + +Support module. Creates a set of OCI policies including policy statements. + +## Providers + +| Name | Version | +| --------------------------------------------------------------------- | ------- | +| [OCI](https://registry.terraform.io/providers/oracle/oci/latest/docs) | n/a | + +## Inputs Variables + +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:---------------------------------------|:------------------------------------------------------------------:|:--------:|--------------:|:-----------------------------------------------------| +| `config_file_profile` | OCI CLI profile name | Yes | | "ONBOARDING" | +| `tenancy_id` | Tenancy OCID | Yes | | "ocid1.tenancy.oc1..xxxxxxxxxxxxx" | +| `compartment_ocid` | Compartment OCID | Yes | | "ocid1.tenancy.oc1..xxxxxxxxxxxxx" | +| `region` | OCI region Identifier | Yes | | "us-ashburn-1" | +| `google_project_number` | Google Project ID related to the current OCI configuration | No | | "" | +| `identity_domain_name` | Name of an existing identity domain | No | "Default" | "ExistingDomain" | +| `identity_domain_configuration` | Identity domain definition to isolate the federation configuration | No | | See [domain module](./oci-identity-domain/README.md) | +| `identity_domain_groups_configuration` | Additional groups to create | No | | See [groups module](./oci-identity-group/README.md) | + +### Setting param value + +The following input tfvars _must_ be set + +Either as `terraform.tfvars` file in same directory + +``` +config_file_profile="" +compartment_ocid="" +region="" +``` + +Or running as command line parameter + +``` +terraform apply -var="config_file_profile=ONBOARDING" -var='compartment_ocid=ocid1.tenancy.oc1..xxxxxxxxxxxxx' -var='region=us-ashburn-1' +``` + +### Authentication + +``` +# authenticate OCI cli +oci session authenticate --region= +``` + +### Execution + +The policies created using this script are similar to the ones created by default and can be differentiated because the name of +the policies would include the word "Custom". + +To recreate the default policies on the Default domain you can use the following domain. + +``` +terraform apply +``` + +To create the default policies using an existing identity domain. + +``` +terraform apply -var="identity_domain_name= +``` + +To create a new identity domain and recreate the default groups and policies add a value for the variable `identity_domain_configuration`. + +``` +# on terraform.tvars +# identity_domain_configuration = { +# display_name = "NewDomain" +# description = "New domain to isolate groups and policies for federation" +# license_type = "Free" +# } + +terraform apply +``` + +To recreate the default policies and limit the access to only a single Google Cloud project. + +``` +terraform apply -var="gcp_project_number=" +``` + +New groups can be created by providing a value for `identity_domain_groups_configuration` + +``` +# on terraform.tvars +# identity_domain_groups_configuration = { +# groups: { +# "group_1" : { +# name: "Console Access Group" +# description: "Group with access to OCI Console" +# requestable: true +# } +# } +# } + +terraform apply +``` \ No newline at end of file diff --git a/modules/oci-identity-policies/default_groups.tf b/modules/oci-identity-policies/default_groups.tf new file mode 100644 index 0000000..986a312 --- /dev/null +++ b/modules/oci-identity-policies/default_groups.tf @@ -0,0 +1,65 @@ +locals { + groups = { + "odbg-db-family-administrators" : { + name : "odbg-db-family-administrators" + description : "Oracle.Database owner" + policy : "MulticloudLink_ODBG_Custom_DbFamilyPolicy" + permissions : ["manage database-family"] + } + "odbg-db-family-readers" : { + name : "odbg-db-family-readers" + description : "Oracle.Database reader" + policy : "MulticloudLink_ODBG_Custom_DbFamilyPolicy" + permissions : ["read database-family", "read autonomous-database-family"] + } + "odbg-exa-infra-administrators" : { + name : "odbg-exa-infra-administrators" + description : "Oracle.Database Exadata Infrastructure Administrator" + policy : "MulticloudLink_ODBG_Custom_DbFamilyPolicy" + permissions : ["manage cloud-exadata-infrastructures", "use cloud-vmclusters"] + } + "odbg-vm-cluster-administrators" : { + name : "odbg-vm-cluster-administrators" + description : "Oracle.Database VmCluster Administrator" + policy : "MulticloudLink_ODBG_Custom_DbFamilyPolicy" + permissions : [ + "use cloud-exadata-infrastructures", "manage cloud-vmclusters", "manage db-homes", "manage databases", + "manage db-backups" + ] + } + "odbg-exa-cdb-administrators" : { + name : "odbg-exa-cdb-administrators" + description : "Oracle.Database CDB Administrator" + policy : "MulticloudLink_ODBG_Custom_DbFamilyPolicy" + permissions : ["manage db-homes", "manage databases", "manage db-backups"] + } + "odbg-exa-pdb-administrators" : { + name : "odbg-exa-pdb-administrators" + description : "Oracle.Database PDB Administrator" + policy : "MulticloudLink_ODBG_Custom_DbFamilyPolicy" + permissions : ["manage pluggable-databases"] + } + "odbg-adbs-db-administrators" : { + name : "odbg-adbs-db-administrators" + description : "Oracle.Database Autonomous Administrator" + policy : "MulticloudLink_ODBG_Custom_DbFamilyPolicy" + permissions : ["manage autonomous-databases", "manage autonomous-backups"] + } + "odbg-network-administrators" : { + name : "odbg-network-administrators" + description : "Oracle.Database Network Administrator" + policy : "MulticloudLink_ODBG_Custom_NetworkAdminPolicy" + permissions : ["manage virtual-network-family"] + } + "odbg-costmgmt-administrators" : { + name : "odbg-costmgmt-administrators" + description : "Oracle.Database Cost Management" + policy : "MulticloudLink_ODBG_Custom_CostMgmtPolicy" + permissions : ["manage usage-report"] + } + } + domain_groups = var.identity_domain_configuration == null ? {} : { for key, group in local.groups : key => { + name : group.name + description : group.description + } } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/group_policies.tf b/modules/oci-identity-policies/group_policies.tf new file mode 100644 index 0000000..ee40bac --- /dev/null +++ b/modules/oci-identity-policies/group_policies.tf @@ -0,0 +1,65 @@ +# Copyright (c) 2024 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + oci_console_policy_statements = [ + #"inspect tenancy", + "inspect tag-namespaces", "use tag-namespaces" + ] + + gcp_project_where = var.google_project_number != null ? "target.resource.compartment.tag.orcl-OracleDatabase@GCP.ocid1.tagdefinition.oc1..aaaaaaaaobo5z3qm4xu5p5nueol46zwfnz7et2axi3vgp7tzp77ndjrrqepq = '${var.google_project_number}'" : null + # TODO: The condition for GCP project should be the one below this line, temporarily the previous line works + # gcp_project_where = var.google_project_number != null ? "ALL {target.resource.compartment.tag.orcl-OracleDatabase@GCP.AuthorizationContextId = '${var.google_project_number}' }" : null + + oci_console_policy = { + "MulticloudLink_ODBG_Custom_ConsoleAccess" : { + name : "MulticloudLink_ODBG_Custom_ConsoleAccess" + description: "MulticloudLink_ODBG_Custom_ConsoleAccess" + statements : flatten([ + for group in keys(local.groups) : [ + for statement in local.oci_console_policy_statements : { + group : "%{if var.identity_domain_configuration != null && module.identity_domain.idcs_display_name != "Default"}'${module.identity_domain.idcs_display_name}'/%{endif}${group}" + permission : statement + tenancy_statement : true + where : statement == "use tag-namespaces" ? "ALL{%{if local.gcp_project_where != null}${local.gcp_project_where}, %{endif}target.tag-namespace.name = 'Multicloud'}" : local.gcp_project_where != null ? "ALL {${local.gcp_project_where}}" : null + } + ] + ]) + } + } + + oci_policies = { + for group, configuration in local.groups : configuration.policy => [for permission in configuration.permissions : { + group : group + permission : permission + }]... + } + + oci_group_policies = { + for policy, statements in local.oci_policies : policy => { + name : policy, + description : policy, + statements : [for statement in flatten(statements) : { + group : "%{if var.identity_domain_configuration != null && module.identity_domain.idcs_display_name != "Default"}'${module.identity_domain.idcs_display_name}'/%{endif}${statement.group}" + permission : statement.permission + where : local.gcp_project_where != null ? "ALL {${local.gcp_project_where}}" : null + }] + } + } + + # TODO: Add policy statement to cover DBAASEXACS-13091 issue. + # Allow group odbg-db-family-administrators to manage database-family in compartment id where any {!request.operation, all { request.operation != 'CreateAutonomousContainerDatabase', request.operation != 'CreateAutonomousDatabase', request.operation != 'CreateAutonomousDatabaseBackup', request.operation != 'CreateAutonomousVmCluster', request.operation != 'CreateBackup', request.operation != 'CreateBackupDestination', request.operation != 'CreateCloudAutonomousVmCluster', request.operation != 'CreateCloudExadataInfrastructure', request.operation != 'CreateCloudVmCluster', request.operation != 'CreateDatabaseSoftwareImage', request.operation != 'CreateExadataInfrastructure', request.operation != 'CreateExternalBackupJob', request.operation != 'CreateExternalContainerDatabase', request.operation != 'CreateExternalDatabaseConnector', request.operation != 'CreateExternalPluggableDatabase', request.operation != 'CreateVmCluster', request.operation != 'CreateVmClusterNetwork' } } +} + +module "identity_policies" { + source = "./oci-identity-policy" + + tenancy_ocid = var.tenancy_id + compartment_ocid = var.compartment_ocid + config_file_profile = var.config_file_profile + region = var.region + + identity_domain_policies = { + policies : merge(local.oci_console_policy, local.oci_group_policies) + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/main.tf b/modules/oci-identity-policies/main.tf new file mode 100644 index 0000000..9ece858 --- /dev/null +++ b/modules/oci-identity-policies/main.tf @@ -0,0 +1,26 @@ +module "identity_domain" { + source = "./oci-identity-domain" + + region = var.region + tenancy_id = var.tenancy_id + compartment_ocid = var.compartment_ocid + config_file_profile = var.config_file_profile + + identity_domain_name = var.identity_domain_name + identity_domain_configuration = var.identity_domain_configuration +} + +module "identity_domain_groups" { + source = "./oci-identity-group" + + region = var.region + compartment_ocid = var.compartment_ocid + config_file_profile = var.config_file_profile + + idcs_endpoint = module.identity_domain.idcs_endpoint + identity_domain_groups_configuration = var.identity_domain_configuration != null ? { + default_defined_tags = var.identity_domain_groups_configuration != null ? var.identity_domain_groups_configuration.default_defined_tags : null + default_freeform_tags = var.identity_domain_groups_configuration != null ? var.identity_domain_groups_configuration.default_freeform_tags : null + groups = merge(local.domain_groups, var.identity_domain_groups_configuration != null ? var.identity_domain_groups_configuration.groups != null ? var.identity_domain_groups_configuration.groups : {} : {}) + } : var.identity_domain_groups_configuration +} diff --git a/modules/oci-identity-policies/oci-identity-domain/README.md b/modules/oci-identity-policies/oci-identity-domain/README.md new file mode 100644 index 0000000..159eee7 --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-domain/README.md @@ -0,0 +1,57 @@ +# Terraform Template to setup federation policies on OCI + +Support module. Creates a set of OCI policies including policy statements. + +## Providers + +| Name | Version | +| --------------------------------------------------------------------- | ------- | +| [OCI](https://registry.terraform.io/providers/oracle/oci/latest/docs) | n/a | + +## Inputs Variables + +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:---------------------------------|:----------------------------------------:|:--------:|--------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `config_file_profile` | OCI CLI profile name | Yes | | "ONBOARDING" | +| `compartment_ocid` | Tenancy OCID | Yes | | "ocid1.tenancy.oc1..xxxxxxxxxxxxx" | +| `region` | OCI region Identifier | Yes | | "us-ashburn-1" | +| `identity_domain_name` | Name of an existing identity domain | No | "Default" | | +| `identity_domain_configuration` | Configuration for a new identity domain | No | |
{
name = "domain_name"
description = "domain description"
compartment_id = "ocid1.compartment.oc1..xxxxxxx"
home_region = "us-ashburn-1"
license_type = "Free"
admin_email = ""
admin_first_name = "First Name"
admin_last_name = "Last Name"
admin_user_name = "admin"
is_hidden_on_login = false
is_notification_bypassed = false
is_primary_email_required = true
} | + +## Output Variables + +| VARIABLE | DESCRIPTION | SAMPLE VALUE | +|:--------------------|:--------------------------------------|:----------------------------------------------------| +| `idcs_endpoint` | URL for the identity domain. | https://idcs-xxxxxxxxx.identity.pint.oracle.com:443 | +| `idcs_display_name` | Display name for the identity domain. | Default | + + +### Setting param value + +The following input tfvars _must_ be set + +Either as `terraform.tfvars` file in same directory + +``` +config_file_profile="" +compartment_ocid="" +region="" +``` + +Or running as command line parameter + +``` +terraform apply -var="config_file_profile=ONBOARDING" -var='compartment_ocid=ocid1.tenancy.oc1..xxxxxxxxxxxxx' -var='region=us-ashburn-1' +``` + +### Authentication + +``` +# authenticate OCI cli +oci session authenticate --region= +``` + +### Execution + +Review execution details for the [parent module](../README.md). That module defines the required configuration +for default groups and policies. \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-domain/main.tf b/modules/oci-identity-policies/oci-identity-domain/main.tf new file mode 100644 index 0000000..f5f466e --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-domain/main.tf @@ -0,0 +1,57 @@ +# Copyright (c) 2024 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +data "oci_identity_regions" "these" {} + +data "oci_identity_tenancy" "this" { + tenancy_id = var.tenancy_id +} + +locals { + regions_map = { for r in data.oci_identity_regions.these.regions : r.key => r.name } + home_region_key = data.oci_identity_tenancy.this.home_region_key +} + +resource "oci_identity_domain" "identity_domain" { + count = var.identity_domain_configuration != null ? 1 : 0 + compartment_id = var.compartment_ocid + + display_name = var.identity_domain_configuration.display_name + description = var.identity_domain_configuration.description + home_region = var.identity_domain_configuration.home_region != null ? var.identity_domain_configuration.home_region : local.regions_map[local.home_region_key] + license_type = var.identity_domain_configuration.license_type + + admin_email = var.identity_domain_configuration.admin_email + admin_first_name = var.identity_domain_configuration.admin_first_name + admin_last_name = var.identity_domain_configuration.admin_last_name + admin_user_name = var.identity_domain_configuration.admin_user_name + + is_hidden_on_login = var.identity_domain_configuration.is_hidden_on_login + is_notification_bypassed = var.identity_domain_configuration.is_notification_bypassed + is_primary_email_required = var.identity_domain_configuration.is_primary_email_required + + defined_tags = var.identity_domain_configuration.defined_tags + freeform_tags = var.identity_domain_configuration.freeform_tags +} + +data "oci_identity_domains" "existing_domain" { + count = var.identity_domain_configuration == null ? 1 : 0 + compartment_id = var.compartment_ocid + + display_name = var.identity_domain_name + + lifecycle { + postcondition { + condition = length(self.domains) == 1 + error_message = "Unable to find group with name '${self.display_name}' and no identity_domain_configuration was provided" + } + } +} + +output "idcs_endpoint" { + value = var.identity_domain_configuration != null ? oci_identity_domain.identity_domain[0].url : data.oci_identity_domains.existing_domain[0].domains[0].url +} + +output "idcs_display_name" { + value = var.identity_domain_configuration != null ? oci_identity_domain.identity_domain[0].display_name : data.oci_identity_domains.existing_domain[0].domains[0].display_name +} \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-domain/variables.tf b/modules/oci-identity-policies/oci-identity-domain/variables.tf new file mode 100644 index 0000000..6e1e777 --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-domain/variables.tf @@ -0,0 +1,47 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "config_file_profile" { + description = "The profile name if you would like to use a custom profile in the OCI config file to provide the authentication credentials. See Using the SDK and CLI Configuration File for more information, Refer: https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm#terraformproviderconfiguration_topic-SDK_and_CLI_Config_File" + type = string +} + +variable "region" { + description = "OCI region " + type = string +} + +variable "compartment_ocid" { + description = "for root compartment pass tenancy_ocid , else pass compartment_ocid" + type = string +} + +variable "tenancy_id" { + description = "Tenancy ocid" + type = string +} + +variable "identity_domain_name" { + description = "Name of existing domain where groups would be created. It would be used if identity_domain_configuration is not present" + type = string + default = "Default" +} + +variable "identity_domain_configuration" { + description = "The identity domain configuration." + type = object({ + compartment_id = optional(string), + display_name = string, + description = string, + home_region = optional(string), + license_type = string, + admin_email = optional(string), + admin_first_name = optional(string), + admin_last_name = optional(string), + admin_user_name = optional(string), + is_hidden_on_login = optional(bool), + is_notification_bypassed = optional(bool), + is_primary_email_required = optional(bool), + defined_tags = optional(map(string)), + freeform_tags = optional(map(string)) + }) + default = null +} \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-group/README.md b/modules/oci-identity-policies/oci-identity-group/README.md new file mode 100644 index 0000000..6d7a588 --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-group/README.md @@ -0,0 +1,49 @@ +# Terraform Template to setup federation groups on OCI + +Support module. Creates a set of OCI groups for user federation. + +## Providers + +| Name | Version | +| --------------------------------------------------------------------- | ------- | +| [OCI](https://registry.terraform.io/providers/oracle/oci/latest/docs) | n/a | + +## Inputs Variables + +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:---------------------------------------|:-------------------------------------------:|:--------:|--------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `config_file_profile` | OCI CLI profile name | Yes | | "ONBOARDING" | +| `compartment_ocid` | Tenancy OCID | Yes | | "ocid1.tenancy.oc1..xxxxxxxxxxxxx" | +| `region` | OCI region Identifier | Yes | | "us-ashburn-1" | +| `idcs_enpoint` | Identity provider endpoint | Yes | | https://idcs-xxxxxxxxx.identity.pint.oracle.com:443 | +| `identity_domain_groups_configuration` | Configuration for new groups to be created. | Yes | |
{
default_defined_tags = var.default_defined_tags
default_freeform_tags = var.default_freeform_tags
groups = {
name = "group_name"
description = "group description"
requestable = true
members = ["user_1", "user_2"]
freeform_tags = var.freeform_tags
}
} | + +### Setting param value + +The following input tfvars _must_ be set + +Either as `terraform.tfvars` file in same directory + +``` +config_file_profile="" +compartment_ocid="" +region="" +``` + +Or running as command line parameter + +``` +terraform apply -var="config_file_profile=ONBOARDING" -var='compartment_ocid=ocid1.tenancy.oc1..xxxxxxxxxxxxx' -var='region=us-ashburn-1' +``` + +### Authentication + +``` +# authenticate OCI cli +oci session authenticate --region= +``` + +### Execution + +Review execution details for the [parent module](../README.md). That module defines the required configuration +for default groups and policies. \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-group/main.tf b/modules/oci-identity-policies/oci-identity-group/main.tf new file mode 100644 index 0000000..29400ff --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-group/main.tf @@ -0,0 +1,62 @@ +# Copyright (c) 2024 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +data "oci_identity_domains_users" "these" { + for_each = var.identity_domain_groups_configuration != null ? (var.identity_domain_groups_configuration.groups != null ? var.identity_domain_groups_configuration.groups : {}) : {} + idcs_endpoint = var.idcs_endpoint +} + +locals { + users = { for k, g in(var.identity_domain_groups_configuration != null ? var.identity_domain_groups_configuration["groups"] : {}) : k => + { for u in data.oci_identity_domains_users.these[k].users : u.user_name => u.id } } +} + + + +resource "oci_identity_domains_group" "these" { + for_each = var.identity_domain_groups_configuration != null ? var.identity_domain_groups_configuration.groups : {} + + idcs_endpoint = var.idcs_endpoint + + display_name = each.value.name + schemas = ["urn:ietf:params:scim:schemas:core:2.0:Group", "urn:ietf:params:scim:schemas:oracle:idcs:extension:group:Group", "urn:ietf:params:scim:schemas:extension:custom:2.0:Group"] + + urnietfparamsscimschemasoracleidcsextensiongroup_group { + creation_mechanism = "api" + description = each.value.description + } + + urnietfparamsscimschemasoracleidcsextensionrequestable_group { + requestable = each.value.requestable + } + + dynamic "members" { + for_each = each.value.members != null ? each.value.members : [] + content { + type = "User" + value = local.users[each.key][members["value"]] + + } + } + urnietfparamsscimschemasoracleidcsextension_oci_tags { + + dynamic "defined_tags" { + for_each = each.value.defined_tags != null ? each.value.defined_tags : (var.identity_domain_groups_configuration.default_defined_tags != null ? var.identity_domain_groups_configuration.default_defined_tags : {}) + content { + key = split(".", defined_tags["key"])[1] + namespace = split(".", defined_tags["key"])[0] + value = defined_tags["value"] + } + } + + + dynamic "freeform_tags" { + for_each = each.value.freeform_tags != null ? each.value.freeform_tags : (var.identity_domain_groups_configuration.default_freeform_tags != null ? var.identity_domain_groups_configuration.default_freeform_tags : {}) + content { + key = freeform_tags["key"] + value = freeform_tags["value"] + } + } + + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-group/variables.tf b/modules/oci-identity-policies/oci-identity-group/variables.tf new file mode 100644 index 0000000..444e406 --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-group/variables.tf @@ -0,0 +1,37 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "config_file_profile" { + description = "The profile name if you would like to use a custom profile in the OCI config file to provide the authentication credentials. See Using the SDK and CLI Configuration File for more information, Refer: https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm#terraformproviderconfiguration_topic-SDK_and_CLI_Config_File" + type = string +} + +variable "region" { + description = "OCI region " + type = string +} + +variable "compartment_ocid" { + description = "for root compartment pass tenancy_ocid , else pass compartment_ocid" + type = string +} + +variable "idcs_endpoint" { + description = "Identity domain group endpoint" + type = string +} + +variable "identity_domain_groups_configuration" { + description = "The identity domain groups configuration." + type = object({ + default_defined_tags = optional(map(string)) + default_freeform_tags = optional(map(string)) + groups = map(object({ + name = string, + description = optional(string), + requestable = optional(bool), + members = optional(list(string)), + defined_tags = optional(map(string)), + freeform_tags = optional(map(string)) + })) + }) + default = null +} \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-policy/README.md b/modules/oci-identity-policies/oci-identity-policy/README.md new file mode 100644 index 0000000..7224440 --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-policy/README.md @@ -0,0 +1,49 @@ +# Terraform Template to setup federation policies on OCI + +Support module. Creates a set of OCI policies including policy statements. + +## Providers + +| Name | Version | +| --------------------------------------------------------------------- | ------- | +| [OCI](https://registry.terraform.io/providers/oracle/oci/latest/docs) | n/a | + +## Inputs Variables + +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:---------------------------|:-------------------------:|:--------:|--------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `config_file_profile` | OCI CLI profile name | Yes | | "ONBOARDING" | +| `tenancy_ocid` | Tenancy OCID | Yes | | "ocid1.tenancy.oc1..xxxxxxxxxxxxx" | +| `compartment_ocid` | Compartment OCID | Yes | | "ocid1.tenancy.oc1..xxxxxxxxxxxxx" | +| `region` | OCI region Identifier | Yes | | "us-ashburn-1" | +| `identity_domain_policies` | Map of policies to create | Yes | |
{
policies = {
name = "Sample policy"
description = "Sample policy description"
statements = [
{
group = "group_name"
permission = "manage network"
tenancy_statement = false
where = "!request.operation"
}]
} | + +### Setting param value + +The following input tfvars _must_ be set + +Either as `terraform.tfvars` file in same directory + +``` +config_file_profile="" +compartment_ocid="" +region="" +``` + +Or running as command line parameter + +``` +terraform apply -var="config_file_profile=ONBOARDING" -var='compartment_ocid=ocid1.tenancy.oc1..xxxxxxxxxxxxx' -var='region=us-ashburn-1' +``` + +### Authentication + +``` +# authenticate OCI cli +oci session authenticate --region= +``` + +### Execution + +Review execution details for [oci-identity-policies](../README.md). That module defines the required configuration +for default groups and policies. \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-policy/main.tf b/modules/oci-identity-policies/oci-identity-policy/main.tf new file mode 100644 index 0000000..69b7ce3 --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-policy/main.tf @@ -0,0 +1,23 @@ +locals { + policy_configuration = { + supplied_policies = { + for name, config in var.identity_domain_policies.policies : name => { + name : config.name + description : config.description + compartment_id : var.compartment_ocid + statements : [ + for policy in config.statements : + "ALLOW group ${policy.group} TO ${policy.permission} IN %{if coalesce(policy.tenancy_statement, false)}tenancy%{else}compartment id ${var.compartment_ocid}%{endif}%{if policy.where != null} WHERE ${policy.where}%{endif}" + ] + } + } + } +} + +module "identity_policies" { + # Reference to cis landing zone can use github.com instead of local source, the referenced version needs to support terraform newer than 1.3 + source = "../policies" + + tenancy_ocid = var.tenancy_ocid + policies_configuration = local.policy_configuration +} \ No newline at end of file diff --git a/modules/oci-identity-policies/oci-identity-policy/variables.tf b/modules/oci-identity-policies/oci-identity-policy/variables.tf new file mode 100644 index 0000000..688464d --- /dev/null +++ b/modules/oci-identity-policies/oci-identity-policy/variables.tf @@ -0,0 +1,37 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "config_file_profile" { + description = "The profile name if you would like to use a custom profile in the OCI config file to provide the authentication credentials. See Using the SDK and CLI Configuration File for more information, Refer: https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm#terraformproviderconfiguration_topic-SDK_and_CLI_Config_File" + type = string +} + +variable "region" { + description = "OCI region " + type = string +} + +variable "tenancy_ocid" { + description = "The tenancy OCID." + type = string +} + +variable "compartment_ocid" { + description = "for root compartment pass tenancy_ocid , else pass compartment_ocid" + type = string +} + +variable "identity_domain_policies" { + description = "Identity Policies for the user groups." + type = object({ + policies = map(object({ + name = string + description = optional(string) + statements = list(object({ + group = string + permission = string + + tenancy_statement = optional(bool) + where = optional(string) + })) + })) + }) +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/README.md b/modules/oci-identity-policies/policies/README.md new file mode 100644 index 0000000..61185da --- /dev/null +++ b/modules/oci-identity-policies/policies/README.md @@ -0,0 +1,382 @@ +# OCI Landing Zones IAM Policy Module + +![Landing Zone logo](../landing_zone_300.png) + + This module manages Oracle Cloud Infrastructure (OCI) Identity and Access Management (IAM) policies. It supports explicitly provided policies as well as a pre-configured implementation of Separation of Duties (SoD) through template policies. With template policies, the Least Privilege security principle is enforced, as recommended by CIS (Center for Internet Security) OCI Foundations Benchmark. + + The module operates over a single *policies_configuration* input variable. + + Check [module specification](./SPEC.md) for a full description of module requirements, supported variables, managed resources and outputs. + + Check the [examples](./examples/) folder for actual module usage. + +- [Requirements](#requirements) +- [How to Invoke the Module](#invoke) +- [Module Functioning](#functioning) +- [Related Documentation](#related) +- [Known Issues](#issues) + +## Requirements +### IAM Permissions + +This module requires the following OCI IAM permissions in the top-most compartment that policies get attached to. +``` +Allow group to manage policies in compartment +``` +If the top-most compartment is the root compartment, the permission becomes: +``` +Allow group to manage policies in tenancy +``` + +### Terraform Version < 1.3.x and Optional Object Type Attributes +This module relies on [Terraform Optional Object Type Attributes feature](https://developer.hashicorp.com/terraform/language/expressions/type-constraints#optional-object-type-attributes), which is experimental from Terraform 0.14.x to 1.2.x. It shortens the amount of input values in complex object types, by having Terraform automatically inserting a default value for any missing optional attributes. The feature has been promoted and it is no longer experimental in Terraform 1.3.x. + +**As is, this module can only be used with Terraform versions up to 1.2.x**, because it can be consumed by other modules via [OCI Resource Manager service](https://docs.oracle.com/en-us/iaas/Content/ResourceManager/home.htm), that still does not support Terraform 1.3.x. + +Upon running *terraform plan* with Terraform versions prior to 1.3.x, Terraform displays the following warning: +``` +Warning: Experimental feature "module_variable_optional_attrs" is active +``` + +Note the warning is harmless. The code has been tested with Terraform 1.3.x and the implementation is fully compatible. + +If you really want to use Terraform 1.3.x, in [providers.tf](./providers.tf): +1. Change the terraform version requirement to: +``` +required_version = ">= 1.3.0" +``` +2. Remove the line: +``` +experiments = [module_variable_optional_attrs] +``` +## How to Invoke the Module + +Terraform modules can be invoked locally or remotely. + +For invoking the module locally, just set the module *source* attribute to the module file path (relative path works). The following example assumes the module is two folders up in the file system. +``` +module "policies" { + source = "../.." + tenancy_ocid = var.tenancy_ocid + policies_configuration = var.policies_configuration + +} +``` + +For invoking the module remotely, set the module *source* attribute to the policies module folder in this repository, as shown: +``` +module "policies" { + source = "github.com/oracle-quickstart/terraform-oci-cis-landing-zone-iam/policies" + tenancy_ocid = var.tenancy_ocid + policies_configuration = var.policies_configuration +} +``` +For referring to a specific module version, append *ref=\* to the *source* attribute value, as in: +``` + source = "github.com/oracle-quickstart/terraform-oci-cis-landing-zone-iam//policies?ref=v0.1.0" +``` +## Module Functioning + + The module operates in two non-exclusive modes: + - [**Supplied policies**](#supplied-policies): a map of policies is explicitly supplied to the module. + - [**Template policies**](#template-policies): At the tenancy level (Root compartment), for group principals, policies are driven off supplied roles assigned to groups that must be passed to the module. For OCI service principals, policies can be enabled for all services or to specific ones. At the compartment level (excluding the Root compartment), policies are driven off metadata that must passed to the module. + + Regardless the mode, the module checks policy statements against CIS OCI Foundations Benchmark recommendations. For example, the module would not allow a statement like *allow group to manage all-resources in tenancy*. + +### 1) Supplied Policies + +In this mode, policies are provided as an input parameter and can supplement or completely override the template policies. + +Supplied policies are defined using the *supplied_policies* attribute. + +Check the [Supplied Policies example](./examples/supplied-policies/) for how to supply your own policies. + + ### 2) Template Policies + + In this mode, policies are pre-configured, automatically providing Separation of Duties across a set of pre-defined group roles. Template policies are specified with *template_policies* attribute. The module distinguishes between tenancy level and compartment level policies. Tenancy level policies are specified via *tenancy_level_settings* attribute, while compartment level policies are specified via *compartment_level_settings*. + + #### 2.1) Tenancy Level Policies + +Tenancy level policies (policies attached to Root compartment) for groups principals are generated based on *groups_with_tenancy_level_roles* attribute within *tenancy_level_settings* attribute. + +Supported tenancy level roles: + +- **iam**: grants IAM related admin permissions at the tenancy level. +- **cost**: grants cost related admin permissions at the tenancy level. +- **cred**: grants credential related admin permissions at the tenancy level. +- **security**: grants security related admin permissions at the tenancy level. +- **application**: grants (read-only) application related permissions at the tenancy level. +- **auditor**: grants (read-only) auditing permissions at the tenancy level. +- **announcement-reader**: grants announcement reading permissions at the tenancy level. +- **basic**: grants simple basic permissions at the tenancy level, specifically the ability to use cloud-shell and read usage-budgets. + +As OCI supports 50 statements per policy, tenancy level grants are split into two policies: one for all groups with manage/use permissions over at least one resource and one for groups with only read/inspect and basic role permissions. + +Tenancy level policies (policies attached to Root compartment) for OCI service principals are generated based on *oci_services* attribute within *tenancy_level_settings* attribute. Policies can be enabled for all services at once or on a per service basis. The following attributes of *oci_services* control the behavior: + +- **enable_all_policies**: when set to true, policies are enabled for all services. Default is false. +- **enable_scanning_policies**: when set to true, policies are enabled for VSS service. Default is false. +- **enable_cloud_guard_policies**: when set to true, policies are enabled for Cloud Guard service. +- **enable_os_management_policies**: when set to true, policies are enabled for OS Management service. Default is false. +- **enable_block_storage_policies**: when set to true, policies are enabled for Block Storage service. Default is false. +- **enable_file_storage_policies**: when set to true, policies are enabled for File Storage service. Default is false. +- **enable_oke_policies**: when set to true, policies are enabled for OKE service. Default is false. +- **enable_streaming_policies**: when set to true, policies are enabled for Streaming service. Default is false. +- **enable_object_storage_policies**: when set to true, policies are enabled for Object Storage service in all tenancy subscribed regions. Default is false. + +##### Policy Naming + +At the tenancy level, two policies are generated, named as: +- *policy_name_prefix*root-admin*policy_name_suffix*: for all groups with manage/use permissions over at least one resource +- *policy_name_prefix*root-non-admin*policy_name_suffix*: for groups with only read/inspect and basic role permissions. + +You can tweak policy names by changing *policy_name_prefix* and *policy_name_suffix* atributes. Default values are: +- *policy_name_prefix* : "" +- *policy_name_suffix* : "-policy" + +##### Policy Attachment + +Tenancy level policies are always attached to the Root compartment. + +##### Putting it Together - An Example + +As an example, let's assume the following *groups_with_tenancy_level_roles* attribute is passed to the module: + +``` +groups_with_tenancy_level_roles = [ + {"name"="vision-iam-admin-group", "roles"="iam"}, + {"name"="vision-cred-admin-group", "roles"="cred"}, + {"name"="vision-cost-admin-group", "roles"="cost"}, + {"name"="vision-security-admin-group","roles"="security"}, + {"name"="vision-app-admin-group", "roles"="application"}, + {"name"="vision-auditor-group", "roles"="auditor"}, + {"name"="vision-announcement_reader-group","roles"="announcement-reader"}, + {"name"="vision-database-admin-group","roles"="basic"}, + {"name"="vision-exainfra-admin-group","roles"="basic"}, + {"name"="vision-storage-admin-group", "roles"="basic"} + ] +``` +The following two policies would be attached to the Root compartment. + +Policy with *manage* or *use* permissions: + +``` +define tenancy usage-report as ocid1.tenancy.oc1..aaaaaaaaned4fkpkisbwjlr56u7cj63lf3wffbilvqknstgtvzub7vhqkggq +endorse group vision-cost-admin-group to read objects in tenancy usage-report +allow group vision-cost-admin-group to manage usage-report in tenancy +allow group vision-cost-admin-group to manage usage-budgets in tenancy +allow group vision-iam-admin-group to inspect users in tenancy +allow group vision-iam-admin-group to inspect groups in tenancy +allow group vision-iam-admin-group to read policies in tenancy +allow group vision-iam-admin-group to manage groups in tenancy where all {target.group.name != 'Administrators', target.group.name != 'vision-cred-admin-group'} +allow group vision-iam-admin-group to inspect identity-providers in tenancy +allow group vision-iam-admin-group to manage identity-providers in tenancy where any {request.operation = 'AddIdpGroupMapping', request.operation = 'DeleteIdpGroupMapping'} +allow group vision-iam-admin-group to manage dynamic-groups in tenancy +allow group vision-iam-admin-group to manage authentication-policies in tenancy +allow group vision-iam-admin-group to manage network-sources in tenancy +allow group vision-iam-admin-group to manage quota in tenancy +allow group vision-iam-admin-group to read audit-events in tenancy +allow group vision-iam-admin-group to manage tag-defaults in tenancy +allow group vision-iam-admin-group to manage tag-namespaces in tenancy +allow group vision-iam-admin-group to manage orm-stacks in tenancy +allow group vision-iam-admin-group to manage orm-jobs in tenancy +allow group vision-iam-admin-group to manage orm-config-source-providers in tenancy +allow group vision-cred-admin-group to inspect users in tenancy +allow group vision-cred-admin-group to inspect groups in tenancy +allow group vision-cred-admin-group to manage users in tenancy where any {request.operation = 'ListApiKeys',request.operation = 'ListAuthTokens',request.operation = 'ListCustomerSecretKeys',request.operation = 'UploadApiKey',request.operation = 'DeleteApiKey',request.operation = 'UpdateAuthToken',request.operation = 'CreateAuthToken',request.operation = 'DeleteAuthToken',request.operation = 'CreateSecretKey',request.operation = 'UpdateCustomerSecretKey',request.operation = 'DeleteCustomerSecretKey',request.operation = 'UpdateUserCapabilities'} +allow group vision-security-admin-group to manage cloudevents-rules in tenancy +allow group vision-security-admin-group to manage cloud-guard-family in tenancy +allow group vision-security-admin-group to read tenancies in tenancy +allow group vision-security-admin-group to read objectstorage-namespaces in tenancy +``` + +Policy with *read*, *inspect*, and *basic* role permissions: + +``` +allow group vision-announcement_reader-group,vision-app-admin-group,vision-auditor-group,vision-cost-admin-group,vision-cred-admin-group,vision-database-admin-group,vision-exainfra-admin-group,vision-iam-admin-group,vision-security-admin-group,vision-storage-admin-group to use cloud-shell in tenancy +allow group vision-announcement_reader-group,vision-app-admin-group,vision-auditor-group,vision-cost-admin-group,vision-cred-admin-group,vision-database-admin-group,vision-exainfra-admin-group,vision-iam-admin-group,vision-security-admin-group,vision-storage-admin-group to read usage-budgets in tenancy +allow group vision-auditor-group to inspect all-resources in tenancy +allow group vision-auditor-group to read instances in tenancy +allow group vision-auditor-group to read load-balancers in tenancy +allow group vision-auditor-group to read buckets in tenancy +allow group vision-auditor-group to read nat-gateways in tenancy +allow group vision-auditor-group to read public-ips in tenancy +allow group vision-auditor-group to read file-family in tenancy +allow group vision-auditor-group to read instance-configurations in tenancy +allow group vision-auditor-group to read network-security-groups in tenancy +allow group vision-auditor-group to read resource-availability in tenancy +allow group vision-auditor-group to read audit-events in tenancy +allow group vision-auditor-group to read users in tenancy +allow group vision-auditor-group to read vss-family in tenancy +allow group vision-auditor-group to read data-safe-family in tenancy +allow group vision-announcement_reader-group to read announcements in tenancy +``` + +A fully functional example is provided in [Template policies example](./examples/template-policies/). + + #### 2.2) Compartment Level Policies + +At the compartment level, compartment metadata attributes drive policy creation. These metadata define the compartment types and consumer groups. + + The compartments to which policies are applied must be provided to the module using *supplied_compartments* attribute within *compartment_level_settings* attribute. *supplied_compartments* is a map of objects, where each object is composed of the following attributes: + - **name** : the compartment name, determining the target compartment in each policy statement. + - **id** : the compartment where the policy is attached to. This attribute is overloaded, i.e., it can be either a literal compartment OCID, or a reference (a key) to an OCID. See [External Dependencies](#ext-dep) for more information. + - **cislz_metadata** : the metadata attributes, determining the policy statements for each compartment policy. + +The module supports the following metadata attributes (*cislz_metadata*): + +- cislz-cmp-type +- cislz-consumer-groups-read +- cislz-consumer-groups-iam +- cislz-consumer-groups-security +- cislz-consumer-groups-network +- cislz-consumer-groups-application +- cislz-consumer-groups-database +- cislz-consumer-groups-exainfra +- cislz-consumer-groups-storage +- cislz-consumer-groups-dyn-database-kms +- cislz-consumer-groups-dyn-compute-agent + +##### cislz-cmp-type Attribute + +Defines the compartment's intent, in other words, it communicates the OCI resource types the compartment is intended to hold. For instance, a compartment can be created with the objective of holding network resources, or security resources, or both. + +Currently supported values: + +- **security**: compartment´s intent to hold *security* related resources, hence this value drives the creation of *security* related policies. +- **network**: compartment´s intent to hold *network* related resources, hence this value drives the creation of *network* related policies. +- **application**: compartment´s intent to hold *application* related resources, hence this value drives the creation of *application* related policies. +- **database**: compartment´s intent to hold *database* related resources, hence this value drives the creation of *database* related policies. +- **exainfra**: compartment´s intent to hold *Exatada Cloud Service Infrastructure* related resources, hence this value drives the creation of *Exatada Cloud Service infrastructure* policies. +- **enclosing**: drives the creation of policies that are scoped to more than one compartment, as a compartment tagged as *enclosing* is intended to be the parent of above compartment types. + +**Note**: Multiple values can be assigned to *cislz-cmp-type*, as a comma separated string of values. + +##### cislz-consumer-groups-\ Attribute + +The *cislz-consumer-groups-\* metadata attributes define the groups that use (or consume) resources from the compartment, denoting the groups' interest in the resources. For instance, a tag *cislz-consumer-groups-database: database-admin-group* indicates *database-admin-group* consumes resources in the compartment from a *database* admin perspective. This results in policy statements granting specific *database* related permissions to the group in the compartment. + +**Note**: The *cislz-consumer-groups-\* metadata attributes support a comma-separated string of values, which allows for communicating the interest of multiple groups in a single compartment. This is important in scenarios where a compartment provides shared services, must be managed by different administrators and is used (consumed) by roles with other responsibilities. + +Picture the use case where a single compartment provide network and security services. It might be the case it requires distinct administrators for network and security resources. Moreover, distinct database administrators (Dev admin and Prod admin) may need access to those resources. In this scenario, the compartment metadata would be like: +``` +cislz-cmp-type: "network,security" +cislz-consumer-groups-network: "network-admin" +cislz-consumer-groups-security: "security-admin" +cislz-consumer-groups-database: "dev-database-admin", "prod-database-admin" +``` + +These are the currently supported metadata attributes for consumer groups: + +- **cislz-consumer-groups-read**: defines the groups that can only read from the compartment. +- **cislz-consumer-groups-iam**: defines the groups that manage or consume compartment resources from an *IAM* admin perspective. +- **cislz-consumer-groups-security**: defines the groups that manage or consume compartment resources from a *security* admin perspective. +- **cislz-consumer-groups-network**: defines the groups that manage or consume compartment resources from a *network* admin perspective. +- **cislz-consumer-groups-application**: defines the groups that manage or consume compartment resources from an *application* admin perspective. +- **cislz-consumer-groups-database**: defines the groups that manage or consume compartment resources from a *database* admin perspective. +- **cislz-consumer-groups-exainfra**: defines the groups that manage or consume compartment resources from an *Exadata Cloud Service infrastructure* admin perspective. +- **cislz-consumer-groups-storage**: defines the groups that manage or consume compartment resources from a *storage* admin perspective. +- **cislz-consumer-groups-dyn-database-kms**: defines the dynamic groups that consume key management services (KMS) from the perspective of a database system. This typically applies to compartments tagged with *cislz-cmp-type:security*, as security compartments are an adequate container for KMS. +- **cislz-consumer-groups-dyn-compute-agent**: defines the dynamic groups allowed to execute Compute agent plugin in the compartment. + +##### Policy Generation Logic + +The actual applied policies are the combined result of *cislz-cmp-type* and *cislz-consumer-groups-\* values. *cislz-cmp-type* defines the greater set of OCI resource types available for the compartment, while *cislz-consumer-groups-\* defines the grantees and their access levels (*manage,use,read,inspect*) on a subset of those resource types. Whenever *cislz-cmp-type* matches *\*, manage permissions in compartment resources are granted to *cislz-consumer-groups-\*. + +**IMPORTANT**: There is an implied relationship between *cislz-cmp-type* and *\* in *cislz-consumer-groups-\* as far as *manage* permissions are concerned, according to table below: + +**cislz-cmp-type** | **cislz-consumer-groups' \** | **Policy Outcome** +--------------|-------------|---------- +*security* | *security* | *manage* permissions are granted over *security* related resources to groups names assigned to *cislz-consumer-groups-security*. +*network* | *network* | *manage* permissions are granted over *network* related resources to groups names assigned to *cislz-consumer-groups-network*. +*application* | *application* | *manage* permissions are granted over *application* related resources to groups names assigned to *cislz-consumer-groups-application*. +*database* | *database* | *manage* permissions are granted over *database* related resources to groups names assigned to *cislz-consumer-groups-database*. +*exainfra* | *exainfra* | *manage* permissions are granted over *Exadata Cloud Service Infrastructure* related resources to groups names assigned to *cislz-consumer-groups-exainfra*. + +##### Policy Naming + +At the compartments level, policies are generated for each selected compartment. As such they are named after compartment names, following the pattern *policy_name_prefix*\*policy_name_suffix*. + +You can tweak policy names by changing *policy_name_prefix* and *policy_name_suffix* atributes. Default values are: +- *policy_name_prefix* : "" +- *policy_name_suffix* : "-policy" + +##### Policy Attachment + +The *id* attribute within *supplied_compartments* attribute defines the compartment to which the policy is attached. + +##### Putting it Together - An Example + +As an example, assume the following metadata is associated with the *vision-network-cmp* compartment: + +- *cislz-cmp-type: "network"*: the value communicates this compartment is about *network* resource types, hence network policy statements should be pulled from the template. +- *cislz-consumer-groups-network: "vision-network-admin-group"*: the value communicates the group name that effectively manages compartment resources, because the tag name *\* matches *cislz-cmp-type* value, per table above. +- *cislz-consumer-groups-security: "vision-security-admin-group"*: the value communicates the group name that consumes compartment resources from a *security* admin perspective. +- *cislz-consumer-groups-application: "vision-app-admin-group"*: the value communicates the group name that consumes compartment resources from an *application* admin perspective. +- *cislz-consumer-groups-database: "vision-database-admin-group"*: the value communicates the group name that consumes compartment resources from a *database* admin perspective. +- *cislz-consumer-groups-storage: "vision-storage-admin-group"*: the value communicates the group name that consumes compartment resources from a *storage* admin perspective. +- *cislz-consumer-groups-exainfra: "vision-exainfra-admin-group"*: the value communicates the group name that consumes compartment resources from an *exainfra* (Exadata Cloud Service Infrastructure) admin perspective. + +Per defined metadata, the module attaches the following policy to *vision-network-cmp* compartment: + +``` +allow group vision-network-admin-group to read all-resources in compartment vision-network-cmp +allow group vision-network-admin-group to manage virtual-network-family in compartment vision-network-cmp +allow group vision-network-admin-group to manage dns in compartment vision-network-cmp +allow group vision-network-admin-group to manage load-balancers in compartment vision-network-cmp +allow group vision-network-admin-group to manage alarms in compartment vision-network-cmp +allow group vision-network-admin-group to manage metrics in compartment vision-network-cmp +allow group vision-network-admin-group to manage ons-family in compartment vision-network-cmp +allow group vision-network-admin-group to manage orm-stacks in compartment vision-network-cmp +allow group vision-network-admin-group to manage orm-jobs in compartment vision-network-cmp +allow group vision-network-admin-group to manage orm-config-source-providers in compartment vision-network-cmp +allow group vision-network-admin-group to read audit-events in compartment vision-network-cmp +allow group vision-network-admin-group to read work-requests in compartment vision-network-cmp +allow group vision-network-admin-group to manage instance-family in compartment vision-network-cmp +allow group vision-network-admin-group to manage volume-family in compartment vision-network-cmp where all{request.permission != 'VOLUME_BACKUP_DELETE', request.permission != 'VOLUME_DELETE', request.permission != 'BOOT_VOLUME_BACKUP_DELETE'} +allow group vision-network-admin-group to manage object-family in compartment vision-network-cmp where all{request.permission != 'OBJECT_DELETE', request.permission != 'BUCKET_DELETE'} +allow group vision-network-admin-group to manage file-family in compartment vision-network-cmp where all{request.permission != 'FILE_SYSTEM_DELETE', request.permission != 'MOUNT_TARGET_DELETE', request.permission != 'EXPORT_SET_DELETE', request.permission != 'FILE_SYSTEM_DELETE_SNAPSHOT', request.permission != 'FILE_SYSTEM_NFSv3_UNEXPORT'} +allow group vision-network-admin-group to manage bastion-session in compartment vision-network-cmp +allow group vision-network-admin-group to manage cloudevents-rules in compartment vision-network-cmp +allow group vision-network-admin-group to manage alarms in compartment vision-network-cmp +allow group vision-network-admin-group to manage metrics in compartment vision-network-cmp +allow group vision-network-admin-group to read instance-agent-plugins in compartment vision-network-cmp +allow group vision-security-admin-group to read virtual-network-family in compartment vision-network-cmp +allow group vision-security-admin-group to use subnets in compartment vision-network-cmp +allow group vision-security-admin-group to use network-security-groups in compartment vision-network-cmp +allow group vision-security-admin-group to use vnics in compartment vision-network-cmp +allow group vision-app-admin-group to read virtual-network-family in compartment vision-network-cmp +allow group vision-app-admin-group to use subnets in compartment vision-network-cmp +allow group vision-app-admin-group to use network-security-groups in compartment vision-network-cmp +allow group vision-app-admin-group to use vnics in compartment vision-network-cmp +allow group vision-app-admin-group to use load-balancers in compartment vision-network-cmp +allow group vision-database-admin-group to read virtual-network-family in compartment vision-network-cmp +allow group vision-database-admin-group to use vnics in compartment vision-network-cmp +allow group vision-database-admin-group to use subnets in compartment vision-network-cmp +allow group vision-database-admin-group to use network-security-groups in compartment vision-network-cmp +allow group vision-exainfra-admin-group to read virtual-network-family in compartment vision-network-cmp +allow group vision-storage-admin-group to read bucket in compartment vision-network-cmp +allow group vision-storage-admin-group to inspect object in compartment vision-network-cmp +allow group vision-storage-admin-group to manage object-family in compartment vision-network-cmp where any {request.permission = 'OBJECT_DELETE', request.permission = 'BUCKET_DELETE'} +allow group vision-storage-admin-group to read volume-family in compartment vision-network-cmp +allow group vision-storage-admin-group to manage volume-family in compartment vision-network-cmp where any {request.permission = 'VOLUME_DELETE', request.permission = 'VOLUME_BACKUP_DELETE', request.permission = 'BOOT_VOLUME_BACKUP_DELETE'} +allow group vision-storage-admin-group to read file-family in compartment vision-network-cmp +allow group vision-storage-admin-group to manage file-family in compartment vision-network-cmp where any {request.permission = 'FILE_SYSTEM_DELETE', request.permission = 'MOUNT_TARGET_DELETE', request.permission = 'VNIC_DELETE', request.permission = 'SUBNET_DETACH', request.permission = 'VNIC_DETACH', request.permission = 'PRIVATE_IP_DELETE', request.permission = 'PRIVATE_IP_UNASSIGN', request.permission = 'VNIC_UNASSIGN', request.permission = 'EXPORT_SET_UPDATE', request.permission = 'FILE_SYSTEM_NFSv3_UNEXPORT', request.permission = 'EXPORT_SET_DELETE', request.permission = 'FILE_SYSTEM_DELETE_SNAPSHOT'} +``` + +A fully functional example is provided in [Template policies example](./examples/template-policies/). + +### External Dependencies + +An optional feature, external dependencies are resources managed elsewhere that resources managed by this module may depend on. The following dependencies are supported: + +- **compartments_dependency**: 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 with the compartment OCID. This mechanism allows for the usage of referring keys (instead of OCIDs) when referring to compartments (within *supplied_policies* and *supplied_compartments* attributes). The module replaces the keys by the OCIDs provided within *compartments_dependency* map. Contents of *compartments_dependency* is typically the output of a [Compartments module](../compartments/) client. + +## Related Documentation +- [Getting Started with OCI Policies](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/policygetstarted.htm) +- [How Policies Work](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/policies.htm) +- [Advanced Policy Features](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/policyadvancedfeatures.htm) +- [Policies in Terraform OCI Provider](https://registry.terraform.io/providers/oracle/oci/4.112.0/docs/resources/identity_policy) + +## Known Issues +None. \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/SPEC.md b/modules/oci-identity-policies/policies/SPEC.md new file mode 100644 index 0000000..aeea236 --- /dev/null +++ b/modules/oci-identity-policies/policies/SPEC.md @@ -0,0 +1,41 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | < 1.3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [oci](#provider\_oci) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [oci_identity_policy.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_policy) | resource | +| [oci_identity_region_subscriptions.these](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_region_subscriptions) | data source | +| [oci_identity_tenancy.this](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_tenancy) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [compartments\_dependency](#input\_compartments\_dependency) | 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. |
map(object({
id = string
}))
| `null` | no | +| [enable\_debug](#input\_enable\_debug) | Whether Terraform should enable module debug information. | `bool` | `false` | no | +| [enable\_output](#input\_enable\_output) | Whether Terraform should enable module output. | `bool` | `true` | no | +| [module\_name](#input\_module\_name) | The module name. | `string` | `"iam-policies"` | no | +| [policies\_configuration](#input\_policies\_configuration) | Policies configuration |
object({
enable_cis_benchmark_checks = optional(bool) # Whether to check policies for CIS Foundations Benchmark recommendations. Default is true.
supplied_policies = optional(map(object({ # A map of directly supplied policies. Use this to suplement or override the template policies.
name = string
description = string
compartment_id = string
statements = list(string)
defined_tags = optional(map(string))
freeform_tags = optional(map(string))
})))
template_policies = optional(object({ # An object describing the template policies. In this mode, policies are derived according to tenancy_level_settings and compartment_level_settings.
tenancy_level_settings = optional(object({ # Settings for tenancy level (Root compartment) policies generation.
groups_with_tenancy_level_roles = optional(list(object({ # A list of group names and their roles at the tenancy level. Template policies are granted to each group in the Root compartment.
name = string
roles = string
})))
oci_services = optional(object({
enable_all_policies = optional(bool)
enable_scanning_policies = optional(bool)
enable_cloud_guard_policies = optional(bool)
enable_os_management_policies = optional(bool)
enable_block_storage_policies = optional(bool)
enable_file_storage_policies = optional(bool)
enable_oke_policies = optional(bool)
enable_streaming_policies = optional(bool)
enable_object_storage_policies = optional(bool)
}))
policy_name_prefix = optional(string) # A prefix to Root compartment policy names.
}))
compartment_level_settings = optional(object({ # Settings for compartment (non Root) level policies generation.
supplied_compartments = optional(map(object({ # List of compartments that are policy targets.
name = string # The compartment name
id = string # The compartment id
cislz_metadata = map(string) # The compartment metadata. See module README.md for details.
})))
#policy_name_prefix = optional(string) # A prefix to compartment policy names.
}))
}))
policy_name_prefix = optional(string) # A prefix to all policy names.
policy_name_suffix = optional(string) # A suffix to all policy names.
defined_tags = optional(map(string)) # Any defined tags to apply on the template (pre-configured) policies.
freeform_tags = optional(map(string)) # Any freeform tags to apply on the template (pre-configured) policies.
})
| `null` | no | +| [tenancy\_ocid](#input\_tenancy\_ocid) | The tenancy OCID. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [policies](#output\_policies) | The policies. Enabled if enable\_output attribute is true. | +| [template\_target\_compartments](#output\_template\_target\_compartments) | An internal map driving the assignment of template policies according to compartment metadata. Enabled if enable\_debug attribute is true. | diff --git a/modules/oci-identity-policies/policies/application_cmp_policy.tf b/modules/oci-identity-policies/policies/application_cmp_policy.tf new file mode 100644 index 0000000..153ef5f --- /dev/null +++ b/modules/oci-identity-policies/policies/application_cmp_policy.tf @@ -0,0 +1,102 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + #-------------------------------------------------------------------------------------------- + #-- Application compartments policies + #-------------------------------------------------------------------------------------------- + + #-- Application read grants on application compartment + application_read_grants_on_application_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"application") && values["read-group"] != null) ? [ + "allow group ${values["read-group"]} to read all-resources in compartment ${values["name"]}" + ] : [] + } + + #-- Application admin grants on application compartment + application_admin_grants_on_application_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"application") && values["app-group"] != null) ? [ + "allow group ${values["app-group"]} to read all-resources in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage functions-family in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage api-gateway-family in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage ons-family in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage streams in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage cluster-family in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage alarms in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage metrics in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage logging-family in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage instance-family in compartment ${values["name"]}", + # CIS 1.2 - 1.14 Level 2 + "allow group ${values["app-group"]} to manage volume-family in compartment ${values["name"]} where all{request.permission != 'VOLUME_BACKUP_DELETE', request.permission != 'VOLUME_DELETE', request.permission != 'BOOT_VOLUME_BACKUP_DELETE'}", + "allow group ${values["app-group"]} to manage object-family in compartment ${values["name"]} where all{request.permission != 'OBJECT_DELETE', request.permission != 'BUCKET_DELETE'}", + "allow group ${values["app-group"]} to manage file-family in compartment ${values["name"]} where all{request.permission != 'FILE_SYSTEM_DELETE', request.permission != 'MOUNT_TARGET_DELETE', request.permission != 'EXPORT_SET_DELETE', request.permission != 'FILE_SYSTEM_DELETE_SNAPSHOT', request.permission != 'FILE_SYSTEM_NFSv3_UNEXPORT'}", + "allow group ${values["app-group"]} to manage repos in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage orm-stacks in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage orm-jobs in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage orm-config-source-providers in compartment ${values["name"]}", + #"allow group ${values["app-group"]} to read audit-events in compartment ${values["name"]}", + #"allow group ${values["app-group"]} to read work-requests in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage bastion-session in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage cloudevents-rules in compartment ${values["name"]}", + #"allow group ${values["app-group"]} to read instance-agent-plugins in compartment ${values["name"]}", + "allow group ${values["app-group"]} to use vnics in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage keys in compartment ${values["name"]}", + "allow group ${values["app-group"]} to use key-delegate in compartment ${values["name"]}", + "allow group ${values["app-group"]} to manage secret-family in compartment ${values["name"]}" + ] : [] + } + + #-- Storage admin grants on application compartment + storage_admin_grants_on_application_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"application") && values["stg-group"] != null) ? [ + # Object Storage + "allow group ${values["stg-group"]} to read bucket in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to inspect object in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage object-family in compartment ${values["name"]} where any {request.permission = 'OBJECT_DELETE', request.permission = 'BUCKET_DELETE'}", + # Volume Storage + "allow group ${values["stg-group"]} to read volume-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage volume-family in compartment ${values["name"]} where any {request.permission = 'VOLUME_DELETE', request.permission = 'VOLUME_BACKUP_DELETE', request.permission = 'BOOT_VOLUME_BACKUP_DELETE'}", + # File Storage + "allow group ${values["stg-group"]} to read file-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage file-family in compartment ${values["name"]} where any {request.permission = 'FILE_SYSTEM_DELETE', request.permission = 'MOUNT_TARGET_DELETE', request.permission = 'EXPORT_SET_UPDATE', request.permission = 'FILE_SYSTEM_NFSv3_UNEXPORT', request.permission = 'EXPORT_SET_DELETE', request.permission = 'FILE_SYSTEM_DELETE_SNAPSHOT'}" + ] : [] + } + + #-- Compute Agent grants on application compartment + compute_agent_grants_on_application_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"application") && values["ca-dyn-group"] != null) ? [ + "allow dynamic-group ${values["ca-dyn-group"]} to manage management-agents in compartment ${values["name"]}", + "allow dynamic-group ${values["ca-dyn-group"]} to use metrics in compartment ${values["name"]}", + "allow dynamic-group ${values["ca-dyn-group"]} to use tag-namespaces in compartment ${values["name"]}" + ] : [] + } + + #-- Security admin grants on application compartment + security_admin_grants_on_application_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"application") && values["sec-group"] != null) ? [ + "allow group ${values["sec-group"]} to read keys in compartment ${values["name"]}" + ] : [] + } + + oke_cluster_grants_on_application_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"application")) && local.enable_oke_service_policies ? [ + "allow any-user to manage instances in compartment ${values["name"]} where all { request.principal.type = 'cluster', request.principal.compartment.id = '${values["ocid"]}' }" + ] : [] + } + + #-- Policies for compartments marked as application compartments (values["cmp-type"] == "application"). + application_cmps_policies = { + for k, values in local.cmp_name_to_cislz_tag_map : + (upper("${k}-application-policy")) => { + name = length(regexall("^${local.policy_name_prefix}", values["name"])) > 0 ? (length(split(",",values["cmp-type"])) > 1 ? "${values["name"]}-application${local.policy_name_suffix}" : "${values["name"]}${local.policy_name_suffix}") : (length(split(",",values["cmp-type"])) > 1 ? "${local.policy_name_prefix}${values["name"]}-application${local.policy_name_suffix}" : "${local.policy_name_prefix}${values["name"]}${local.policy_name_suffix}") + compartment_id = values.ocid + description = "CIS Landing Zone policy for Application compartment." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = concat(local.application_admin_grants_on_application_cmp_map[k],local.application_read_grants_on_application_cmp_map[k], + local.storage_admin_grants_on_application_cmp_map[k],local.security_admin_grants_on_application_cmp_map[k], + local.compute_agent_grants_on_application_cmp_map[k],local.oke_cluster_grants_on_application_cmp_map[k]) + } + if contains(split(",",values["cmp-type"]),"application") + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/database_cmp_policy.tf b/modules/oci-identity-policies/policies/database_cmp_policy.tf new file mode 100644 index 0000000..d6c454b --- /dev/null +++ b/modules/oci-identity-policies/policies/database_cmp_policy.tf @@ -0,0 +1,101 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + #-------------------------------------------------------------------------------------------- + #-- Database compartments policies + #-------------------------------------------------------------------------------------------- + + #-- Database read grants on Database compartment + database_read_grants_on_database_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"database") && values["read-group"] != null) ? [ + "allow group ${values["read-group"]} to read all-resources in compartment ${values["name"]}" + ] : [] + } + + #-- Database admin grants on Database compartment + database_admin_grants_on_database_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"database") && values["db-group"] != null) ? [ + "allow group ${values["db-group"]} to read all-resources in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage database-family in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage autonomous-database-family in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage alarms in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage metrics in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage cloudevents-rules in compartment ${values["name"]}", + # CIS 1.2 - 1.14 Level 2 + "allow group ${values["db-group"]} to manage object-family in compartment ${values["name"]} where all{request.permission != 'OBJECT_DELETE', request.permission != 'BUCKET_DELETE'}", + "allow group ${values["db-group"]} to manage instance-family in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage volume-family in compartment ${values["name"]} where all{request.permission != 'VOLUME_BACKUP_DELETE', request.permission != 'VOLUME_DELETE', request.permission != 'BOOT_VOLUME_BACKUP_DELETE'}", + "allow group ${values["db-group"]} to manage file-family in compartment ${values["name"]} where all{request.permission != 'FILE_SYSTEM_DELETE', request.permission != 'MOUNT_TARGET_DELETE', request.permission != 'EXPORT_SET_DELETE', request.permission != 'FILE_SYSTEM_DELETE_SNAPSHOT', request.permission != 'FILE_SYSTEM_NFSv3_UNEXPORT'}", + "allow group ${values["db-group"]} to manage orm-stacks in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage orm-jobs in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage orm-config-source-providers in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage ons-family in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage logging-family in compartment ${values["name"]}", + #"allow group ${values["db-group"]} to read audit-events in compartment ${values["name"]}", + #"allow group ${values["db-group"]} to read work-requests in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage bastion-session in compartment ${values["name"]}", + #"allow group ${values["db-group"]} to read instance-agent-plugins in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage data-safe-family in compartment ${values["name"]}", + "allow group ${values["db-group"]} to use vnics in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage keys in compartment ${values["name"]}", + "allow group ${values["db-group"]} to use key-delegate in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage secret-family in compartment ${values["name"]}" + ] : [] + } + + #-- Application admin grants on Database compartment + appdev_admin_grants_on_database_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"database") && values["app-group"] != null) ? [ + "allow group ${values["app-group"]} to read autonomous-database-family in compartment ${values["name"]}", + "allow group ${values["app-group"]} to read database-family in compartment ${values["name"]}" + ] : [] + } + + #-- Storage admin grants in database compartment + storage_admin_grants_on_database_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"database") && values["stg-group"] != null) ? [ + # Object Storage + "allow group ${values["stg-group"]} to read bucket in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to inspect object in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage object-family in compartment ${values["name"]} where any {request.permission = 'OBJECT_DELETE', request.permission = 'BUCKET_DELETE'}", + # Volume Storage + "allow group ${values["stg-group"]} to read volume-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage volume-family in compartment ${values["name"]} where any {request.permission = 'VOLUME_DELETE', request.permission = 'VOLUME_BACKUP_DELETE', request.permission = 'BOOT_VOLUME_BACKUP_DELETE'}", + # File Storage + "allow group ${values["stg-group"]} to read file-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage file-family in compartment ${values["name"]} where any {request.permission = 'FILE_SYSTEM_DELETE', request.permission = 'MOUNT_TARGET_DELETE', request.permission = 'EXPORT_SET_UPDATE', request.permission = 'FILE_SYSTEM_NFSv3_UNEXPORT', request.permission = 'EXPORT_SET_DELETE', request.permission = 'FILE_SYSTEM_DELETE_SNAPSHOT'}" + ] : [] + } + + #-- Security admin grants on database compartment + security_admin_grants_on_database_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"database") && values["sec-group"] != null) ? [ + "allow group ${values["sec-group"]} to read keys in compartment ${values["name"]}" + ] : [] + } + + #-- Database grants on Security compartment + database_kms_grants_on_database_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"database") && values["db-dyn-group"] != null) ? [ + "allow dynamic-group ${values["db-dyn-group"]} to use keys in compartment ${values["name"]}", + "allow dynamic-group ${values["db-dyn-group"]} to use secret-family in compartment ${values["name"]}" + ] : [] + } + + #-- Policies for compartments marked as database compartments (values["cmp-type"] == "database"). + database_cmps_policies = { + for k, values in local.cmp_name_to_cislz_tag_map : + (upper("${k}-database-policy")) => { + name = length(regexall("^${local.policy_name_prefix}", values["name"])) > 0 ? (length(split(",",values["cmp-type"])) > 1 ? "${values["name"]}-database${local.policy_name_suffix}" : "${values["name"]}${local.policy_name_suffix}") : (length(split(",",values["cmp-type"])) > 1 ? "${local.policy_name_prefix}${values["name"]}-database${local.policy_name_suffix}" : "${local.policy_name_prefix}${values["name"]}${local.policy_name_suffix}") + compartment_id = values.ocid + description = "CIS Landing Zone policy for Database compartment." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = concat(local.database_admin_grants_on_database_cmp_map[k],local.database_read_grants_on_database_cmp_map[k], + local.appdev_admin_grants_on_database_cmp_map[k],local.storage_admin_grants_on_database_cmp_map[k], + local.security_admin_grants_on_database_cmp_map[k], local.database_kms_grants_on_database_cmp_map[k]) + } + if contains(split(",",values["cmp-type"]),"database") + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/enclosing_cmp_policy.tf b/modules/oci-identity-policies/policies/enclosing_cmp_policy.tf new file mode 100644 index 0000000..f2c4c5d --- /dev/null +++ b/modules/oci-identity-policies/policies/enclosing_cmp_policy.tf @@ -0,0 +1,87 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + #-------------------------------------------------------------------------------------------- + #-- Enclosing compartments policies + #-------------------------------------------------------------------------------------------- + + #-- Read grants on enclosing compartment. + read_grants_on_enclosing_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"enclosing") && values["read-group"] != null) ? + values["ocid"] != var.tenancy_ocid ? + ["allow group ${values["read-group"]} to read all-resources in compartment ${values["name"]}"] : + + ["allow group ${values["read-group"]} to read all-resources in tenancy"] : + [] + } + + #-- IAM admin grants on enclosing compartment. + iam_admin_grants_on_enclosing_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"enclosing") && values["iam-group"] != null) ? + values["ocid"] != var.tenancy_ocid ? + ["allow group ${values["iam-group"]} to manage policies in compartment ${values["name"]}", + "allow group ${values["iam-group"]} to manage compartments in compartment ${values["name"]}"] : + + ["allow group ${values["iam-group"]} to manage policies in tenancy", + "allow group ${values["iam-group"]} to manage compartments in tenancy"] : + [] + } + + #-- Security admin grants on enclosing compartment. + security_admin_grants_on_enclosing_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"enclosing") && values["sec-group"] != null) ? + values["ocid"] != var.tenancy_ocid ? + ["allow group ${values["sec-group"]} to manage tag-namespaces in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage tag-defaults in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage repos in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to read audit-events in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to read app-catalog-listing in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to read instance-images in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to inspect buckets in compartment ${values["name"]}"] : + + ["allow group ${values["sec-group"]} to manage tag-namespaces in tenancy", + "allow group ${values["sec-group"]} to manage tag-defaults in tenancy", + "allow group ${values["sec-group"]} to manage repos in tenancy", + "allow group ${values["sec-group"]} to read audit-events in tenancy", + "allow group ${values["sec-group"]} to read app-catalog-listing in tenancy", + "allow group ${values["sec-group"]} to read instance-images in tenancy", + "allow group ${values["sec-group"]} to inspect buckets in tenancy"] : + [] + } + + #-- Application admin grants on enclosing compartment. + application_admin_grants_on_enclosing_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"enclosing") && values["app-group"] != null) ? + values["ocid"] != var.tenancy_ocid ? + ["allow group ${values["app-group"]} to read app-catalog-listing in compartment ${values["name"]}", + "allow group ${values["app-group"]} to read instance-images in compartment ${values["name"]}", + "allow group ${values["app-group"]} to read repos in compartment ${values["name"]}"] : + + ["allow group ${values["app-group"]} to read app-catalog-listing in tenancy", + "allow group ${values["app-group"]} to read instance-images in tenancy", + "allow group ${values["app-group"]} to read repos in tenancy"] : + [] + } + + #-- Policies for compartments marked as enclosing compartments (values["cmp-type"] == "enclosing"). + enclosing_cmps_policies = { + for k, values in local.cmp_name_to_cislz_tag_map : + (upper("${k}-enclosing-policy")) => { + name = length(regexall("^${local.policy_name_prefix}", values["name"])) > 0 ? (length(split(",",values["cmp-type"])) > 1 ? "${values["name"]}-enclosing${local.policy_name_suffix}" : "${values["name"]}${local.policy_name_suffix}") : (length(split(",",values["cmp-type"])) > 1 ? "${local.policy_name_prefix}${values["name"]}-enclosing${local.policy_name_suffix}" : "${local.policy_name_prefix}${values["name"]}${local.policy_name_suffix}") + compartment_id = values.ocid + description = "CIS Landing Zone policy for enclosing compartment." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = concat(local.read_grants_on_enclosing_cmp_map[k],local.iam_admin_grants_on_enclosing_cmp_map[k], + local.security_admin_grants_on_enclosing_cmp_map[k],local.application_admin_grants_on_enclosing_cmp_map[k]) + } + if contains(split(",",values["cmp-type"]),"enclosing") + } +} + +/* resource "random_string" "this" { + length = 5 + special = false + upper = false +} */ \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/exainfra_cmp_policy.tf b/modules/oci-identity-policies/policies/exainfra_cmp_policy.tf new file mode 100644 index 0000000..5f25ea4 --- /dev/null +++ b/modules/oci-identity-policies/policies/exainfra_cmp_policy.tf @@ -0,0 +1,75 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + #-------------------------------------------------------------------------------------------- + #-- Exadata Cloud Service infrastructure compartments policies + #-------------------------------------------------------------------------------------------- + + #-- Exainfra read grants on Exinfra compartment + exainfra_read_grants_on_exainfra_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"exainfra") && values["read-group"] != null) ? [ + "allow group ${values["read-group"]} to read all-resources in compartment ${values["name"]}" + ] : [] + } + + #-- Exainfra admin grants on Exinfra compartment + exainfra_admin_grants_on_exainfra_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"exainfra") && values["exa-group"] != null) ? [ + "allow group ${values["exa-group"]} to read all-resources in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage cloud-exadata-infrastructures in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage cloud-vmclusters in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to read work-requests in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage bastion-session in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage instance-family in compartment ${values["name"]}", + #"allow group ${values["exa-group"]} to read instance-agent-plugins in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage ons-family in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage alarms in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage metrics in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage data-safe-family in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to use vnics in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage keys in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to use key-delegate in compartment ${values["name"]}", + "allow group ${values["exa-group"]} to manage secret-family in compartment ${values["name"]}" + ] : [] + } + + #-- Database admin grants on Exainfra compartment + database_admin_grants_on_exainfra_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"exainfra") && values["db-group"] != null) ? [ + "allow group ${values["db-group"]} to read cloud-exadata-infrastructures in compartment ${values["name"]}", + "allow group ${values["db-group"]} to use cloud-vmclusters in compartment ${values["name"]}", + "allow group ${values["db-group"]} to read work-requests in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage db-nodes in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage db-homes in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage databases in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage db-backups in compartment ${values["name"]}", + "allow group ${values["db-group"]} to manage data-safe-family in compartment ${values["name"]}", + "allow group ${values["db-group"]} to use vnics in compartment ${values["name"]}" + ] : [] + } + + #-- Security admin grants on Exainfra compartment + security_admin_grants_on_exainfra_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"exainfra") && values["sec-group"] != null) ? [ + "allow group ${values["sec-group"]} to read keys in compartment ${values["name"]}" + ] : [] + } + + #-- Policies for compartments marked as exainfra compartments (values["cmp-type"] == "exainfra"). + exainfra_cmps_policies = { + for k, values in local.cmp_name_to_cislz_tag_map : + (upper("${k}-exainfra-policy")) => { + name = length(regexall("^${local.policy_name_prefix}", values["name"])) > 0 ? (length(split(",",values["cmp-type"])) > 1 ? "${values["name"]}-exainfra${local.policy_name_suffix}" : "${values["name"]}${local.policy_name_suffix}") : (length(split(",",values["cmp-type"])) > 1 ? "${local.policy_name_prefix}${values["name"]}-exainfra${local.policy_name_suffix}" : "${local.policy_name_prefix}${values["name"]}${local.policy_name_suffix}") + compartment_id = values.ocid + description = "CIS Landing Zone policy for Exadata Cloud Service infrastructure compartment." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = concat(local.exainfra_admin_grants_on_exainfra_cmp_map[k], + local.exainfra_read_grants_on_exainfra_cmp_map[k], + local.database_admin_grants_on_exainfra_cmp_map[k], + local.security_admin_grants_on_exainfra_cmp_map[k]) + } + if contains(split(",",values["cmp-type"]),"exainfra") + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/locals.tf b/modules/oci-identity-policies/policies/locals.tf new file mode 100644 index 0000000..3dbeb64 --- /dev/null +++ b/modules/oci-identity-policies/policies/locals.tf @@ -0,0 +1,57 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + + #-- The expected tag names. + cmp_type_tag_name = "cislz-cmp-type" + iam_group_tag_name = "cislz-consumer-groups-iam" + security_group_tag_name = "cislz-consumer-groups-security" + read_group_tag_name = "cislz-consumer-groups-read" + application_group_tag_name = "cislz-consumer-groups-application" + database_group_tag_name = "cislz-consumer-groups-database" + network_group_tag_name = "cislz-consumer-groups-network" + exainfra_group_tag_name = "cislz-consumer-groups-exainfra" + storage_group_tag_name = "cislz-consumer-groups-storage" + database_kms_dyn_group_tag_name = "cislz-consumer-groups-dyn-database-kms" + compute_agent_dyn_group_tag_name = "cislz-consumer-groups-dyn-compute-agent" + + #-- Module defaults + enable_cis_benchmark_checks = var.policies_configuration != null ? (var.policies_configuration.enable_cis_benchmark_checks != null ? var.policies_configuration.enable_cis_benchmark_checks : true) : false + enable_tenancy_level_template_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.groups_with_tenancy_level_roles != null ? true : false) : false) : false) : false + enable_compartment_level_template_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.compartment_level_settings != null ? (var.policies_configuration.template_policies.compartment_level_settings.supplied_compartments != null ? true : false) : false) : false) : false + enable_output = var.enable_output != null ? var.enable_output : false + enable_debug = var.enable_debug != null ? var.enable_debug : false + supplied_compartments = local.enable_compartment_level_template_policies == true ? (var.policies_configuration != null ? var.policies_configuration.template_policies.compartment_level_settings.supplied_compartments : {}) : {} + supplied_policies = var.policies_configuration != null ? (var.policies_configuration.supplied_policies != null ? var.policies_configuration.supplied_policies : {}) : {} + #-- Service policies + enable_scanning_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_scanning_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + enable_cloud_guard_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_cloud_guard_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + enable_os_management_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_os_management_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + enable_block_storage_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_block_storage_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + enable_file_storage_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_file_storage_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + enable_streaming_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_streaming_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + enable_oke_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_oke_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + enable_object_storage_service_policies = var.policies_configuration != null ? (var.policies_configuration.template_policies != null ? (var.policies_configuration.template_policies.tenancy_level_settings != null ? (var.policies_configuration.template_policies.tenancy_level_settings.oci_services != null ? (coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_object_storage_policies,false) == true || coalesce(var.policies_configuration.template_policies.tenancy_level_settings.oci_services.enable_all_policies,false) == true ? true : false) : false) : false) : false) : false + + #-- Map derived from compartments input variable. + cmp_name_to_cislz_tag_map = {for k, cmp in local.supplied_compartments : k => { + name : cmp.name + ocid : length(regexall("^ocid1.*$", cmp.id)) > 0 ? cmp.id : var.compartments_dependency[cmp.id].id + cmp-type : lookup(cmp.cislz_metadata, local.cmp_type_tag_name,""), + iam-group : length(lookup(cmp.cislz_metadata, local.iam_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.iam_group_tag_name,"") : null, + sec-group : length(lookup(cmp.cislz_metadata, local.security_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.security_group_tag_name,"") : null, + read-group : length(lookup(cmp.cislz_metadata, local.read_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.read_group_tag_name,"") : null, + app-group : length(lookup(cmp.cislz_metadata, local.application_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.application_group_tag_name,"") : null, + db-group : length(lookup(cmp.cislz_metadata, local.database_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.database_group_tag_name,"") : null, + net-group : length(lookup(cmp.cislz_metadata, local.network_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.network_group_tag_name,"") : null, + exa-group : length(lookup(cmp.cislz_metadata, local.exainfra_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.exainfra_group_tag_name,"") : null, + stg-group : length(lookup(cmp.cislz_metadata, local.storage_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.storage_group_tag_name,"") : null, + db-dyn-group : length(lookup(cmp.cislz_metadata, local.database_kms_dyn_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata, local.database_kms_dyn_group_tag_name,"") : null, + ca-dyn-group : length(lookup(cmp.cislz_metadata, local.compute_agent_dyn_group_tag_name,"")) > 0 ? lookup(cmp.cislz_metadata,local.compute_agent_dyn_group_tag_name,"") : null, + }} + + #cmp_policy_name_prefix = local.enable_compartment_level_template_policies == true ? (var.policies_configuration.template_policies.compartment_level_settings.policy_name_prefix != null ? "${var.policies_configuration.template_policies.compartment_level_settings.policy_name_prefix}-" : "") : "" + policy_name_prefix = var.policies_configuration != null ? (var.policies_configuration.policy_name_prefix != null ? "${var.policies_configuration.policy_name_prefix}-" : "") : "" + policy_name_suffix = var.policies_configuration != null ? (var.policies_configuration.policy_name_suffix != null ? (var.policies_configuration.policy_name_suffix == "" ? "" : "-${var.policies_configuration.policy_name_suffix}") : "-policy") : "-policy" +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/main.tf b/modules/oci-identity-policies/policies/main.tf new file mode 100644 index 0000000..469dac8 --- /dev/null +++ b/modules/oci-identity-policies/policies/main.tf @@ -0,0 +1,47 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + + cis_iam12_regex = ".*to\\s*manage\\s*all-resources\\s*in\\s*tenancy\\s*$" + + cis_iam13_main_final_regex = ".*to\\s*(manage|use)\\s*(users|groups)\\s*in\\s*tenancy\\s*$" + cis_iam13_main_nonfinal_regex = ".*to\\s*(manage|use)\\s*(users|groups)\\s*in\\s*tenancy.*" + cis_iam13_tenancy_admin_regex = ".*(\\s.*all\\s.*).*(target.group.name\\s*!=\\s*'Administrators').*" + cis_iam13_non_admin_regex = ".*(target.group.name\\s*=\\s*'[^Administrator]').*" + cis_iam13_operations_regex = ".*(request.operation|request.permission)\\s*=\\s*('AddUserToGroup'|'RemoveUserFromGroup'|'UpdateGroup'|'DeleteGroup'|'GROUP_UPDATE'|'GROUP_DELETE').*" + + template_root_policies = local.enable_tenancy_level_template_policies ? merge(local.root_cmp_admin_policy, local.root_cmp_nonadmin_policy, local.services_policy) : {} + template_cmp_policies = local.enable_compartment_level_template_policies ? merge(local.enclosing_cmps_policies, local.security_cmps_policies, + local.network_cmps_policies, local.application_cmps_policies, + local.database_cmps_policies, local.exainfra_cmps_policies) : {} + + policies = merge(local.template_root_policies, local.template_cmp_policies, local.supplied_policies) +} + +resource "oci_identity_policy" "these" { + for_each = local.policies + name = each.value.name + description = each.value.description + compartment_id = length(regexall("^ocid1.*$", each.value.compartment_id)) > 0 ? each.value.compartment_id : (upper(each.value.compartment_id) == "TENANCY-ROOT" ? var.tenancy_ocid : var.compartments_dependency[each.value.compartment_id].id) + statements = each.value.statements + defined_tags = each.value.defined_tags + freeform_tags = merge(local.cislz_module_tag, each.value.freeform_tags) + + ##-- The can(regex(pattern)) combination used below is a way to ask if pattern is present in each policy statement. + ##-- Conversely, !can(regex(pattern)) asks if pattern is not present. + lifecycle { + precondition { + 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 + error_message = "VALIDATION FAILURE (CIS IAM 1.2): Policy ${each.value.name} has statements that allow a group to manage any resource in the tenancy: \"${join(",",[for s in each.value.statements : s if can(regex(local.cis_iam12_regex,lower(s)))])}\"" + } + precondition { + condition = local.enable_cis_benchmark_checks ? length([for s in each.value.statements : s if can(regex(local.cis_iam13_main_final_regex,lower(s)))]) == 0 : true + error_message = "VALIDATION FAILURE (CIS IAM 1.3): Policy ${each.value.name} has statements that allow a group to change the Administrators group: \"${join(",",[for s in each.value.statements : s if can(regex(local.cis_iam13_main_final_regex,lower(s)))])}\". You can exclude Administrators group from this statement by adding \"target.group.name != 'Administrators'\" to the statement." + } + precondition { + condition = local.enable_cis_benchmark_checks ? length([for s in each.value.statements : s if can(regex(local.cis_iam13_main_nonfinal_regex,lower(s))) && can(regex(local.cis_iam13_operations_regex,s)) && !can(regex(local.cis_iam13_tenancy_admin_regex,s))]) == 0 : true + error_message = "VALIDATION FAILURE (CIS IAM 1.3): Policy ${each.value.name} has statements that allow a group to change the Administrators group: \"${join(",",[for s in each.value.statements : s if can(regex(local.cis_iam13_main_nonfinal_regex,lower(s))) && can(regex(local.cis_iam13_operations_regex,s)) && !can(regex(local.cis_iam13_tenancy_admin_regex,s))])}\". You can exclude Administrators group from this statement by adding \"target.group.name != 'Administrators'\" to the statement." + } + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/metadata.tf b/modules/oci-identity-policies/policies/metadata.tf new file mode 100644 index 0000000..b2d9c2d --- /dev/null +++ b/modules/oci-identity-policies/policies/metadata.tf @@ -0,0 +1,7 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +#-- Used to inform module and release number. +locals { + cislz_module_tag = {"cislz-terraform-module" : fileexists("${path.module}/../release.txt") ? "${var.module_name}/${file("${path.module}/../release.txt")}" : "${var.module_name}"} +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/network_cmp_policy.tf b/modules/oci-identity-policies/policies/network_cmp_policy.tf new file mode 100644 index 0000000..77baf64 --- /dev/null +++ b/modules/oci-identity-policies/policies/network_cmp_policy.tf @@ -0,0 +1,118 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + #-------------------------------------------------------------------------------------------- + #-- Network compartments policies + #-------------------------------------------------------------------------------------------- + + #-- Network read-only grants on Network compartment + network_read_grants_on_network_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"network") && values["read-group"] != null) ? [ + "allow group ${values["read-group"]} to read all-resources in compartment ${values["name"]}" + ] : [] + } + + #-- Network admin grants on Network compartment + network_admin_grants_on_network_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"network") && values["net-group"] != null) ? [ + "allow group ${values["net-group"]} to read all-resources in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage virtual-network-family in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage dns in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage load-balancers in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage alarms in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage metrics in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage ons-family in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage orm-stacks in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage orm-jobs in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage orm-config-source-providers in compartment ${values["name"]}", + #"allow group ${values["net-group"]} to read audit-events in compartment ${values["name"]}", + #"allow group ${values["net-group"]} to read work-requests in compartment ${values["name"]}", + # CIS 1.2 - 1.14 Level 2 + "allow group ${values["net-group"]} to manage instance-family in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage volume-family in compartment ${values["name"]} where all{request.permission != 'VOLUME_BACKUP_DELETE', request.permission != 'VOLUME_DELETE', request.permission != 'BOOT_VOLUME_BACKUP_DELETE'}", + "allow group ${values["net-group"]} to manage object-family in compartment ${values["name"]} where all{request.permission != 'OBJECT_DELETE', request.permission != 'BUCKET_DELETE'}", + "allow group ${values["net-group"]} to manage file-family in compartment ${values["name"]} where all{request.permission != 'FILE_SYSTEM_DELETE', request.permission != 'MOUNT_TARGET_DELETE', request.permission != 'EXPORT_SET_DELETE', request.permission != 'FILE_SYSTEM_DELETE_SNAPSHOT', request.permission != 'FILE_SYSTEM_NFSv3_UNEXPORT'}", + "allow group ${values["net-group"]} to manage bastion-session in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage cloudevents-rules in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage alarms in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage metrics in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage keys in compartment ${values["name"]}", + "allow group ${values["net-group"]} to use key-delegate in compartment ${values["name"]}", + "allow group ${values["net-group"]} to manage secret-family in compartment ${values["name"]}" + #"allow group ${values["net-group"]} to read instance-agent-plugins in compartment ${values["name"]}" + ] : [] + } + + common_groups_on_network_cmp = { + for k, values in local.cmp_name_to_cislz_tag_map : k => [values["sec-group"] != null ? "${values["sec-group"]}" : "", values["app-group"] != null ? "${values["app-group"]}" : "", values["db-group"] != null ? "${values["db-group"]}" : "", values["exa-group"] != null ? "${values["exa-group"]}" : ""] + if contains(split(",",values["cmp-type"]),"network")} + + common_admin_grants_on_network_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"network")) ? [ + "allow group ${trim(join(",",local.common_groups_on_network_cmp[k]),",")} to read virtual-network-family in compartment ${values["name"]}", + "allow group ${trim(join(",",local.common_groups_on_network_cmp[k]),",")} to use subnets in compartment ${values["name"]}", + "allow group ${trim(join(",",local.common_groups_on_network_cmp[k]),",")} to use network-security-groups in compartment ${values["name"]}", + "allow group ${trim(join(",",local.common_groups_on_network_cmp[k]),",")} to use vnics in compartment ${values["name"]}", + "allow group ${trim(join(",",local.common_groups_on_network_cmp[k]),",")} to manage private-ips in compartment ${values["name"]}", + ] : [] + } + + #-- Security admin grants on Network compartment + security_admin_grants_on_network_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"network") && values["sec-group"] != null) ? [ + "allow group ${values["sec-group"]} to read keys in compartment ${values["name"]}" + ] : [] + } + + #-- Application admin grants on Network compartment + appdev_admin_grants_on_network_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"network") && values["app-group"] != null) ? [ + "allow group ${values["app-group"]} to use load-balancers in compartment ${values["name"]}" + ] : [] + } + + #-- Storag admin grants on Network compartment + storage_admin_grants_on_network_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"network") && values["stg-group"] != null) ? [ + # Object Storage + "allow group ${values["stg-group"]} to read bucket in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to inspect object in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage object-family in compartment ${values["name"]} where any {request.permission = 'OBJECT_DELETE', request.permission = 'BUCKET_DELETE'}", + # Volume Storage + "allow group ${values["stg-group"]} to read volume-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage volume-family in compartment ${values["name"]} where any {request.permission = 'VOLUME_DELETE', request.permission = 'VOLUME_BACKUP_DELETE', request.permission = 'BOOT_VOLUME_BACKUP_DELETE'}", + # File Storage + "allow group ${values["stg-group"]} to read file-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage file-family in compartment ${values["name"]} where any {request.permission = 'FILE_SYSTEM_DELETE', request.permission = 'MOUNT_TARGET_DELETE', request.permission = 'VNIC_DELETE', request.permission = 'SUBNET_DETACH', request.permission = 'VNIC_DETACH', request.permission = 'PRIVATE_IP_DELETE', request.permission = 'PRIVATE_IP_UNASSIGN', request.permission = 'VNIC_UNASSIGN', request.permission = 'EXPORT_SET_UPDATE', request.permission = 'FILE_SYSTEM_NFSv3_UNEXPORT', request.permission = 'EXPORT_SET_DELETE', request.permission = 'FILE_SYSTEM_DELETE_SNAPSHOT'}", + ] : [] + } + + application_compartment_id = local.enable_oke_service_policies ? (distinct(compact(concat([for k, values in local.cmp_name_to_cislz_tag_map : (contains(split(",",values["cmp-type"]),"application")) ? values["ocid"] : ""])))[0]) : null + + oke_cluster_grants_on_network_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"network")) && local.enable_oke_service_policies && local.application_compartment_id != null ? [ + "allow any-user to use private-ips in compartment ${values["name"]} where all { request.principal.type = 'cluster', request.principal.compartment.id = '${local.application_compartment_id}' }", + "allow any-user to use network-security-groups in compartment ${values["name"]} where all { request.principal.type = 'cluster', request.principal.compartment.id = '${local.application_compartment_id}' }", + "allow any-user to use subnets in compartment ${values["name"]} where all { request.principal.type = 'cluster', request.principal.compartment.id = '${local.application_compartment_id}' }" + ] : [] + } + + #-- Policies for compartments marked as network compartments (values["cmp-type"] == "network"). + network_cmps_policies = { + for k, values in local.cmp_name_to_cislz_tag_map : + (upper("${k}-network-policy")) => { + name = length(regexall("^${local.policy_name_prefix}", values["name"])) > 0 ? (length(split(",",values["cmp-type"])) > 1 ? "${values["name"]}-network${local.policy_name_suffix}" : "${values["name"]}${local.policy_name_suffix}") : (length(split(",",values["cmp-type"])) > 1 ? "${local.policy_name_prefix}${values["name"]}-network${local.policy_name_suffix}" : "${local.policy_name_prefix}${values["name"]}${local.policy_name_suffix}") + compartment_id = values.ocid + description = "CIS Landing Zone policy for Network compartment." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = concat(local.network_admin_grants_on_network_cmp_map[k],local.network_read_grants_on_network_cmp_map[k], + local.security_admin_grants_on_network_cmp_map[k],local.appdev_admin_grants_on_network_cmp_map[k], + #local.database_admin_grants_on_network_cmp_map[k],local.exainfra_admin_grants_on_network_cmp_map[k], + local.common_admin_grants_on_network_cmp_map[k], local.storage_admin_grants_on_network_cmp_map[k], + local.oke_cluster_grants_on_network_cmp_map[k]) + } + if contains(split(",",values["cmp-type"]),"network") + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/outputs.tf b/modules/oci-identity-policies/policies/outputs.tf new file mode 100644 index 0000000..0e83b87 --- /dev/null +++ b/modules/oci-identity-policies/policies/outputs.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +output "policies" { + description = "The policies. Enabled if enable_output attribute is true." + value = local.enable_output ? oci_identity_policy.these : null +} + +output "template_target_compartments" { + description = "An internal map driving the assignment of template policies according to compartment metadata. Enabled if enable_debug attribute is true." + value = local.enable_debug ? local.cmp_name_to_cislz_tag_map : null +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/providers.tf b/modules/oci-identity-policies/policies/providers.tf new file mode 100644 index 0000000..1db9bff --- /dev/null +++ b/modules/oci-identity-policies/policies/providers.tf @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +terraform { + required_providers { + oci = { + source = "oracle/oci" + } + /* random = { + source = "hashicorp/random" + } */ + } + # experiments = [module_variable_optional_attrs] +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/root_cmp_policy.tf b/modules/oci-identity-policies/policies/root_cmp_policy.tf new file mode 100644 index 0000000..17c1499 --- /dev/null +++ b/modules/oci-identity-policies/policies/root_cmp_policy.tf @@ -0,0 +1,196 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +#-------------------------------------------------------------------------------------------- +#-- Tenancy root compartment policy +#-- Notice that policy is conditioned to the existence of applicable grants +#-------------------------------------------------------------------------------------------- + +locals { + + #-- Valid group roles for policies at the root compartment (tenancy) level. + iam_role = "iam" + cred_role = "cred" + cost_role = "cost" + security_role = "security" + application_role = "application" + network_role = "network" + database_role = "database" + exainfra_role = "exainfra" + auditor_role = "auditor" + announcement_reader_role = "announcement-reader" + + groups_with_tenancy_level_roles = local.enable_tenancy_level_template_policies == true ? var.policies_configuration.template_policies.tenancy_level_settings.groups_with_tenancy_level_roles : [] + + group_name_to_role_map = {for group in local.groups_with_tenancy_level_roles : group.name => split(",", lookup(group,"roles","basic"))} # this produces objects like {"group-name-1" : ["iam", "security"]} + group_names = join(",", compact(keys(local.group_name_to_role_map))) # this produces a comma separated string of group names, like "group-name-1, group-name-2, group-name-3" + group_name_map_transpose = transpose(local.group_name_to_role_map) # this produces objects like {"iam" : ["group-name-1"], "security" : ["group-name-1"]} + #group_role_to_name_map = {for key, value in local.group_name_map_transpose : key => value[0]} # this is the same transposed matrix, but it takes group name string at index 0. + + iam_group_names = contains(keys(local.group_name_map_transpose),local.iam_role) ? join(",",local.group_name_map_transpose[local.iam_role]) : null + cred_group_names = contains(keys(local.group_name_map_transpose),local.cred_role) ? join(",",local.group_name_map_transpose[local.cred_role]) : null + cost_group_names = contains(keys(local.group_name_map_transpose),local.cost_role) ? join(",",local.group_name_map_transpose[local.cost_role]) : null + security_group_names = contains(keys(local.group_name_map_transpose),local.security_role) ? join(",",local.group_name_map_transpose[local.security_role]) : null + application_group_names = contains(keys(local.group_name_map_transpose),local.application_role) ? join(",",local.group_name_map_transpose[local.application_role]) : null + network_group_names = contains(keys(local.group_name_map_transpose),local.network_role) ? join(",",local.group_name_map_transpose[local.network_role]) : null + database_group_names = contains(keys(local.group_name_map_transpose),local.database_role) ? join(",",local.group_name_map_transpose[local.database_role]) : null + exainfra_group_names = contains(keys(local.group_name_map_transpose),local.exainfra_role) ? join(",",local.group_name_map_transpose[local.exainfra_role]) : null + auditor_group_names = contains(keys(local.group_name_map_transpose),local.auditor_role) ? join(",",local.group_name_map_transpose[local.auditor_role]) : null + announcement_reader_group_names = contains(keys(local.group_name_map_transpose),local.announcement_reader_role) ? join(",",local.group_name_map_transpose[local.announcement_reader_role]) : null + + iam_grants_condition = contains(keys(local.group_name_map_transpose),local.cred_role) ? [for g in split(",",local.cred_group_names) : substr(g,0,1) == "'" && substr(g,length(g)-1,1) == "'" ? "target.group.name != ${g}" : "target.group.name != '${g}'"] : [] + + #-- Used to check if an enclosing compartment is available. + cmp_type_list = flatten([for cmp, values in local.cmp_name_to_cislz_tag_map : split(",",values.cmp-type)]) + + #-- Basic grants + basic_grants_on_root_cmp = length(local.group_names) > 0 ? [ + "allow group ${local.group_names} to use cloud-shell in tenancy", + "allow group ${local.group_names} to read usage-budgets in tenancy", + "allow group ${local.group_names} to read usage-reports in tenancy", + "allow group ${local.group_names} to read objectstorage-namespaces in tenancy" + ] : [] + + iam_admin_grants_on_root_cmp = contains(keys(local.group_name_map_transpose),local.iam_role) ? [ + "allow group ${local.iam_group_names} to inspect users in tenancy", + "allow group ${local.iam_group_names} to manage users in tenancy where all {request.operation != 'ListApiKeys',request.operation != 'ListAuthTokens',request.operation != 'ListCustomerSecretKeys',request.operation != 'UploadApiKey',request.operation != 'DeleteApiKey',request.operation != 'UpdateAuthToken',request.operation != 'CreateAuthToken',request.operation != 'DeleteAuthToken',request.operation != 'CreateSecretKey',request.operation != 'UpdateCustomerSecretKey',request.operation != 'DeleteCustomerSecretKey'}", + "allow group ${local.iam_group_names} to inspect groups in tenancy", + "allow group ${local.iam_group_names} to read policies in tenancy", + "allow group ${local.iam_group_names} to manage groups in tenancy where all {target.group.name != 'Administrators', ${join(",",local.iam_grants_condition)}}", + "allow group ${local.iam_group_names} to inspect identity-providers in tenancy", + "allow group ${local.iam_group_names} to manage identity-providers in tenancy where any {request.operation = 'AddIdpGroupMapping', request.operation = 'DeleteIdpGroupMapping'}", + "allow group ${local.iam_group_names} to manage dynamic-groups in tenancy", + "allow group ${local.iam_group_names} to manage authentication-policies in tenancy", + "allow group ${local.iam_group_names} to manage network-sources in tenancy", + "allow group ${local.iam_group_names} to manage quota in tenancy", + "allow group ${local.iam_group_names} to read audit-events in tenancy", + "allow group ${local.iam_group_names} to manage tag-defaults in tenancy", + "allow group ${local.iam_group_names} to manage tag-namespaces in tenancy", + # Statements scoped to allow an IAM admin to deploy IAM resources via ORM + "allow group ${local.iam_group_names} to manage orm-stacks in tenancy", + "allow group ${local.iam_group_names} to manage orm-jobs in tenancy", + "allow group ${local.iam_group_names} to manage orm-config-source-providers in tenancy" + ] : [] + + # For the case when there's no enclosing compartment defined, the grants are set in the root compartment. Analogous grants are present in enclosing_cmp_policy.tf, which are applied when an enclosing compartment is defined. + iam_admin_grants_on_enclosing_cmp = contains(keys(local.group_name_map_transpose),local.iam_role) && !contains(local.cmp_type_list,"enclosing") ? [ + "allow group ${local.iam_group_names} to manage policies in tenancy", + "allow group ${local.iam_group_names} to manage compartments in tenancy" + ] : [] + + cred_admin_grants_on_root_cmp = contains(keys(local.group_name_map_transpose),local.cred_role) ? [ + "allow group ${local.cred_group_names} to inspect users in tenancy", + "allow group ${local.cred_group_names} to inspect groups in tenancy", + "allow group ${local.cred_group_names} to manage users in tenancy where any {request.operation = 'ListApiKeys',request.operation = 'ListAuthTokens',request.operation = 'ListCustomerSecretKeys',request.operation = 'UploadApiKey',request.operation = 'DeleteApiKey',request.operation = 'UpdateAuthToken',request.operation = 'CreateAuthToken',request.operation = 'DeleteAuthToken',request.operation = 'CreateSecretKey',request.operation = 'UpdateCustomerSecretKey',request.operation = 'DeleteCustomerSecretKey',request.operation = 'UpdateUserCapabilities'}" + ] : [] + + cost_admin_grants_on_root_cmp = contains(keys(local.group_name_map_transpose),local.cost_role) ? [ + "define tenancy usage-report as ocid1.tenancy.oc1..aaaaaaaaned4fkpkisbwjlr56u7cj63lf3wffbilvqknstgtvzub7vhqkggq", + "endorse group ${local.cost_group_names} to read objects in tenancy usage-report", + "allow group ${local.cost_group_names} to manage usage-report in tenancy", + "allow group ${local.cost_group_names} to manage usage-budgets in tenancy" + ] : [] + + security_admin_grants_on_root_cmp = contains(keys(local.group_name_map_transpose),local.security_role) ? [ + "allow group ${local.security_group_names} to manage cloudevents-rules in tenancy", + "allow group ${local.security_group_names} to manage cloud-guard-family in tenancy", + "allow group ${local.security_group_names} to read tenancies in tenancy" + #"allow group ${local.security_group_names} to read objectstorage-namespaces in tenancy" + ] : [] + + objectstorage_read_grantees = compact( + concat(contains(keys(local.group_name_map_transpose),local.network_role) ? [local.network_group_names] : [], + contains(keys(local.group_name_map_transpose),local.security_role) ? [local.security_group_names] : [], + contains(keys(local.group_name_map_transpose),local.application_role) ? [local.application_group_names] : [], + contains(keys(local.group_name_map_transpose),local.database_role) ? [local.database_group_names] : [], + contains(keys(local.group_name_map_transpose),local.exainfra_role) ? [local.exainfra_group_names] : []) + ) + objectstorage_read_on_root_cmp = coalescelist(local.objectstorage_read_grantees,[1]) != [1] ? ["allow group ${join(",",local.objectstorage_read_grantees)} to read objectstorage-namespaces in tenancy"] : [] + + # For the case when there's no enclosing compartment defined, the grants are set in the root compartment. Analogous grants are present in enclosing_cmp_policy.tf, which are applied when an enclosing compartment is defined. + security_admin_grants_on_enclosing_cmp = contains(keys(local.group_name_map_transpose),local.security_role) && !contains(local.cmp_type_list,"enclosing") ? [ + "allow group ${local.security_group_names} to manage tag-namespaces in tenancy", + "allow group ${local.security_group_names} to manage tag-defaults in tenancy", + "allow group ${local.security_group_names} to manage repos in tenancy", + "allow group ${local.security_group_names} to read audit-events in tenancy", + "allow group ${local.security_group_names} to read app-catalog-listing in tenancy", + "allow group ${local.security_group_names} to read instance-images in tenancy", + "allow group ${local.security_group_names} to inspect buckets in tenancy" + ] : [] + + # For the case when there's no enclosing compartment defined, the grants are set in the root compartment. Analogous grants are present in enclosing_cmp_policy.tf, which are applied when an enclosing compartment is defined. + application_admin_grants_on_enclosing_cmp = contains(keys(local.group_name_map_transpose),local.application_role) && !contains(local.cmp_type_list,"enclosing") ? [ + "allow group ${local.application_group_names} to read app-catalog-listing in tenancy", + "allow group ${local.application_group_names} to read instance-images in tenancy", + "allow group ${local.application_group_names} to read repos in tenancy" + ] : [] + + auditor_grants = contains(keys(local.group_name_map_transpose),local.auditor_role) ? [ + "allow group ${local.auditor_group_names} to inspect all-resources in tenancy", + "allow group ${local.auditor_group_names} to read instances in tenancy", + "allow group ${local.auditor_group_names} to read load-balancers in tenancy", + "allow group ${local.auditor_group_names} to read buckets in tenancy", + "allow group ${local.auditor_group_names} to read nat-gateways in tenancy", + "allow group ${local.auditor_group_names} to read public-ips in tenancy", + "allow group ${local.auditor_group_names} to read file-family in tenancy", + "allow group ${local.auditor_group_names} to read instance-configurations in tenancy", + "allow group ${local.auditor_group_names} to read network-security-groups in tenancy", + "allow group ${local.auditor_group_names} to read resource-availability in tenancy", + "allow group ${local.auditor_group_names} to read audit-events in tenancy", + "allow group ${local.auditor_group_names} to read users in tenancy", + "allow group ${local.auditor_group_names} to use cloud-shell in tenancy", + "allow group ${local.auditor_group_names} to read vss-family in tenancy", + "allow group ${local.auditor_group_names} to read usage-budgets in tenancy", + "allow group ${local.auditor_group_names} to read usage-reports in tenancy", + "allow group ${local.auditor_group_names} to read data-safe-family in tenancy", + "allow group ${local.auditor_group_names} to read vaults in tenancy", + "allow group ${local.auditor_group_names} to read keys in tenancy", + "allow group ${local.auditor_group_names} to read tag-namespaces in tenancy", + "allow group ${local.auditor_group_names} to use ons-family in tenancy where any {request.operation!=/Create*/, request.operation!=/Update*/, request.operation!=/Delete*/, request.operation!=/Change*/}" + ] : [] + + announcement_reader_grants = contains(keys(local.group_name_map_transpose),local.announcement_reader_role) ? [ + "allow group ${local.announcement_reader_group_names} to read announcements in tenancy" + ] : [] + + root_cmp_admin_grants = concat(local.cost_admin_grants_on_root_cmp,local.iam_admin_grants_on_root_cmp, + local.iam_admin_grants_on_enclosing_cmp,local.cred_admin_grants_on_root_cmp, + local.security_admin_grants_on_root_cmp,local.security_admin_grants_on_enclosing_cmp) + + root_cmp_nonadmin_grants = concat(local.basic_grants_on_root_cmp,local.application_admin_grants_on_enclosing_cmp, + local.auditor_grants,local.announcement_reader_grants, local.objectstorage_read_on_root_cmp) + + #-- Policies + #root_policy_name_prefix = local.enable_tenancy_level_template_policies == true ? (var.policies_configuration.template_policies.tenancy_level_settings.policy_name_prefix != null ? "${var.policies_configuration.template_policies.tenancy_level_settings.policy_name_prefix}-" : "") : "" + #-- Naming + root_cmp_admin_policy_key = "ROOT-CMP-ADMIN-POLICY" + #root_cmp_admin_policy_name = "${local.root_policy_name_prefix}root-admin${local.policy_name_suffix}" + root_cmp_admin_policy_name = "${local.policy_name_prefix}root-admin${local.policy_name_suffix}" + + root_cmp_admin_policy = length(local.root_cmp_admin_grants) > 0 ? { + (local.root_cmp_admin_policy_key) = { + name = local.root_cmp_admin_policy_name + compartment_id = var.tenancy_ocid + description = "CIS Landing Zone root policy for admin groups." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = local.root_cmp_admin_grants + } + } : {} + + #-- Naming + root_cmp_nonadmin_policy_key = "ROOT-CMP-NONADMIN-POLICY" + #root_cmp_nonadmin_policy_name = "${local.root_policy_name_prefix}root-non-admin${local.policy_name_suffix}" + root_cmp_nonadmin_policy_name = "${local.policy_name_prefix}root-non-admin${local.policy_name_suffix}" + + root_cmp_nonadmin_policy = length(local.root_cmp_nonadmin_grants) > 0 ? { + (local.root_cmp_nonadmin_policy_key) = { + name = local.root_cmp_nonadmin_policy_name + compartment_id = var.tenancy_ocid + description = "CIS Landing Zone root policy for non-admin groups." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = local.root_cmp_nonadmin_grants + } + } : {} +} diff --git a/modules/oci-identity-policies/policies/security_cmp_policy.tf b/modules/oci-identity-policies/policies/security_cmp_policy.tf new file mode 100644 index 0000000..bb37619 --- /dev/null +++ b/modules/oci-identity-policies/policies/security_cmp_policy.tf @@ -0,0 +1,103 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +locals { + #-------------------------------------------------------------------------------------------- + #-- Security compartments policies + #-------------------------------------------------------------------------------------------- + + #-- Security read-only grants on Security compartment + security_read_grants_on_security_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"security") && values["read-group"] != null) ? [ + "allow group ${values["read-group"]} to read all-resources in compartment ${values["name"]}" + ] : [] + } + + #-- Security admin grants on Security compartment + security_admin_grants_on_security_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"security") && values["sec-group"] != null) ? [ + "allow group ${values["sec-group"]} to read all-resources in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage instance-family in compartment ${values["name"]}", + # CIS 1.2 - 1.14 Level 2 + "allow group ${values["sec-group"]} to manage volume-family in compartment ${values["name"]} where all{request.permission != 'VOLUME_BACKUP_DELETE', request.permission != 'VOLUME_DELETE', request.permission != 'BOOT_VOLUME_BACKUP_DELETE'}", + "allow group ${values["sec-group"]} to manage object-family in compartment ${values["name"]} where all{request.permission != 'OBJECT_DELETE', request.permission != 'BUCKET_DELETE'}", + "allow group ${values["sec-group"]} to manage file-family in compartment ${values["name"]} where all{request.permission != 'FILE_SYSTEM_DELETE', request.permission != 'MOUNT_TARGET_DELETE', request.permission != 'EXPORT_SET_DELETE', request.permission != 'FILE_SYSTEM_DELETE_SNAPSHOT', request.permission != 'FILE_SYSTEM_NFSv3_UNEXPORT'}", + "allow group ${values["sec-group"]} to manage vaults in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage keys in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage secret-family in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage logging-family in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage serviceconnectors in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage streams in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage ons-family in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage functions-family in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage waas-family in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage security-zone in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage orm-stacks in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage orm-jobs in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage orm-config-source-providers in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage vss-family in compartment ${values["name"]}", + #"allow group ${values["sec-group"]} to read work-requests in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage bastion-family in compartment ${values["name"]}", + #"allow group ${values["sec-group"]} to read instance-agent-plugins in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage cloudevents-rules in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage alarms in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to manage metrics in compartment ${values["name"]}", + "allow group ${values["sec-group"]} to use key-delegate in compartment ${values["name"]}" + ] : [] + } + + #-- Non security admins + common_groups_on_security_cmp = { + for k, values in local.cmp_name_to_cislz_tag_map : k => compact([values["net-group"] != null ? "${values["net-group"]}" : "", values["app-group"] != null ? "${values["app-group"]}" : "", values["db-group"] != null ? "${values["db-group"]}" : "", values["exa-group"] != null ? "${values["exa-group"]}" : ""]) + if contains(split(",",values["cmp-type"]),"security")} + + #-- Common grants on Security compartment to non security admins + common_grants_on_security_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"security")) ? [ + "allow group ${join(",",local.common_groups_on_security_cmp[k])} to read vss-family in compartment ${values["name"]}", + "allow group ${join(",",local.common_groups_on_security_cmp[k])} to use vaults in compartment ${values["name"]}", + "allow group ${join(",",local.common_groups_on_security_cmp[k])} to use bastion in compartment ${values["name"]}", + "allow group ${join(",",local.common_groups_on_security_cmp[k])} to manage bastion-session in compartment ${values["name"]}", + "allow group ${join(",",local.common_groups_on_security_cmp[k])} to read logging-family in compartment ${values["name"]}" + ] : [] + } + + #-- Storage admin grants on Security compartment + storage_admin_grants_on_security_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"security") && values["stg-group"] != null) ? [ + # Object Storage + "allow group ${values["stg-group"]} to read bucket in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to inspect object in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage object-family in compartment ${values["name"]} where any {request.permission = 'OBJECT_DELETE', request.permission = 'BUCKET_DELETE'}", + # Volume Storage + "allow group ${values["stg-group"]} to read volume-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage volume-family in compartment ${values["name"]} where any {request.permission = 'VOLUME_DELETE', request.permission = 'VOLUME_BACKUP_DELETE', request.permission = 'BOOT_VOLUME_BACKUP_DELETE'}", + # File Storage + "allow group ${values["stg-group"]} to read file-family in compartment ${values["name"]}", + "allow group ${values["stg-group"]} to manage file-family in compartment ${values["name"]} where any {request.permission = 'FILE_SYSTEM_DELETE', request.permission = 'MOUNT_TARGET_DELETE', request.permission = 'EXPORT_SET_UPDATE', request.permission = 'FILE_SYSTEM_NFSv3_UNEXPORT', request.permission = 'EXPORT_SET_DELETE', request.permission = 'FILE_SYSTEM_DELETE_SNAPSHOT'}" + ] : [] + } + + #-- Database grants on Security compartment + database_kms_grants_on_security_cmp_map = { + for k, values in local.cmp_name_to_cislz_tag_map : k => (contains(split(",",values["cmp-type"]),"security") && values["db-dyn-group"] != null) ? [ + "allow dynamic-group ${values["db-dyn-group"]} to use vaults in compartment ${values["name"]}" + ] : [] + } + + #-- Policies for compartments marked as security compartments (values["cmp-type"] == "security"). + security_cmps_policies = { + for k, values in local.cmp_name_to_cislz_tag_map : + (upper("${k}-security-policy")) => { + name = length(regexall("^${local.policy_name_prefix}", values["name"])) > 0 ? (length(split(",",values["cmp-type"])) > 1 ? "${values["name"]}-security${local.policy_name_suffix}" : "${values["name"]}${local.policy_name_suffix}") : (length(split(",",values["cmp-type"])) > 1 ? "${local.policy_name_prefix}${values["name"]}-security${local.policy_name_suffix}" : "${local.policy_name_prefix}${values["name"]}${local.policy_name_suffix}") + compartment_id = values.ocid + description = "CIS Landing Zone policy for Security compartment." + defined_tags = var.policies_configuration.defined_tags + freeform_tags = var.policies_configuration.freeform_tags + statements = concat(local.security_read_grants_on_security_cmp_map[k],local.security_admin_grants_on_security_cmp_map[k], + local.common_grants_on_security_cmp_map[k], + local.storage_admin_grants_on_security_cmp_map[k],local.database_kms_grants_on_security_cmp_map[k]) + } + if contains(split(",",values["cmp-type"]),"security") + } +} diff --git a/modules/oci-identity-policies/policies/services_policy.tf b/modules/oci-identity-policies/policies/services_policy.tf new file mode 100644 index 0000000..373d32d --- /dev/null +++ b/modules/oci-identity-policies/policies/services_policy.tf @@ -0,0 +1,61 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +data "oci_identity_tenancy" "this" { + tenancy_id = var.tenancy_ocid +} + +data "oci_identity_region_subscriptions" "these" { + tenancy_id = var.tenancy_ocid +} + +locals { + cloud_guard_statements = local.enable_cloud_guard_service_policies == true ? [ + "Allow service cloudguard to read all-resources in tenancy", + "Allow service cloudguard to use network-security-groups in tenancy" + ] : [] + + vss_statements = local.enable_scanning_service_policies == true ? [ + "Allow service vulnerability-scanning-service to manage instances in tenancy", + "Allow service vulnerability-scanning-service to read compartments in tenancy", + "Allow service vulnerability-scanning-service to read repos in tenancy", + "Allow service vulnerability-scanning-service to read vnics in tenancy", + "Allow service vulnerability-scanning-service to read vnic-attachments in tenancy" + ] : [] + + os_mgmt_statements = local.enable_os_management_service_policies == true ? [ + "Allow service osms to read instances in tenancy" + ] : [] + + # The name of the File Storage service user depends on your realm . + # For realms with realm key numbers of 10 or less, the pattern for the File Storage service user is FssOcProd, where n is the realm key number. + # Realms with a realm key number greater than 10 have a service user of fssocprod. + # https://docs.oracle.com/en-us/iaas/Content/File/Tasks/encrypt-file-system.htm + realm = split(".",trimprefix(data.oci_identity_tenancy.this.id, "ocid1.tenancy."))[0] + fss_principal_name = substr(local.realm,2,10) <= 10 ? "Fss${local.realm}Prod" : "fssocprod" + + object_storage_service_principals = join(",", [for region in data.oci_identity_region_subscriptions.these.region_subscriptions : "objectstorage-${region.region_name}"]) + + keys_access_principals = join(",",compact([local.enable_block_storage_service_policies == true ? "blockstorage" : null, + local.enable_oke_service_policies == true ? "oke" : null, + local.enable_streaming_service_policies == true ? "streaming" : null, + local.enable_file_storage_service_policies == true ? local.fss_principal_name : null, + local.enable_object_storage_service_policies == true ? local.object_storage_service_principals : null])) + + keys_access_statements = length(local.keys_access_principals) > 0 ? ["Allow service ${local.keys_access_principals} to use keys in tenancy"] : [] + + services_policy_key = "SERVICES-POLICY" + #services_policy_name = "${local.root_policy_name_prefix}services${local.policy_name_suffix}" + services_policy_name = "${local.policy_name_prefix}services${local.policy_name_suffix}" + + services_policy = { + (local.services_policy_key) = { + compartment_id = var.tenancy_ocid + name = local.services_policy_name + description = "CIS Landing Zone policy for OCI services." + statements = concat(local.cloud_guard_statements, local.vss_statements, local.os_mgmt_statements, local.keys_access_statements) + defined_tags = var.policies_configuration != null ? var.policies_configuration.defined_tags : null + freeform_tags = var.policies_configuration != null ? var.policies_configuration.freeform_tags : null + } + } +} \ No newline at end of file diff --git a/modules/oci-identity-policies/policies/variables.tf b/modules/oci-identity-policies/policies/variables.tf new file mode 100644 index 0000000..617b0f2 --- /dev/null +++ b/modules/oci-identity-policies/policies/variables.tf @@ -0,0 +1,102 @@ +# Copyright (c) 2023 Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +variable "tenancy_ocid" { + description = "The tenancy OCID." + type = string +} + +variable "policies_configuration" { + description = "Policies configuration" + type = object({ + enable_cis_benchmark_checks = optional(bool) # Whether to check policies for CIS Foundations Benchmark recommendations. Default is true. + supplied_policies = optional(map(object({ # A map of directly supplied policies. Use this to suplement or override the template policies. + name = string + description = string + compartment_id = string + statements = list(string) + defined_tags = optional(map(string)) + freeform_tags = optional(map(string)) + }))) + template_policies = optional(object({ # An object describing the template policies. In this mode, policies are derived according to tenancy_level_settings and compartment_level_settings. + tenancy_level_settings = optional(object({ # Settings for tenancy level (Root compartment) policies generation. + groups_with_tenancy_level_roles = optional(list(object({ # A list of group names and their roles at the tenancy level. Template policies are granted to each group in the Root compartment. + name = string + roles = string + }))) + oci_services = optional(object({ + enable_all_policies = optional(bool) + enable_scanning_policies = optional(bool) + enable_cloud_guard_policies = optional(bool) + enable_os_management_policies = optional(bool) + enable_block_storage_policies = optional(bool) + enable_file_storage_policies = optional(bool) + enable_oke_policies = optional(bool) + enable_streaming_policies = optional(bool) + enable_object_storage_policies = optional(bool) + })) + policy_name_prefix = optional(string) # A prefix to Root compartment policy names. + })) + compartment_level_settings = optional(object({ # Settings for compartment (non Root) level policies generation. + supplied_compartments = optional(map(object({ # List of compartments that are policy targets. + name = string # The compartment name + id = string # The compartment id + cislz_metadata = map(string) # The compartment metadata. See module README.md for details. + }))) + #policy_name_prefix = optional(string) # A prefix to compartment policy names. + })) + })) + policy_name_prefix = optional(string) # A prefix to all policy names. + policy_name_suffix = optional(string) # A suffix to all policy names. + defined_tags = optional(map(string)) # Any defined tags to apply on the template (pre-configured) policies. + freeform_tags = optional(map(string)) # Any freeform tags to apply on the template (pre-configured) policies. + }) + default = null +} + +#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +#-- Note about supplied_compartments attribute: +#-- The original ideia was having the module looking up compartments obtained from a data source internal to the module. But that introduces an issue to the processing logic, as +#-- Terraform requires compartments to be known at plan time, because compartment names are used as map keys by the module. +#-- The error is: +#-- +#-- Error: Invalid for_each argument +#--│ +#--│ on .terraform\modules\cislz_policies\main.tf line 23, in resource "oci_identity_policy" "these": +#--│ 23: for_each = {for k, v in local.policies : k => v if length(v.statements) > 0} +#--│ ├──────────────── +#--││ │ local.policies will be known only after apply +#--││ +#--││ The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, +#--|| and so Terraform cannot determine the full set of keys that will identify the instances of this resource. +#--││ +#--││ When working with unknown values in for_each, it's better to define the map keys statically in your configuration and place apply-time results only in the map values. +#--││ +#--││ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge. +#-- + +variable enable_output { + description = "Whether Terraform should enable module output." + type = bool + default = true +} + +variable enable_debug { + description = "Whether Terraform should enable module debug information." + type = bool + default = false +} + +variable module_name { + description = "The module name." + type = string + default = "iam-policies" +} + +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(object({ + id = string + })) + default = null +} \ No newline at end of file diff --git a/modules/oci-identity-policies/variables.tf b/modules/oci-identity-policies/variables.tf new file mode 100644 index 0000000..b67f549 --- /dev/null +++ b/modules/oci-identity-policies/variables.tf @@ -0,0 +1,76 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "config_file_profile" { + description = "The profile name if you would like to use a custom profile in the OCI config file to provide the authentication credentials. See Using the SDK and CLI Configuration File for more information, Refer: https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm#terraformproviderconfiguration_topic-SDK_and_CLI_Config_File" + type = string +} + +variable "region" { + description = "OCI region " + type = string +} + +variable "compartment_ocid" { + description = "for root compartment pass tenancy_ocid , else pass compartment_ocid" + type = string +} + +variable "tenancy_id" { + description = "Tenancy ocid" + type = string +} + +variable "domain_name" { + description = "Name of existing domain where groups would be created. It would be used if identity_domain_configuration is not present" + type = string + default = "Default" +} + +variable "google_project_number" { + description = "The linked GCP project used to limit access to existing groups and policies" + type = string + default = null +} + +variable "identity_domain_name" { + description = "Name of existing domain where groups would be created. It would be used if identity_domain_configuration is not present" + type = string + default = "Default" +} + +variable "identity_domain_configuration" { + description = "The identity domain configuration." + type = object({ + compartment_id = optional(string), + display_name = string, + description = string, + home_region = optional(string), + license_type = string, + admin_email = optional(string), + admin_first_name = optional(string), + admin_last_name = optional(string), + admin_user_name = optional(string), + is_hidden_on_login = optional(bool), + is_notification_bypassed = optional(bool), + is_primary_email_required = optional(bool), + defined_tags = optional(map(string)), + freeform_tags = optional(map(string)) + }) + default = null +} + +variable "identity_domain_groups_configuration" { + description = "The identity domain groups configuration." + type = object({ + default_defined_tags = optional(map(string)) + default_freeform_tags = optional(map(string)) + groups = map(object({ + name = string, + description = optional(string), + requestable = optional(bool), + members = optional(list(string)), + defined_tags = optional(map(string)), + freeform_tags = optional(map(string)) + })) + }) + default = null +} \ No newline at end of file diff --git a/modules/oci-identity/README.md b/modules/oci-identity/README.md new file mode 100644 index 0000000..4cd5960 --- /dev/null +++ b/modules/oci-identity/README.md @@ -0,0 +1,77 @@ +# GCP Identity - Federation +## Introduction +Setup Identity Providers for ODB@G service. + +## Providers + +| Name | Version | +|---------------------------------------------------------------------|----------| +| [oci](https://registry.terraform.io/providers/hashicorp/oci/latest) | ~> 5.0.0 | + + +## Inputs Variables +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:--------------------------|:-------------------------------------------------------------------------:|:--------:|--------------:|--------------------------------------:| +| `config_file_profile` | Authorization profile used by the OCI provider | NO | "DEFAULT" | | +| `region` | OCI region. | YES | | "us-ashburn-1" | +| `compartment_ocid` | OCI compartment for the new provider. It can be a tenancy id. | YES | | ""ocid1.tenancy.oc1..xxxxxxxxxxxxx"" | +| `gcp_federation_xml_url` | URL to download the GCP metadata xml file from. | NO | "" | "https://somedomain.com/metadata.xml" | +| `gcp_federation_xml_file` | Local file for the GCP metadata xml file. | NO | "" | "//local_path/metadata.xml" | +| `idp_name` | Identity provider name. | NO | "Google" | | +| `idp_description` | Identity provider description. | NO | "" | | +| `domain_display_name` | Identity domain linked to the identity provider. | NO | "Default" | | +| `group_prefix` | Group prefix as referred on the [RBAC](../gcp-identity/README.md) module. | NO | "" | "org_prefix" | + +# Setup OCI Federation +Setting up Federation configuration on OCI. + +### Setting param value + +The following input tfvars _must_ be set + +Either as `terraform.tfvars` file in same directory + +``` +config_file_profile="" +compartment_ocid="" +region="" +``` + +### Authentication +``` +# authenticate OCI CLI for execution on a local workstation +oci session authenticate +``` + +### Initialize +``` +$ terraform init +``` +### Apply + +First apply [oci-identity-domain](oci-identity-domain/README.md) module, the output of the module would be used for GCP configuration and for later steps of Federation configuration. + +``` +$ cd oci-identitity-domain +$ terraform init +$ terraform apply -var="compartment_ocid=" -var="region=" +$ cd .. +``` + +Output would include some values used to configure GCP Federation. The `acs_url` and `provider_id` ID would be needed for the identity +configuration, `next_steps` describes the steps needed to complete the Google side configuration. + +During the GCP Federation configuration, get the metadata file URL or download the file to your local environment, this file would be used +for OCI configuration. + +To configure OCI Identity Provider with default group names using a local metadata file: + +``` +$ tf apply -var="gcp_federation_xml_file=/path/metadata.xml" +``` + +To configure OCI Identity Provider with groups created using a prefix and a URL metadata file: + +``` +$ tf apply -var="gcp_federation_xml_url=" -var="group_prefix=" +``` \ No newline at end of file diff --git a/modules/oci-identity/main.tf b/modules/oci-identity/main.tf new file mode 100644 index 0000000..0b66d44 --- /dev/null +++ b/modules/oci-identity/main.tf @@ -0,0 +1,24 @@ +module "oci-identity-provider" { + source = "./oci-identity-provider" + config_file_profile = var.config_file_profile + region = var.region + compartment_ocid = var.compartment_ocid + gcp_federation_xml_file = var.gcp_federation_xml_file + gcp_federation_xml_url = var.gcp_federation_xml_url + + idp_name = var.idp_name + idp_description = var.idp_description + default_rule_id = var.default_rule_id + domain_display_name = var.domain_display_name + + jit_groups = [] + idp_group_mapping = { + "${var.group_prefix}odbg_db_family_administrators" : "odbg-db-family-administrators" + "${var.group_prefix}odbg_db_family_readers" : "odbg-db-family-readers" + "${var.group_prefix}odbg_vm_cluster_administrators" : "odbg-vm-cluster-administrators" + "${var.group_prefix}odbg_exa_infra_administrators" : "odbg-exa-infra-administrators" + "${var.group_prefix}odbg_exa_cdb_administrators" : "odbg-exa-cdb-administrators" + "${var.group_prefix}odbg_exa_pdb_administrators" : "odbg-exa-pdb-administrators" + "${var.group_prefix}odbg_adbs_db_administrators" : "odbg-adbs-db-administrators" + } +} \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-domain/README.md b/modules/oci-identity/oci-identity-domain/README.md new file mode 100644 index 0000000..18103e9 --- /dev/null +++ b/modules/oci-identity/oci-identity-domain/README.md @@ -0,0 +1,80 @@ +# Terraform Template to setup SSO Federation between OCI & GCP + +Terraform module to fetch service provider metadata url and enable global access i.e. without authorization. This module performs the first step of the SSO configuration between OCI and Google. + +## Providers + +| Name | Version | +| --------------------------------------------------------------------- | ------- | +| [OCI](https://registry.terraform.io/providers/oracle/oci/latest/docs) | n/a | + +## Inputs Variables + +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +| :---------------------- | :-------------------------------------------------: | :------: |-----------------------:| ---------------------------------: | +| `config_file_profile` | OCI CLI profile name | Yes | | "ONBOARDING" | +| `compartment_ocid` | Tenancy OCID | Yes | | "ocid1.tenancy.oc1..xxxxxxxxxxxxx" | +| `region` | OCI region Identifier | Yes | | "us-ashburn-1" | +| `domain_display_name` | OCI Identify Domain Name | No | "Default" | | + +## Output Values + +| VARIABLE | DESCRIPTION | SAMPLE VALUE | +|:--------------------------|:-------------------------------------:|:---------------------------------------------------------------------:| +| `domain_url` | Identity domains url | https://idcs-xxxxxxxxx.identity.pint.oracle.com:443 | +| `domain_metadata_xml_url` | Identity domains metadata xml url | https://idcs-xxxxxxxxx.identity.pint.oc9qadev.com:443/fed/v1/metadata | +| `provider_id` | Provider ID / Entity ID | https://idcs-xxxxxxxxx.identity.pint.oc9qadev.com:443/fed | +| `acs_url` | ACS (Assertion consumer service) URL | https://idcs-xxxxxxxxx.identity.pint.oc9qadev.com:443/fed/v1/sp/sso | +| `next_steps` | Next steps after domain being enabled | | + +### Setting param value + +The following input tfvars _must_ be set + +Either as `terraform.tfvars` file in same directory + +``` +config_file_profile="" +compartment_ocid="" +region="" +``` + +Or running as command line parameter + +``` +terraform apply -var="config_file_profile=ONBOARDING" -var='compartment_ocid=ocid1.tenancy.oc1..xxxxxxxxxxxxx' -var='region=us-ashburn-1' +``` + +### Authentication + +``` +# authenticate OCI cli +oci session authenticate --region= +``` + +### Execution + +Assuming authentication is successful, and parameters tfvars are in same directory. +Initialize Terraform from `templates/gcp-oci-sso-federation` + +``` +terraform init +``` + +Run Terraform Plan from `templates/gcp-oci-sso-federation` to check the resources that will be created + +``` +terraform plan +``` + +Run Terraform Apply from `templates/gcp-oci-sso-federation` + +``` +terraform apply +``` + +## Troubleshooting + +### Known Issues: + +NA \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-domain/main.tf b/modules/oci-identity/oci-identity-domain/main.tf new file mode 100644 index 0000000..25e424f --- /dev/null +++ b/modules/oci-identity/oci-identity-domain/main.tf @@ -0,0 +1,58 @@ +terraform { + required_providers { + oci = { + source = "hashicorp/oci" + version = ">= 5.0.0" + } + } +} + +# uncomment below for local module run -- start +# provider "oci" { +# auth = "SecurityToken" +# config_file_profile = var.config_file_profile +# region = var.region +# } +# uncomment -- end + +# https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_domains +data "oci_identity_domains" "list_domain" { + compartment_id = var.compartment_ocid + display_name = var.domain_display_name +} + +locals { + oci_domain_url = data.oci_identity_domains.list_domain.domains[0].url +} + + + +# https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_domains_setting +data "oci_identity_domains_setting" "domain_setting" { + #Required + idcs_endpoint = local.oci_domain_url + setting_id = "Settings" + + #Optional + attribute_sets = ["all"] +} + + +# https://registry.terraform.io/providers/oracle/oci/5.40.0/docs/resources/identity_domains_setting +resource "oci_identity_domains_setting" "domain_setting" { + #Required + csr_access = "readWrite" + idcs_endpoint = local.oci_domain_url + schemas = ["urn:ietf:params:scim:schemas:oracle:idcs:Settings"] + setting_id = "Settings" + + # Optional + attribute_sets = ["all"] + signing_cert_public_access = true + # to counter auto-updates + contact_emails = data.oci_identity_domains_setting.domain_setting.contact_emails + custom_branding = data.oci_identity_domains_setting.domain_setting.custom_branding + locale = data.oci_identity_domains_setting.domain_setting.locale + service_admin_cannot_list_other_users = data.oci_identity_domains_setting.domain_setting.service_admin_cannot_list_other_users + timezone = data.oci_identity_domains_setting.domain_setting.timezone +} diff --git a/modules/oci-identity/oci-identity-domain/output.tf b/modules/oci-identity/oci-identity-domain/output.tf new file mode 100644 index 0000000..264a2fc --- /dev/null +++ b/modules/oci-identity/oci-identity-domain/output.tf @@ -0,0 +1,21 @@ + +output "domain_url" { + value = local.oci_domain_url +} + +output "provider_id" { + value = "${local.oci_domain_url}/fed" +} + +output "acs_url" { + value = "${local.oci_domain_url}/fed/v1/sp/sso" +} + +output "domain_metadata_xml_url" { + value = "${local.oci_domain_url}/fed/v1/metadata" +} + +# TODO: URL for instructions on GCP configuration needs to be used instead of Azure instructions. +output "next_steps" { + value = "For next steps see CHANGE_TO_GCP! https://docs.oracle.com/en-us/iaas/Content/Identity/tutorials/azure_ad/sso_azure/azure_sso.htm CHANGE_TO_GCP" +} \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-domain/variables.tf b/modules/oci-identity/oci-identity-domain/variables.tf new file mode 100644 index 0000000..900a8c5 --- /dev/null +++ b/modules/oci-identity/oci-identity-domain/variables.tf @@ -0,0 +1,21 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "config_file_profile" { + description = "The profile name if you would like to use a custom profile in the OCI config file to provide the authentication credentials. See Using the SDK and CLI Configuration File for more information, Refer: https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm#terraformproviderconfiguration_topic-SDK_and_CLI_Config_File" + type = string +} + +variable "region" { + description = "OCI region " + type = string +} + +variable "compartment_ocid" { + description = "for root compartment pass tenancy_ocid , else pass compartment_ocid" + type = string +} + +variable "domain_display_name" { + description = "Use default value unless using non default domain" + type = string + default = "Default" +} \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-provider-group/README.md b/modules/oci-identity/oci-identity-provider-group/README.md new file mode 100644 index 0000000..46088ca --- /dev/null +++ b/modules/oci-identity/oci-identity-provider-group/README.md @@ -0,0 +1,28 @@ +# OCI Identity - Federation +## Introduction +Support module. Search groups inside an identity domain. This is used by other modules when +setting up identity provider configuration. + +## Providers + +| Name | Version | +|---------------------------------------------------------------------|----------| +| [oci](https://registry.terraform.io/providers/hashicorp/oci/latest) | ~> 5.0.0 | + + +## Inputs Variables +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:-------------------------|:--------------------------------------------------------------:|:--------:|--------------:|----------------------------------------------------:| +| `oci_domain_url` | URL of the identity domain. | YES | "" | https://idcs-xxxxxxxxx.identity.pint.oracle.com:443 | +| `group_name` | Name of the group to search for. | YES | "" | "Sample Group Name" | +| `fail_on_missing_group` | Indicates if a missing group should fail the module execution. | NO | true | false | + + +## Output Variables +| VARIABLE | DESCRIPTION | SAMPLE VALUE | +|:------------|:------------------------------------------------------------|:-------------| +| `group_id` | The ID of the group in the context of the identity domain. | xxxxxxxx | + +### Apply + +See [parent project](../README.md) for apply details. \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-provider-group/main.tf b/modules/oci-identity/oci-identity-provider-group/main.tf new file mode 100644 index 0000000..c974107 --- /dev/null +++ b/modules/oci-identity/oci-identity-provider-group/main.tf @@ -0,0 +1,39 @@ +terraform { + required_providers { + oci = { + source = "hashicorp/oci" + version = ">= 5.0.0" # -- for local module run + } + } +} + +# uncomment below for local module run -- start +# provider "oci" { +# auth = "SecurityToken" +# config_file_profile = var.config_file_profile +# region = var.region +# } +# uncomment -- end + +locals { + oci_domain_url = var.oci_domain_url + group_name = var.group_name + fail_on_missing_group = var.fail_on_missing_group +} + +data "oci_identity_domains_groups" "identity_groups" { + idcs_endpoint = local.oci_domain_url + group_filter = "displayName eq \"${local.group_name}\"" + attributes = "id" + + lifecycle { + postcondition { + condition = length(self.groups) == 1 || !local.fail_on_missing_group + error_message = "Unable to find group with filter '${local.group_name}'" + } + } +} + +output "group_id" { + value = length(data.oci_identity_domains_groups.identity_groups.groups) > 0 ? data.oci_identity_domains_groups.identity_groups.groups[0].id : "" +} \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-provider-group/variables.tf b/modules/oci-identity/oci-identity-provider-group/variables.tf new file mode 100644 index 0000000..b4ff6d8 --- /dev/null +++ b/modules/oci-identity/oci-identity-provider-group/variables.tf @@ -0,0 +1,16 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "oci_domain_url" { + description = "this will be output of module oci-identity-domain" + type = string +} + +variable "group_name" { + type = string + description = "Group name prefix in GCP" +} + +variable "fail_on_missing_group" { + type = bool + default = true + description = "Fail the execution if the given group name is not found." +} \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-provider/README.md b/modules/oci-identity/oci-identity-provider/README.md new file mode 100644 index 0000000..1c00a1e --- /dev/null +++ b/modules/oci-identity/oci-identity-provider/README.md @@ -0,0 +1,47 @@ +# GCP Identity - Federation +## Introduction +Setup Identity Providers for ODB@G service. + +## Providers + +| Name | Version | +|---------------------------------------------------------------------|----------| +| [oci](https://registry.terraform.io/providers/hashicorp/oci/latest) | ~> 5.0.0 | + + +## Inputs Variables +| VARIABLE | DESCRIPTION | REQUIRED | DEFAULT_VALUE | SAMPLE VALUE | +|:--------------------------|:-------------------------------------------------------------:|:--------:|--------------:|--------------------------------------:| +| `config_file_profile` | Authorization profile used by the OCI provider | NO | "DEFAULT" | | +| `region` | OCI region. | YES | | "us-ashburn-1" | +| `compartment_ocid` | OCI compartment for the new provider. It can be a tenancy id. | YES | | ""ocid1.tenancy.oc1..xxxxxxxxxxxxx"" | +| `gcp_federation_xml_url` | URL to download the GCP metadata xml file from. | NO | "" | "https://somedomain.com/metadata.xml" | +| `gcp_federation_xml_file` | Local file for the GCP metadata xml file. | NO | "" | "//local_path/metadata.xml" | +| `idp_name` | Identity provider name. | NO | "Google" | | +| `idp_description` | Identity provider description. | NO | "" | | +| `domain_display_name` | Identity domain linked to the identity provider. | NO | "Default" | | +| `jit_groups` | List of groups to be assigned to new JIT users. | NO | [] | ["odbag-adbs-db-users"] | +| `group_prefix` | Group prefix to add to each jit_groups element. | NO | "" | "org_prefix" | +| `idp_group_mapping` | Mapping between IDP and domain groups. | NO | {} | {"idp-group-1":"odbag-adbs-db-users"} | + +## Output Variables +| VARIABLE | DESCRIPTION | SAMPLE VALUE | +|:------------------|:------------------------------------------|:-------------| +| `new_saml_idp_id` | The ID of the new SAML Identity Provider. | xxxxxxxxxx | + +# Setup OCI Federation +Setting up Federation configuration on OCI. + +### Authentication +``` +# authenticate OCI CLI for execution on a local workstation +oci session authenticate +``` + +### Initialize +``` +$ terraform init +``` +### Apply + +See [parent project](../README.md) for apply details. \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-provider/main.tf b/modules/oci-identity/oci-identity-provider/main.tf new file mode 100644 index 0000000..205c024 --- /dev/null +++ b/modules/oci-identity/oci-identity-provider/main.tf @@ -0,0 +1,141 @@ +terraform { + required_providers { + oci = { + source = "hashicorp/oci" + version = ">= 5.0.0" # -- for local module run + } + } +} + +# uncomment below for local module run -- start +# provider "oci" { +# auth = "SecurityToken" +# config_file_profile = var.config_file_profile +# region = var.region +# } +# uncomment -- end + +locals { + jit_groups = var.jit_groups + group_prefix = var.group_prefix + idp_group_mapping = var.idp_group_mapping +} + +# https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_domains +data "oci_identity_domains" "list_domain" { + compartment_id = var.compartment_ocid + display_name = var.domain_display_name +} + +locals { + oci_domain_url = data.oci_identity_domains.list_domain.domains[0].url +} + +# Fetch GCP federation metadata url +data "http" "gcp_federation_xmldata_http" { + count = var.gcp_federation_xml_url != "" ? 1 : 0 + url = var.gcp_federation_xml_url +} + +locals { + gcp_federation_xmldata = var.gcp_federation_xml_url != "" ? replace(data.http.gcp_federation_xmldata_http[0].response_body, "\ufeff", "") : file(var.gcp_federation_xml_file) +} + +module "jit_groups" { + source = "../oci-identity-provider-group" + + oci_domain_url = local.oci_domain_url + group_name = "${local.group_prefix}${each.key}" + + for_each = local.jit_groups +} + +module "idp_related_groups" { + source = "../oci-identity-provider-group" + + oci_domain_url = local.oci_domain_url + group_name = each.value + + for_each = local.idp_group_mapping +} + +# Create SAML identity provider +# https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_domains_identity_provider +resource "oci_identity_domains_identity_provider" "new_saml_idp" { + lifecycle { + precondition { + condition = var.gcp_federation_xml_file != "" || var.gcp_federation_xml_url != "" + error_message = "Either gcp_federation_xml_file or gcp_federation_xml_url need to be provided." + } + } + + #Required + enabled = true + idcs_endpoint = local.oci_domain_url + partner_name = var.idp_name + schemas = ["urn:ietf:params:scim:schemas:oracle:idcs:IdentityProvider"] + #Optional but required for our purpose + description = var.idp_description + type = "SAML" + metadata = local.gcp_federation_xmldata + signature_hash_algorithm = "SHA-256" + user_mapping_method = "NameIDToUserAttribute" + user_mapping_store_attribute = "userName" + name_id_format = "saml-emailaddress" + + jit_user_prov_enabled = true + jit_user_prov_create_user_enabled = true + jit_user_prov_attribute_update_enabled = true + jit_user_prov_group_assertion_attribute_enabled = true + jit_user_prov_group_assignment_method = "Overwrite" + jit_user_prov_group_saml_attribute_name = "MemberOf" + jit_user_prov_group_static_list_enabled = true + jit_user_prov_ignore_error_on_absent_groups = true + jit_user_prov_group_mapping_mode = "explicit" + + dynamic "jit_user_prov_assigned_groups" { + for_each = local.jit_groups + content { + value = module.jit_groups[jit_user_prov_assigned_groups.value].group_id + } + } + + dynamic "jit_user_prov_group_mappings" { + for_each = local.idp_group_mapping + content { + idp_group = jit_user_prov_group_mappings.key + value = module.idp_related_groups[jit_user_prov_group_mappings.key].group_id + } + } +} + + +locals { + new_saml_idp_id = oci_identity_domains_identity_provider.new_saml_idp.id +} + +output "new_saml_idp_id" { + value = local.new_saml_idp_id +} + +# Patch identity domain policy rule and MappedAttribute +resource "terraform_data" "patch_identity_domain" { + depends_on = [ + oci_identity_domains_identity_provider.new_saml_idp + ] + + provisioner "local-exec" { + working_dir = path.module + command = "pip3 install -r scripts/requirements.txt" + } + + provisioner "local-exec" { + working_dir = path.module + command = "python3 scripts/patch_identity_domain_rule.py -p '${var.config_file_profile}' -u '${local.oci_domain_url}' -r '${var.default_rule_id}' -i '${local.new_saml_idp_id}'" + } + + provisioner "local-exec" { + working_dir = path.module + command = "python3 scripts/patch_identity_domain_attribute_mapping.py -p '${var.config_file_profile}' -u '${local.oci_domain_url}' -i '${local.new_saml_idp_id}'" + } +} \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-provider/scripts/patch_identity_domain_attribute_mapping.py b/modules/oci-identity/oci-identity-provider/scripts/patch_identity_domain_attribute_mapping.py new file mode 100644 index 0000000..74f0dd9 --- /dev/null +++ b/modules/oci-identity/oci-identity-provider/scripts/patch_identity_domain_attribute_mapping.py @@ -0,0 +1,88 @@ +import argparse + +import oci +import requests + +MAPPED_ATTRIBUTE_VALUE = [ + { + "managedObjectAttributeName": "$(assertion.fed.nameidvalue)", + "idcsAttributeName": "userName" + }, + { + "managedObjectAttributeName": "$(assertion.LastName)", + "idcsAttributeName": "name.familyName" + }, + { + "managedObjectAttributeName": "$(assertion.PrimaryEmail)", + "idcsAttributeName": "emails[type eq \"work\" and primary eq true].value" + }, + { + "managedObjectAttributeName": "$(assertion.FirstName)", + "idcsAttributeName": "name.givenName" + } +] + +PATCH_BODY = { + "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], + "Operations": [ + { + "op": "replace", + "path": "attributeMappings", + "value": MAPPED_ATTRIBUTE_VALUE + } + ] +} + + +# https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/clitoken.htm#Running_Scripts_on_a_Computer_without_a_Browser +def get_signer(config: dict): + token_file = config['security_token_file'] + token = None + with open(token_file, 'r') as f: + token = f.read() + private_key = oci.signer.load_private_key_from_file(config['key_file']) + return oci.auth.signers.SecurityTokenSigner(token, private_key) + + +# https://docs.oracle.com/en-us/iaas/tools/python-sdk-examples/2.129.2/identitydomains/patch_identity_provider.py.html +def add_saml_idp_mapped_attribute(config_file_profile, domain_url, new_saml_idp_id): + config = oci.config.from_file(profile_name=config_file_profile) + signer = 1 + if "security_token_file" in config: + signer = get_signer(config) + else: + # https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm + signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner() + identity_domains_client = oci.identity_domains.IdentityDomainsClient(config, domain_url, signer=signer) + identity_domain = identity_domains_client.get_identity_provider( + identity_provider_id=new_saml_idp_id, + attributes="jitUserProvAttributes" + ) + + # PATCH update rules + try: + url = identity_domain.data.jit_user_prov_attributes.ref + patch_attribute_mappings_response = requests.patch(url, auth=signer, json=PATCH_BODY) + if patch_attribute_mappings_response.status_code == 200: + print("Attribute mapping updated successful!") + else: + print(f"Request failed with status code {patch_attribute_mappings_response.status_code}") + except Exception as e: + print("Attribute mapping update encountered issue, but would have updated!", e.args[0]) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument('-p', '--config_file_profile', default='DEFAULT', + help='OCI auth profile name', + required=False) + parser.add_argument('-u', '--domain_url', + help='domain url eg https://idcs-.identity.oraclecloud.com:443', + required=True) + parser.add_argument('-i', '--saml_idp_id', + help='Identity Provider to set the value for the mapped attributes.', + required=True) + args = parser.parse_args() + + add_saml_idp_mapped_attribute(args.config_file_profile, args.domain_url, args.saml_idp_id) diff --git a/modules/oci-identity/oci-identity-provider/scripts/patch_identity_domain_rule.py b/modules/oci-identity/oci-identity-provider/scripts/patch_identity_domain_rule.py new file mode 100644 index 0000000..6184c47 --- /dev/null +++ b/modules/oci-identity/oci-identity-provider/scripts/patch_identity_domain_rule.py @@ -0,0 +1,69 @@ +import argparse +import oci + + +# https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/clitoken.htm#Running_Scripts_on_a_Computer_without_a_Browser +def get_signer(config: dict): + token_file = config['security_token_file'] + token = None + with open(token_file, 'r') as f: + token = f.read() + private_key = oci.signer.load_private_key_from_file(config['key_file']) + return oci.auth.signers.SecurityTokenSigner(token, private_key) + + +# https://docs.oracle.com/en-us/iaas/tools/python-sdk-examples/2.128.0/identitydomains/get_rule.py.html +def add_saml_idp_to_default_ipd_policy(config_file_profile, domain_url, default_rule_id, new_saml_idp_id): + config = oci.config.from_file(profile_name=config_file_profile) + if "security_token_file" in config: + signer = get_signer(config) + identity_domains_client = oci.identity_domains.IdentityDomainsClient(config, domain_url, signer=signer) + else: + identity_domains_client = oci.identity_domains.IdentityDomainsClient(config, domain_url) + + get_rule_response = identity_domains_client.get_rule(rule_id=default_rule_id, attribute_sets=["all"]) + default_rule_details = get_rule_response.data + return_node = default_rule_details._return + + for ele in return_node: + if ele.name == "SamlIDPs": + if ele.value == '[]' or ele.value is None: + ele.value = '[' + new_saml_idp_id + ']' + else: + # replacing closing bracket with new saml idp append value + appending_val = ', ' + new_saml_idp_id + '\"]' + ele.value = ele.value.replace('\"]', appending_val) + print("updated SAML-IPDs=", ele) + + # PATCH update rules + try: + patch_rule_response = identity_domains_client.patch_rule( + rule_id=default_rule_id, + patch_op=oci.identity_domains.models.PatchOp( + schemas=["urn:ietf:params:scim:api:messages:2.0:PatchOp"], + operations=[oci.identity_domains.models.Operations(op="REPLACE", path="return", value=return_node)] + )) + print("Rule update successful!" + patch_rule_response.data) + except Exception as e: + print("Rule update encountered issue, but would have updated!", e.args[0]) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument('-p', '--config_file_profile', default='DEFAULT', + help='OCI auth profile name', + required=False) + parser.add_argument('-u', '--domain_url', + help='domain url eg https://idcs-.identity.oraclecloud.com:443', + required=True) + parser.add_argument('-r', '--default_rule_id', default='DefaultIDPRule', + help='Default Identity Provider Policy if not provided ', + required=False) + parser.add_argument('-i', '--new_saml_idp_id', + help='Default Identity Provider Policy if not provided ', + required=True) + args = parser.parse_args() + + add_saml_idp_to_default_ipd_policy(args.config_file_profile, args.domain_url, args.default_rule_id, + args.new_saml_idp_id) diff --git a/modules/oci-identity/oci-identity-provider/scripts/requirements.txt b/modules/oci-identity/oci-identity-provider/scripts/requirements.txt new file mode 100644 index 0000000..f48a128 --- /dev/null +++ b/modules/oci-identity/oci-identity-provider/scripts/requirements.txt @@ -0,0 +1,9 @@ +certifi +pytz>=2016.10 +python-dateutil>=2.5.3,<3.0.0 +requests==2.21.0; python_version == '3.6' +requests==2.31.0; python_version > '3.6' +oci>=2.127.0 +#refer +#https://github.com/oracle/oci-cli/blob/master/requirements.txt +#https://github.com/oracle/oci-python-sdk/blob/master/requirements.txt \ No newline at end of file diff --git a/modules/oci-identity/oci-identity-provider/variables.tf b/modules/oci-identity/oci-identity-provider/variables.tf new file mode 100644 index 0000000..98b957c --- /dev/null +++ b/modules/oci-identity/oci-identity-provider/variables.tf @@ -0,0 +1,70 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "config_file_profile" { + description = "" + type = string + default = "DEFAULT" +} + +variable "region" { + description = "OCI region " + type = string +} + +variable "compartment_ocid" { + description = "for root compartment pass tenancy_ocid , else pass compartment_ocid" + type = string +} + +variable "gcp_federation_xml_url" { + description = "url used to retrieve GCP identity metadata file" + type = string +} + +variable "gcp_federation_xml_file" { + description = "Local file used to retrieve GCP identity metadata file" + type = string +} + +variable "idp_name" { + description = " name of identity provider, if not provided Default name will be used" + type = string + default = "Google" +} + + +variable "idp_description" { + description = "(optional) description of identity provider" + type = string + default = "" # empty +} + + +variable "default_rule_id" { + description = "name id of Default domain default IDP rule" + type = string + default = "DefaultIDPRule" +} + +variable "domain_display_name" { + description = "Use default value unless using non default domain" + type = string + default = "Default" +} + +variable "jit_groups" { + description = "List of groups to be assigned by default to new JIT users" + type = set(string) + default = [] +} + +variable "group_prefix" { + type = string + default = "" + description = "Group name prefix in GCP" +} + +variable "idp_group_mapping" { + description = "Map between OCI identity groups and IdP groups" + type = map(string) + default = {} +} \ No newline at end of file diff --git a/modules/oci-identity/variables.tf b/modules/oci-identity/variables.tf new file mode 100644 index 0000000..6272ac9 --- /dev/null +++ b/modules/oci-identity/variables.tf @@ -0,0 +1,58 @@ +# config_file_* are for tfm setup , refer https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/terraformproviderconfiguration.htm +variable "config_file_profile" { + description = "" + type = string + default = "DEFAULT" +} + +variable "region" { + description = "OCI region " + type = string +} + +variable "compartment_ocid" { + description = "for root compartment pass tenancy_ocid , else pass compartment_ocid" + type = string +} + +variable "gcp_federation_xml_url" { + description = "url used to retrieve GCP identity metadata file" + type = string +} + +variable "gcp_federation_xml_file" { + description = "Local file used to retrieve GCP identity metadata file" + type = string +} + +variable "idp_name" { + description = " name of identity provider, if not provided Default name will be used" + type = string + default = "Google" +} + + +variable "idp_description" { + description = "(optional) description of identity provider" + type = string + default = "" # empty +} + + +variable "default_rule_id" { + description = "name id of Default domain default IDP rule" + type = string + default = "DefaultIDPRule" +} + +variable "domain_display_name" { + description = "Use default value unless using non default domain" + type = string + default = "Default" +} + +variable "group_prefix" { + type = string + default = "" + description = "Group name prefix in GCP" +} \ No newline at end of file diff --git a/network.tf b/network.tf deleted file mode 100644 index 62349f3..0000000 --- a/network.tf +++ /dev/null @@ -1,54 +0,0 @@ -resource "oci_core_vcn" "simple" { - count = local.use_existing_network ? 0 : 1 - cidr_block = var.vcn_cidr_block - dns_label = substr(var.vcn_dns_label, 0, 15) - compartment_id = var.compartment_ocid - display_name = var.vcn_display_name - - freeform_tags = {(var.tag_key_name) = (var.tag_value)} -} - -#IGW -resource "oci_core_internet_gateway" "simple_internet_gateway" { - count = local.use_existing_network ? 0 : 1 - compartment_id = var.compartment_ocid - vcn_id = oci_core_vcn.simple[count.index].id - enabled = "true" - display_name = "${var.vcn_display_name}-igw" - - freeform_tags = {(var.tag_key_name) = (var.tag_value)} -} - -#simple subnet -resource "oci_core_subnet" "simple_subnet" { - count = local.use_existing_network ? 0 : 1 - cidr_block = var.subnet_cidr_block - compartment_id = var.compartment_ocid - vcn_id = oci_core_vcn.simple[count.index].id - display_name = var.subnet_display_name - dns_label = substr(var.subnet_dns_label, 0, 15) - prohibit_public_ip_on_vnic = ! local.is_public_subnet - - freeform_tags = {(var.tag_key_name) = (var.tag_value)} -} - -resource "oci_core_route_table" "simple_route_table" { - count = local.use_existing_network ? 0 : 1 - compartment_id = var.compartment_ocid - vcn_id = oci_core_vcn.simple[count.index].id - display_name = "${var.subnet_display_name}-rt" - - route_rules { - network_entity_id = oci_core_internet_gateway.simple_internet_gateway[count.index].id - destination = "0.0.0.0/0" - destination_type = "CIDR_BLOCK" - } - - freeform_tags = {(var.tag_key_name) = (var.tag_value)} -} - -resource "oci_core_route_table_attachment" "route_table_attachment" { - count = local.use_existing_network ? 0 : 1 - subnet_id = oci_core_subnet.simple_subnet[count.index].id - route_table_id = oci_core_route_table.simple_route_table[count.index].id -} diff --git a/nsg.tf b/nsg.tf deleted file mode 100644 index 392b560..0000000 --- a/nsg.tf +++ /dev/null @@ -1,77 +0,0 @@ -resource "oci_core_network_security_group" "simple_nsg" { - #Required - compartment_id = var.compartment_ocid - vcn_id = local.use_existing_network ? var.vcn_id : oci_core_vcn.simple.0.id - - #Optional - display_name = var.nsg_display_name - - freeform_tags = {(var.tag_key_name) = (var.tag_value)} -} - -# Allow Egress traffic to all networks -resource "oci_core_network_security_group_security_rule" "simple_rule_egress" { - network_security_group_id = oci_core_network_security_group.simple_nsg.id - - direction = "EGRESS" - protocol = "all" - destination = "0.0.0.0/0" - -} - -# Allow SSH (TCP port 22) Ingress traffic from any network -resource "oci_core_network_security_group_security_rule" "simple_rule_ssh_ingress" { - network_security_group_id = oci_core_network_security_group.simple_nsg.id - protocol = "6" - direction = "INGRESS" - source = var.nsg_source_cidr - stateless = false - - tcp_options { - destination_port_range { - min = var.nsg_ssh_port - max = var.nsg_ssh_port - } - } -} - -# Allow HTTPS (TCP port 443) Ingress traffic from any network -resource "oci_core_network_security_group_security_rule" "simple_rule_https_ingress" { - network_security_group_id = oci_core_network_security_group.simple_nsg.id - protocol = "6" - direction = "INGRESS" - source = var.nsg_source_cidr - stateless = false - - tcp_options { - destination_port_range { - min = var.nsg_https_port - max = var.nsg_https_port - } - } -} - -# Allow HTTP (TCP port 80) Ingress traffic from any network -resource "oci_core_network_security_group_security_rule" "simple_rule_http_ingress" { - network_security_group_id = oci_core_network_security_group.simple_nsg.id - protocol = "6" - direction = "INGRESS" - source = var.nsg_source_cidr - stateless = false - - tcp_options { - destination_port_range { - min = var.nsg_http_port - max = var.nsg_http_port - } - } -} - -# Allow ANY Ingress traffic from within simple vcn -resource "oci_core_network_security_group_security_rule" "simple_rule_all_simple_vcn_ingress" { - network_security_group_id = oci_core_network_security_group.simple_nsg.id - protocol = "all" - direction = "INGRESS" - source = var.vcn_cidr_block - stateless = false -} diff --git a/oci_images.tf b/oci_images.tf deleted file mode 100644 index 37b4872..0000000 --- a/oci_images.tf +++ /dev/null @@ -1,25 +0,0 @@ -#File used only if Stack is a Marketplace Stack -#Update based on Marketplace Listing - App Install Package - Image Oracle Cloud Identifier -#Each element is a single image from Marketpalce Catalog. Elements' name in map is arbitrary - - -variable "marketplace_source_images" { - type = map(object({ - ocid = string - is_pricing_associated = bool - compatible_shapes = list(string) - })) - default = { - main_mktpl_image = { - ocid = "ocid1.image.oc1.." - is_pricing_associated = true - compatible_shapes = [] - } - #Remove comment and add as many marketplace images that your stack references be replicated to other realms - #supporting_image = { - # ocid = "ocid1.image.oc1.." - # is_pricing_associated = false - # compatible_shapes = ["VM.Standard2.2", "VM.Standard.E2.1.Micro"] - #} - } -} diff --git a/outputs.tf b/outputs.tf deleted file mode 100644 index a740c08..0000000 --- a/outputs.tf +++ /dev/null @@ -1,47 +0,0 @@ -### -# compute.tf outputs -### - -output "instance_id" { - value = oci_core_instance.simple-vm.id -} - -output "instance_public_ip" { - value = oci_core_instance.simple-vm.public_ip -} - -output "instance_private_ip" { - value = oci_core_instance.simple-vm.private_ip -} - -output "instance_https_url" { - value = (local.is_public_subnet ? "https://${oci_core_instance.simple-vm.public_ip}" : "https://${oci_core_instance.simple-vm.private_ip}") -} - -### -# network.tf outputs -### - -output "vcn_id" { - value = ! local.use_existing_network ? join("", oci_core_vcn.simple.*.id) : var.vcn_id -} - -output "subnet_id" { - value = ! local.use_existing_network ? join("", oci_core_subnet.simple_subnet.*.id) : var.subnet_id -} - -output "vcn_cidr_block" { - value = ! local.use_existing_network ? join("", oci_core_vcn.simple.*.cidr_block) : var.vcn_cidr_block -} - -output "nsg_id" { - value = join("", oci_core_network_security_group.simple_nsg.*.id) -} - -### -# image_subscription.tf outputs -### - -output "subscription" { - value = data.oci_core_app_catalog_subscriptions.mp_image_subscription.*.app_catalog_subscriptions -} diff --git a/packer/README.md b/packer/README.md deleted file mode 100644 index a0db5d8..0000000 --- a/packer/README.md +++ /dev/null @@ -1,188 +0,0 @@ -# oci-quickstart-template -Packer is a tool provided by Hashicorp that allows you to automate creation of your machine images. - -## Setup - -The `packer` binary can be installed from [here](https://developer.hashicorp.com/packer/tutorials/docker-get-started/get-started-install-cli). While it is not strictly required, the [OCI builder](https://developer.hashicorp.com/packer/plugins/builders/oracle/oci) for `packer` looks at the [config file](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm#SDK_and_CLI_Configuration_File) for the OCI SDK/CLI by default. -These instructions assume you have the CLI installed. The OCI CLI can be installed from [here](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm) -and set up by following these [instructions](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm#configfile). -If it is not installed you will see errors for undefined variables in later steps. - - -## Instructions -1. Clone the oci-quickstart-template repository: -``` -git clone https://github.com/oracle-quickstart/oci-quickstart-template.git -cd oci-quickstart-template/packer -``` -2. Before we can build the image, we must create network components in our tenancy -for an instance (or VM) to exist in. If you don't already have a VCN, log in to your tenancy and go to *Menu -> Networking -> Virtual Cloud Network -> Start VCN Wizard*. Default values should be fine. -[Follow the Networking Quickstart to create a network](https://docs.cloud.oracle.com/en-us/iaas/Content/Network/Tasks/quickstartnetworking.htm) - -3. Modify `env_vars.sh` and fill it with appropriate values from your tenancy. [See below](#how-to-determine-variables) for instructions on ways to obtain these values. Note, these -values are the ones that are specific to your tenancy. -``` -export PACKER_availibilty_domain="" -export PACKER_compartment_ocid="" -export PACKER_subnet_ocid="" -export PACKER_my_secret="" -``` -4. Source the ```env_vars.sh``` file modified above: -``` -source env_vars.sh -``` - -5. Validate the packer file: -``` -packer validate template.pkr.hcl -The configuration is valid. -``` - -6. Create the image: -``` -packer build template.pkr.hcl - -oracle-oci.builder_vm: output will be in this color. - -==> oracle-oci.builder_vm: Creating temporary ssh key for instance... -==> oracle-oci.builder_vm: Creating instance... -==> oracle-oci.builder_vm: Created instance (ocid1.instance.oc1.iad.anuwcljsugt6wmqca77wn3bb77nefpsgpuqnjsk4hglf2r563ymxeg4hfdbq). -==> oracle-oci.builder_vm: Waiting for instance to enter 'RUNNING' state... -==> oracle-oci.builder_vm: Instance 'RUNNING'. -[...] -==> oracle-oci.builder_vm: Created image (ocid1.image.oc1.iad.aaaaaaaagwmrt5k7rdg4od4kp3n3lbowphexjrvhlysczlk2bqzjcrtmaidq). -==> oracle-oci.builder_vm: Terminating instance (ocid1.instance.oc1.iad.anuwcljsugt6wmqca77wn3bb77nefpsgpuqnjsk4hglf2r563ymxeg4hfdbq)... -==> oracle-oci.builder_vm: Terminated instance. -Build 'oracle-oci.builder_vm' finished after 4 minutes 43 seconds. - -==> Wait completed after 4 minutes 43 seconds - -==> Builds finished. The artifacts of successful builds are: ---> oracle-oci.builder_vm: An image was created: 'mybuild-VM.Standard.E4.Flex-20230525181128' (OCID: ocid1.image.oc1.iad.aaaaaaaagwmrt5k7rdg4od4kp3n3lbowphexjrvhlysczlk2bqzjcrtmaidq) in region 'us-ashburn-1' -``` - -7. Done! Check you tenancy for the resulting image under *Menu -> Compute -> Custom Images*. -You now have a custom image based on OL 8 built from `template.pkr.hcl` with the -name `${var.imgprefix}-${var.shape}-${local.timestamp}` - - -## Variables, Provisioners, and Changing the Build - -### Variables -This repo and example template have variables defined in 3 places: - - The default CLI config implicitly. - - Tenancy specific variables in `env_vars.sh` - - Non-tenancy specific variables in `template.pkr.hcl` - -Note, the variable `my_secret` is not required but is just an example of passing -a secret to the provisioners in the template. It and references to it can be removed. It was defined as an env var to discourage committing it's value. - -The variables in the template can be changed there or overridden. For example when -testing your build you may want to skip the actual image creation, just run: -`packer build --var skip_image=true template.pkr.hcl` - -### Provisioners -The file `template.pkr.hcl` contains 4 example provisioners: - - An inline shell example that echo's some strings and touches a file. - - A shell example that copies `installer.sh` to the instance and runs it. - - A file provisioner example that copies this readme to the instance and names it `test_file`. - - A shell provisioner that runs the `oci-image-cleanup` command. - -The `oci-image-cleanup` is part of the [oci-utils](https://github.com/oracle/oci-utils) -package. It clears ssh keys, logs, etc. - -### Changing the Build - -All the 4 provisioners are just examples. You can edit them to install whatever applications are required or remove them. However, running the cleanup script is a best practice as it removes existing ssh keys and resets cloud-init so that instances booted from your custom image behave as if this is their first boot. - -_**However, you must run the cleanup script**_ if using this template to build an image for [OCI Marketplace](https://docs.oracle.com/en-us/iaas/Content/partner-portal/partner-portal_gs-what_s_oracle_cloud_marketplace_partner_portal.htm), if you do not the image will not pass validation. - -Other provisioners, like a [local shell](https://developer.hashicorp.com/packer/docs/provisioners/shell-local) provisioner or an [ansible](https://developer.hashicorp.com/packer/plugins/provisioners/ansible/ansible) provisioner can be added. - ---- - -## How to Determine Variables - -![Cloud Shell](images/cloudshell.png) - -Users will need to log into their tenancy to find the required information for setting the environment variables. One helpful tool is the Cloud Shell feature that can be accessed from the users tenancy homepage. By typing the commands below into the cloudshell terminal, users can get the required key/value pairs. The following direct link will open will the users tenancy and open a Cloud Shell embedded terminal: - - https://cloud.oracle.com/?cloudshell=true - -
PACKER_availibilty_domain

- -The list of Availibility Domains available to a tenancy can be obtained using the following command: -``` -$> oci iam availability-domain list | jq -r '.data[].name' -IYfK:US-ASHBURN-AD-1 -IYfK:US-ASHBURN-AD-2 -IYfK:US-ASHBURN-AD-3 -``` -

- - -
PACKER_compartment_ocid

- -The list of Compartment names and corresponding ocids available to a tenancy can be listed using the following command: -``` -$> oci iam compartment list | jq -r '.data[] | .name + " = " + .id' -TestCompartment = ocid1.compartment.oc1..aaaaaaaay6xopmxqb6oz52m3hdcinhknyagicj6764xx2cotffzpvolhwcsq -``` -See the [Compartments](https://cloud.oracle.com/identity/compartments) page for a list of compartments in this tenancy. Click "Create Compartment" or click an existing Compartment to get the ocid. - -![ScreenShot](images/comp_ocid.png) - -

- -
PACKER_subnet_ocid

- -See the Virtual Cloud Networks page for a list of networks in this tenancy. Click "Networking Quickstart" or click an existing network to get the ocid. - -*IMPORTANT: The ocid MUST be for the Public Subnet* - -![ScreenShot](images/public_network.png) - -

- - -
base_image_ocid -

- -The list of platform image names and corresponding ocids available to a tenancy can be listed using the following command: -``` -$> oci compute image list -c | jq -r '.data[] | ."display-name" + " = " + ."id"' -Windows-Server-2016-Standard-Edition-VM-Gen2-2020.03.16-0 = ocid1.image.oc1.iad.aaaaaaaaafrffa5esbcbcmkqappz37wjkrwh4uzpcmuixx4bcnyi4ljqmeya -Oracle-Linux-7.7-2020.03.23-0 = ocid1.image.oc1.iad.aaaaaaaa6tp7lhyrcokdtf7vrbmxyp2pctgg4uxvt4jz4vc47qoc2ec4anha -Canonical-Ubuntu-18.04-2020.03.17-0 = ocid1.image.oc1.iad.aaaaaaaa7bcrfylytqnbsqcd6jwhp2o4m6wj4lxufo3bmijnkdbfr37wu6oa -[...] -``` -A complete list of Base Images available within OCI can be seen on the [OCI All Image Families](https://docs.cloud.oracle.com/en-us/iaas/images/) page. (Click under "Read More" to get the Image ocid for a particular Image and Region.) -

-
- - -
shape

- -The list of Compute Shapes available to a tenancy can be listed using the following command: -``` -$> oci compute shape list -c | jq -r '.data[].shape' -VM.Standard2.1 -VM.Standard2.2 -[...] -``` -The list of available Compute Shapes is determined by the Service Limits of the tenancy. See the Tenancy Details page under *Service Limits -> + Compute* for the list of Compute Shapes available to your tenancy. Additional Compute Shapes can be requested by clicking the "Request a service limit increase" link on the *Tenanacy Details* page. - -The complete list of Compute Shapes available within OCI can also be seen on the [OCI Compute Shapes](https://docs.cloud.oracle.com/en-us/iaas/Content/Compute/References/computeshapes.htm) listings page. -

- - - - -
ssh_username

- -Oracle Linux ssh user is ```opc``` - -CentOS ssh user is ```opc``` - -Ubuntu ssh user is ```ubuntu``` -

diff --git a/packer/cleanup.sh b/packer/cleanup.sh deleted file mode 100644 index 3efdc33..0000000 --- a/packer/cleanup.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -BASE_IMAGE="" -ORACLE_LINUX="ORACLE_LINUX" -CENTOS="CENTOS" -UBUNTU="UBUNTU" - -################################################# -## Determine the base image (platform) we are on. -## -## 1) Oracle Linux will have the oci-image-cleanup -## script installed by default on the image. -## -## 2) CentOS will have a /etc/redhat-release file. -## -## 3) Ubuntu distro will have the "Ubuntu" string -## output a part of the `uname -a` command. -################################################## - -if [ -f /usr/libexec/oci-image-cleanup ]; then - BASE_IMAGE=$ORACLE_LINUX -elif [ -f /etc/redhat-release ]; then - BASE_IMAGE=$CENTOS -elif uname -a | grep -i "Ubuntu" > /dev/null 2>&1 ; then - BASE_IMAGE=$UBUNTU -fi -#echo Base Image = \"$BASE_IMAGE\" - - -################################################# -## Run the oci-image-cleanup script. This scrip -## comes by default on Oracle Linux. On CentOS -## platform we need to download it. -################################################## - -if [ "$BASE_IMAGE" == "$ORACLE_LINUX" ]; then - sudo /usr/libexec/oci-image-cleanup -f -elif [[ "$BASE_IMAGE" == "$CENTOS" || "$BASE_IMAGE" == "$UBUNTU" ]]; then - wget -P /tmp https://raw.githubusercontent.com/oracle/oci-utils/master/libexec/oci-image-cleanup - chmod 700 /tmp/oci-image-cleanup - sudo /tmp/oci-image-cleanup -f -fi diff --git a/packer/env_vars.sh b/packer/env_vars.sh deleted file mode 100644 index 216e2de..0000000 --- a/packer/env_vars.sh +++ /dev/null @@ -1,4 +0,0 @@ -export PACKER_availibilty_domain="" -export PACKER_compartment_ocid="" -export PACKER_subnet_ocid="" -export PACKER_my_secret="" diff --git a/packer/images/cloudshell.png b/packer/images/cloudshell.png deleted file mode 100644 index 01aa78c..0000000 Binary files a/packer/images/cloudshell.png and /dev/null differ diff --git a/packer/images/comp_ocid.png b/packer/images/comp_ocid.png deleted file mode 100644 index 2661f86..0000000 Binary files a/packer/images/comp_ocid.png and /dev/null differ diff --git a/packer/images/public_network.png b/packer/images/public_network.png deleted file mode 100644 index cdebdb3..0000000 Binary files a/packer/images/public_network.png and /dev/null differ diff --git a/packer/installer.sh b/packer/installer.sh deleted file mode 100644 index d3956ca..0000000 --- a/packer/installer.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -echo "Hello World!" > HelloWorld.txt diff --git a/packer/template.pkr.hcl b/packer/template.pkr.hcl deleted file mode 100644 index fb5c11a..0000000 --- a/packer/template.pkr.hcl +++ /dev/null @@ -1,113 +0,0 @@ - -variable "availibilty_domain" { - type = string - default = env("PACKER_availibilty_domain") -} - -variable "base_image_ocid" { - type = string - default = "ocid1.image.oc1.iad.aaaaaaaazavxtitjiojkyivwwb72pn2vcvnoxirpgkhf26i2ku4egieknkxq" - # Oracle-Linux-8.7-2023.04.25-0 - # ocid1.image.oc1.iad.aaaaaaaazavxtitjiojkyivwwb72pn2vcvnoxirpgkhf26i2ku4egieknkxq -} - -variable "compartment_ocid" { - type = string - default = env("PACKER_compartment_ocid") -} - -variable "my_secret" { - type = string - default = env("PACKER_my_secret") -} - -variable "shape" { - type = string - default = "VM.Standard.E4.Flex" -} - -variable "ocpus" { - type = number - default = 2 -} - -variable "ssh_username" { - type = string - default = "opc" -} - -variable "subnet_ocid" { - type = string - default = env("PACKER_subnet_ocid") -} - -variable "type" { - type = string - default = "oracle-oci" -} - -variable "skip_image" { - type = bool - default = false -} - -variable "imgprefix" { - type = string - default = "mybuild" -} - -locals { - timestamp = regex_replace(timestamp(), "[- TZ:]", "") - is_flex_shape = substr(var.shape, -5, -1) == ".Flex" ? [1] : [] -} - -packer { - required_plugins { - oracle = { - version = ">= 1.0.4" - source = "github.com/hashicorp/oracle" - } - } -} - -source "oracle-oci" "builder_vm" { - image_name = "${var.imgprefix}-${var.shape}-${local.timestamp}" - availability_domain = var.availibilty_domain - base_image_ocid = var.base_image_ocid - compartment_ocid = var.compartment_ocid - shape = var.shape - ssh_username = var.ssh_username - subnet_ocid = var.subnet_ocid - - dynamic "shape_config" { - for_each = local.is_flex_shape - content { - ocpus = var.ocpus - } - } - - skip_create_image = var.skip_image -} - -build { - sources = ["source.oracle-oci.builder_vm"] - - provisioner "shell" { - expect_disconnect = "true" - inline = ["cd ~", "echo Hello World!", "echo ${var.my_secret} > /dev/null", "touch foobar.txt"] - } - - provisioner "shell" { - script = "installer.sh" - } - - provisioner "file" { - source = "README.md" - destination = "~/test_file" - } - - provisioner "shell" { - script = "cleanup.sh" - } - -} diff --git a/provider.tf.cli b/provider.tf.cli deleted file mode 100644 index c9cb468..0000000 --- a/provider.tf.cli +++ /dev/null @@ -1,17 +0,0 @@ -provider "oci" { - tenancy_ocid = var.tenancy_ocid - user_ocid = var.user_ocid - fingerprint = var.fingerprint - private_key_path = var.private_key_path - region = var.region -} - -# Variables required by the OCI Provider only when running Terraform CLI with standard user based Authentication -variable "user_ocid" { -} - -variable "fingerprint" { -} - -variable "private_key_path" { -} diff --git a/schema.yaml b/schema.yaml deleted file mode 100644 index 4ec4d82..0000000 --- a/schema.yaml +++ /dev/null @@ -1,477 +0,0 @@ -# Title shown in Application Information tab. -title: Sample Marketplace Stack / ORM Application - BYOL -# Sub Title shown in Application Information tab. -description: This is a Sample Marketplace Stack / ORM Application - BYOL -schemaVersion: 1.1.0 -version: "20190304" - -# URL of Logo Icon used on Application Information tab. You can copy the contentId from the Marketplace listing logo URL in the Marketplace Partner portal. -# (Optional) -logoUrl: https://cloudmarketplace.oracle.com/marketplace/content?contentId=71617800 - -# Used in Application Information tab to Hyperlink Title and Logo to the Marketplace -# Listing. -# Also used to link to Listing Usage section for "View Instructions". -# (Optional) If it is missing, Application Information uses the -# "marketplace-listing-id" tag for the same purpose. -source: - type: marketplace - reference: 12345 - -locale: "en" -variableGroups: - - title: "Hidden Variable Group" - visible: false - variables: -#"variables used internally - not exposed to end user" - - tenancy_ocid - - region - - mp_listing_id - - mp_listing_resource_id - - mp_listing_resource_version - - availability_domain_number - - mp_subscription_enabled - - network_strategy_enum - - subnet_type_enum - - nsg_config_enum - #change compute.tf and move custom_image_id to Compute group in case you want to use a custom_image - - custom_image_id - - marketplace_source_images - - - title: "Compute Configuration" - variables: - - compute_compartment_ocid - - vm_display_name - - hostname_label - - vm_compute_shape - - vm_flex_shape_ocpus - - availability_domain_name - - ssh_public_key - - - - title: "Virtual Cloud Network" - variables: - - network_compartment_ocid - - network_strategy - - network_configuration_strategy - - vcn_id - - vcn_display_name - - vcn_dns_label - - vcn_cidr_block - - - title: "Simple Subnet" - visible: #($network_strategy == ""Use Existing VCN and Subnet"") OR (network_configuration_strategy == "Customize Network Configuration") - or: - - eq: - - network_strategy - - "Use Existing VCN and Subnet" - - eq: - - network_configuration_strategy - - "Customize Network Configuration" - variables: - - subnet_type - - subnet_id - - subnet_display_name - - subnet_dns_label - - subnet_cidr_block - - nsg_configuration_strategy - - - title: "Network Security Group" - visible: #($nsg_configuration_strategy == "Customize Network Security Group") - eq: - - nsg_configuration_strategy - - "Customize Network Security Group" - variables: - - nsg_display_name - - nsg_source_cidr - - nsg_ssh_port - - nsg_http_port - - nsg_https_port - - - title: "Additional Configuration Options" - variables: - - tag_key_name - - tag_value - - -###################################################### -############## VARIABLES ############# -###################################################### - -variables: - # Hidden variables - -###################################################### -############## HIDDEN VARIABLES ############# -###################################################### - - - tenancy_ocid: - type: string - title: Tenancy ID - description: The Oracle Cloud Identifier (OCID) for your tenancy - required: true - - region: - type: oci:identity:region:name - title: Region - description: The region in which to create all resources - required: true - - availability_domain_number: - type: string - required: false - description: Availability Domain Number (Not used) - -###################################################### -############## MARKETPLACE VARIABLES ############# -###################################################### - - mp_listing_id: - type: string - required: true - description: Marketplace Listing ID - - mp_listing_resource_id: - type: oci:core:image:id - required: true - description: Marketplace Image OCID - dependsOn: - compartmentId: compute_compartment_ocid - - mp_listing_resource_version: - type: string - required: true - description: Marketplace Listing package version - - - mp_subscription_enabled: - type: boolean - title: Use Marketplace Image? - default: false - - custom_image_id: - type: string - required: true - title: Custom Image OCID - # visible: - # eq: - # - mp_subscription_enabled - # - false - - - tag_key_name: - type: string - required: true - title: Tag key name - - tag_value: - type: string - required: true - title: Tag value - - -###################################################### -############## COMPUTE VARIABLES ############# -###################################################### - - compute_compartment_ocid: - type: oci:identity:compartment:id - required: true - title: Compute Compartment - description: The compartment in which to create all Compute resources - default: compartment_ocid - - availability_domain_name: - type: oci:identity:availabilitydomain:name - dependsOn: - compartmentId: compute_compartment_ocid - required: true - default: 1 - title: Availability Domain - description: Availability Domain - - ssh_public_key: - type: oci:core:ssh:publickey - required: true - title: Public SSH Key string - description: Public SSH Key to access VM via SSH - - vm_display_name: - type: string - required: true - title: Instance Name - description: The name of the Instance - - vm_compute_shape: - type: oci:core:instanceshape:name - default: VM.Standard2.2 - title: Compute Shape - required: true - dependsOn: - compartmentId: compute_compartment_ocid - #Comment in to restrict to marketplace image allowed shapes - #imageId: mp_listing_resource_id - - vm_flex_shape_ocpus: - visible: #($vm_compute_shape == ""VM.Standard.E3.Flex"") - eq: - - vm_compute_shape - - "VM.Standard.E3.Flex" - type: integer - default: 2 - title: Flex Shape OCPUs - minimum: 1 - maximum: 64 - required: false - - hostname_label: - type: string - required: false - title: DNS Hostname Label - - -###################################################### -############## NETWORK VARIABLES ############# -###################################################### - - network_compartment_ocid: - type: oci:identity:compartment:id - required: true - title: Network Compartment - description: The compartment in which to create all Network resources - default: compartment_ocid - - - # Network Type Options - network_strategy: - type: enum - title: Network Strategy - description: Create or use existing Network Stack (VCN and Subnet) - enum: - - "Create New VCN and Subnet" - - "Use Existing VCN and Subnet" - required: true - default: "Create New VCN and Subnet" - - - # Network Configuration - network_configuration_strategy: - visible: #($network_strategy == ""Create New VCN and Subnet"") - eq: - - network_strategy - - "Create New VCN and Subnet" - type: enum - title: Configuration Strategy - description: Use recommended configuration or customize it - enum: - - "Use Recommended Configuration" - - "Customize Network Configuration" - required: true - default: "Use Recommended Configuration" - - - -######## VCN ######## - - vcn_display_name: - visible: #($network_strategy == ""Create New VCN and Subnet"") AND (network_configuration_strategy == "Customize Network Configuration") - and: - - eq: - - network_strategy - - "Create New VCN and Subnet" - - eq: - - network_configuration_strategy - - "Customize Network Configuration" - type: string - required: true - title: Name - description: The name of the new Virtual Cloud Network (VCN) - - vcn_id: - visible: #($network_strategy == "Use Existing VCN and Subnet") - eq: - - network_strategy - - "Use Existing VCN and Subnet" - type: oci:core:vcn:id - dependsOn: - compartmentId: network_compartment_ocid - required: true - title: Existing VCN - description: An existing Virtual Cloud Network (VCN) in which to create the compute instances, network resources, and load balancers. If not specified, a new VCN is created. - - vcn_cidr_block: - visible: #($network_strategy == ""Create New VCN and Subnet"") AND (network_configuration_strategy == "Customize Network Configuration") - and: - - eq: - - network_strategy - - "Create New VCN and Subnet" - - eq: - - network_configuration_strategy - - "Customize Network Configuration" - type: string - required: true - pattern: "^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\/(3[0-2]|[1-2]?[0-9])$" - title: CIDR Block - description: The CIDR of the new Virtual Cloud Network (VCN). If you plan to peer this VCN with another VCN, the VCNs must not have overlapping CIDRs. - - vcn_dns_label: - visible: #($network_strategy == ""Create New VCN and Subnet"") AND (network_configuration_strategy == "Customize Network Configuration") - and: - - eq: - - network_strategy - - "Create New VCN and Subnet" - - eq: - - network_configuration_strategy - - "Customize Network Configuration" - type: string - required: true - title: DNS Label - maxLenght: 15 - description: VCN DNS Label. Only letters and numbers, starting with a letter. 15 characters max. - -######## MANAGEMENT SUBNET ######## - subnet_type: - visible: #($network_strategy == ""Create New VCN and Subnet"") - eq: - - network_strategy - - "Create New VCN and Subnet" - type: enum - title: Subnet Type - description: Choose between private and public subnets - enum: - - "Private Subnet" - - "Public Subnet" - required: true - default: "Public Subnet" - - subnet_display_name: - visible: #($network_strategy == ""Create New VCN and Subnet"") - eq: - - network_strategy - - "Create New VCN and Subnet" - type: string - required: true - title: Name - description: The name of the new Subnet - - subnet_cidr_block: - visible: #($network_strategy == ""Create New VCN and Subnet"") - eq: - - network_strategy - - "Create New VCN and Subnet" - type: string - pattern: "^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\/(3[0-2]|[1-2]?[0-9])$" - required: true - title: CIDR Block - description: The CIDR of the new Subnet. The new subnet's CIDR should not overlap with any other subnet CIDRs. - - subnet_id: - visible: #($network_strategy == "Use Existing VCN and Subnet") - eq: - - network_strategy - - "Use Existing VCN and Subnet" - type: oci:core:subnet:id - dependsOn: - vcnId: vcn_id - compartmentId: network_compartment_ocid - default: '' - required: true - title: Existing Subnet - description: An existing Management subnet. This subnet must already be present in the chosen VCN. - - subnet_dns_label: - visible: #($network_strategy == ""Create New VCN and Subnet"") - eq: - - network_strategy - - "Create New VCN and Subnet" - type: string - required: true - title: DNS Label - maxLenght: 15 - description: Subnet DNS Label. Only letters and numbers, starting with a letter. 15 characters max. - -###################################################### -############## SECURITY VARIABLES ############# -###################################################### - - - nsg_configuration_strategy: - visible: true - type: enum - title: Network Security Group Configuration - description: Choose Network Security Rules Strategy - enum: - - "Use Recommended Configuration" - - "Customize Network Security Group" - required: true - default: "Use Recommended Configuration" - - - nsg_display_name: - type: string - required: false - title: Name - - nsg_ssh_port: - type: number - required: false - default: 22 - title: SSH Port Number - - nsg_http_port: - type: number - required: false - default: 80 - title: HTTP Port Number - - nsg_https_port: - type: number - required: false - default: 443 - title: HTTPS Port Number - - nsg_source_cidr: - type: string - required: false - default: "0.0.0.0/0" - title: Allowed Ingress Traffic (CIDR Block) - - - - -############################################# -########### OUTPUT GROUPS ############ -############################################# - -outputGroups: - - title: "Instance Management" - outputs: - - instance_public_ip - - instance_private_ip - - -############################################# -############## OUTPUTS ############## -############################################# - -outputs: - instance_https_url: - type: link - title: Open Application URL - visible: false - - instance_public_ip: - type: link - title: Public IP - visible: #($mgmt_subnet_type == "Public Subnet") - eq: - - mgmt_subnet_type - - "Public Subnet" - - instance_private_ip: - type: link - title: Private IP - visible: true - -primaryOutputButton: instance_https_url diff --git a/scripts/example.sh b/scripts/example.sh deleted file mode 100644 index 0282894..0000000 --- a/scripts/example.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -touch ~opc/hello_world diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..13e9c6a --- /dev/null +++ b/templates/README.md @@ -0,0 +1 @@ +TODO // TODO \ No newline at end of file diff --git a/templates/gcp-rbac/README.md b/templates/gcp-rbac/README.md new file mode 100644 index 0000000..eb3ca0f --- /dev/null +++ b/templates/gcp-rbac/README.md @@ -0,0 +1 @@ +TODO: // TODO \ No newline at end of file diff --git a/terraform.tfvars.template b/terraform.tfvars.template deleted file mode 100644 index dfb6f12..0000000 --- a/terraform.tfvars.template +++ /dev/null @@ -1,10 +0,0 @@ -user_ocid = "ocid1.user.oc1.." -fingerprint = "1c.." -private_key_path = "~/.oci/oci_api_key.pem" -tenancy_ocid = "ocid1.tenancy.oc1.." -region = "us-ashburn-1" - -compute_compartment_ocid = "ocid1.compartment.oc1." -network_compartment_ocid = "ocid1.compartment.oc1." -availability_domain_number = "1" -ssh_public_key = "ssh-rsa AAA...H" \ No newline at end of file diff --git a/variables.tf b/variables.tf deleted file mode 100644 index ba0baf2..0000000 --- a/variables.tf +++ /dev/null @@ -1,214 +0,0 @@ -#Variables declared in this file must be declared in the marketplace.yaml -#Provide a description to your variables. - -############################ -# Hidden Variable Group # -############################ -variable "tenancy_ocid" { -} - -variable "region" { -} - -############################################################################### -# Marketplace Image Listing - information available in the Partner portal # -############################################################################### -variable "mp_subscription_enabled" { - description = "Subscribe to Marketplace listing?" - type = bool - default = false -} - -variable "mp_listing_id" { - // default = "ocid1.appcataloglisting.oc1.." - default = "" - description = "Marketplace Listing OCID" -} - -variable "mp_listing_resource_id" { - // default = "ocid1.image.oc1.." - default = "" - description = "Marketplace Listing Image OCID" -} - -variable "mp_listing_resource_version" { - // default = "1.0" - default = "" - description = "Marketplace Listing Package/Resource Version" -} - -############################ -# Custom Image # -############################ -variable "custom_image_id" { - default = "ocid1.image.oc1...." - description = "Custom Image OCID" -} - -############################ -# Compute Configuration # -############################ - -variable "vm_display_name" { - description = "Instance Name" - default = "simple-vm" -} - -variable "vm_compute_shape" { - description = "Compute Shape" - default = "VM.Standard2.2" //2 cores -} - -# only used for E3 Flex shape -variable "vm_flex_shape_ocpus" { - description = "Flex Shape OCPUs" - default = 1 -} - -variable "availability_domain_name" { - default = "" - description = "Availability Domain name, if non-empty takes precedence over availability_domain_number" -} - -variable "availability_domain_number" { - default = 1 - description = "OCI Availability Domains: 1,2,3 (subject to region availability)" -} - -variable "ssh_public_key" { - description = "SSH Public Key" -} - -variable "hostname_label" { - default = "simple" - description = "DNS Hostname Label. Must be unique across all VNICs in the subnet and comply with RFC 952 and RFC 1123." -} - -############################ -# Network Configuration # -############################ - -variable "network_strategy" { - #default = "Use Existing VCN and Subnet" - default = "Create New VCN and Subnet" -} - -variable "vcn_id" { - default = "" -} - -variable "vcn_display_name" { - description = "VCN Name" - default = "simple-vcn" -} - -variable "vcn_cidr_block" { - description = "VCN CIDR" - default = "10.0.0.0/16" -} - -variable "vcn_dns_label" { - description = "VCN DNS Label" - default = "simplevcn" -} - -variable "subnet_type" { - description = "Choose between private and public subnets" - default = "Public Subnet" - #or - #default = "Private Subnet" -} - -variable "subnet_id" { - default = "" -} - -variable "subnet_display_name" { - description = "Subnet Name" - default = "simple-subnet" -} - -variable "subnet_cidr_block" { - description = "Subnet CIDR" - default = "10.0.0.0/24" -} - -variable "subnet_dns_label" { - description = "Subnet DNS Label" - default = "simplesubnet" -} - -############################ -# Security Configuration # -############################ -variable "nsg_display_name" { - description = "Network Security Group Name" - default = "simple-network-security-group" -} - -variable "nsg_source_cidr" { - description = "Allowed Ingress Traffic (CIDR Block)" - default = "0.0.0.0/0" -} - -variable "nsg_ssh_port" { - description = "SSH Port" - default = 22 -} - -variable "nsg_https_port" { - description = "HTTPS Port" - default = 443 -} - -variable "nsg_http_port" { - description = "HTTP Port" - default = 80 -} - -############################ -# Additional Configuration # -############################ - -variable "compartment_ocid" { - description = "Compartment where Compute and Marketplace subscription resources will be created" -} - -variable "tag_key_name" { - description = "Free-form tag key name" - default = "oracle-quickstart" -} - -variable "tag_value" { - description = "Free-form tag value" - default = "oci-quickstart-template" -} - - -###################### -# Enum Values # -###################### -variable "network_strategy_enum" { - type = map - default = { - CREATE_NEW_VCN_SUBNET = "Create New VCN and Subnet" - USE_EXISTING_VCN_SUBNET = "Use Existing VCN and Subnet" - } -} - -variable "subnet_type_enum" { - type = map - default = { - PRIVATE_SUBNET = "Private Subnet" - PUBLIC_SUBNET = "Public Subnet" - } -} - -variable "nsg_config_enum" { - type = map - default = { - BLOCK_ALL_PORTS = "Block all ports" - OPEN_ALL_PORTS = "Open all ports" - CUSTOMIZE = "Customize ports - Post deployment" - } -} diff --git a/versions.tf b/versions.tf deleted file mode 100644 index d465dd5..0000000 --- a/versions.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_version = ">= 1.0.0" - required_providers { - # Recommendation from ORM / OCI provider teams - oci = { - version =">= 4.21.0" - } - } -}