* Addition of Cloud Deploy Module * Addition of IAM and fixes as per review * Updated README to add IAM information, ran tfdocs and tests * Correcting linting errors * Correcting version.tf * Ordered arguments and variables * Updated README on module and root levels to include Cloud Deploy * Fixing formatting and test issues * fixing formatting --------- Co-authored-by: Vineet Eldochan <vineet.eldochan@withriley.com>
455 lines
26 KiB
Markdown
455 lines
26 KiB
Markdown
# Cloud Deploy Module
|
|
|
|
Cloud Deploy Module for creating and managing Delivery Pipelines, Targets, Automations, Deploy Policies and resource-level IAM roles.
|
|
|
|
<!-- BEGIN TOC -->
|
|
- [Limitations](#limitations)
|
|
- [Examples](#examples)
|
|
- [Single Target Canary Deployment](#single-target-canary-deployment)
|
|
- [Single Target Canary Deployment with Custom Traffic Limits](#single-target-canary-deployment-with-custom-traffic-limits)
|
|
- [Single Target Canary Deployment with Verification](#single-target-canary-deployment-with-verification)
|
|
- [Delivery Pipeline with Existing Target](#delivery-pipeline-with-existing-target)
|
|
- [Multiple Targets in Serial Deployment](#multiple-targets-in-serial-deployment)
|
|
- [Multi Target Multi Project Deployment](#multi-target-multi-project-deployment)
|
|
- [Multi Target with Serial and Parallel deployment](#multi-target-with-serial-and-parallel-deployment)
|
|
- [Automation for Delivery Pipelines](#automation-for-delivery-pipelines)
|
|
- [Deployment Policy](#deployment-policy)
|
|
- [IAM for Delivery Pipeline and Target resource level](#iam-for-delivery-pipeline-and-target-resource-level)
|
|
- [Variables](#variables)
|
|
- [Outputs](#outputs)
|
|
<!-- END TOC -->
|
|
|
|
## Limitations
|
|
> [!WARNING]
|
|
> Currently this module only supports Cloud Run deployments and does not include GKE or Custom Target deployments.
|
|
|
|
## Examples
|
|
|
|
## Single Target Canary Deployment
|
|
|
|
This deploys a Cloud Deploy Delivery Pipeline with a single target using the Canary deployment strategy, which by default routes 10% of traffic initially and upon success, shifts to 100% (making it the stable revision). By default `strategy = "STANDARD"` is set, to use canary strategy this needs to be changed to `strategy = "CANARY"`.
|
|
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=2
|
|
```
|
|
|
|
## Single Target Canary Deployment with Custom Traffic Limits
|
|
|
|
This deploys a Cloud Deploy Delivery Pipeline with a single target with the Canary deployment strategy. `deployment_percentages` can be set to specify the traffic stages that would be applied during the canary deployment. It accepts integer values in ascending order and between 0 to 99.
|
|
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
deployment_percentages = [10, 50, 70]
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=2
|
|
```
|
|
|
|
## Single Target Canary Deployment with Verification
|
|
|
|
This deployments enables the rollout to have a verification step by setting `verify = true`. The verification step and configurations need to be passed within the skaffold file.
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
verify = true
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=2
|
|
```
|
|
|
|
## Delivery Pipeline with Existing Target
|
|
|
|
This deployment demonstrates the ability to create a delivery pipeline by reusing existing targets. By default a `create_target = true` is set, creating and assigning a target to the delivery pipeline. Setting it to false directs the code to assign the target to the delivery pipeline and skip its creation during execution.
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
|
|
targets = [
|
|
{
|
|
create_target = false
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=1
|
|
```
|
|
|
|
## Multiple Targets in Serial Deployment
|
|
|
|
Cloud Deployment supports deployments to multiple targets. This example shows how to create 3 targets and to set them in sequence.
|
|
The sequence of deployment is defined by the sequence of the target configuration object within the list. `require_approval` can be set to true for any target that requires an approval prior to its deployment/rollout.
|
|
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
},
|
|
{
|
|
name = "qa-target"
|
|
description = "QA Target"
|
|
profiles = ["qa"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
},
|
|
{
|
|
name = "prod-target"
|
|
description = "Prod Target"
|
|
profiles = ["prod"]
|
|
require_approval = true
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=4
|
|
```
|
|
|
|
|
|
## Multi Target Multi Project Deployment
|
|
|
|
Targets in this deployment can deploy to different projects. For instance, `qa-target` deploys to a separate `project_id` and `region`. To direct Cloud Run deployments to a different project, specify the `project_id` and `region` under `cloud_run_configs`. By default, Cloud Run services will use the target's own `project_id` and `region`.
|
|
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
},
|
|
{
|
|
name = "qa-target"
|
|
description = "QA Target"
|
|
profiles = ["qa"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
project_id = "<cloud_run_project_id>"
|
|
region = "<cloud_run_region>"
|
|
automatic_traffic_control = true
|
|
}
|
|
},
|
|
{
|
|
name = "prod-target"
|
|
description = "Prod Target"
|
|
profiles = ["prod"]
|
|
require_approval = true
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=4
|
|
```
|
|
|
|
## Multi Target with Serial and Parallel deployment
|
|
|
|
Cloud Deploy allows deploying to targets in a serial and parallel order. By defining a multi-target target configuration using `multi_target_target_ids` cloud deploy would execute the deployments in parallel. `require_approval` should only be applied to the multi-target target configuration and not the the child targets. As the child targets would execute within the multi-target target configuration, they are excluded from being directly assigned in the serial sequence of the delivery pipeline, using `exclude_from_pipeline = true`.
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
},
|
|
{
|
|
name = "multi-qa-target"
|
|
description = "Multi QA target"
|
|
profiles = ["multi-qa"]
|
|
multi_target_target_ids = ["qa-target-1", "qa-target-2"]
|
|
strategy = "STANDARD"
|
|
},
|
|
{
|
|
exclude_from_pipeline = true
|
|
name = "qa-target-1"
|
|
description = "QA target-1"
|
|
profiles = ["qa-1"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
},
|
|
{
|
|
exclude_from_pipeline = true
|
|
name = "qa-target-2"
|
|
description = "QA target-2"
|
|
profiles = ["qa-2"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=5
|
|
```
|
|
|
|
## Automation for Delivery Pipelines
|
|
|
|
This deployment incorporates automations that are supported within a delivery pipeline. If automations are defined at least 1 rule needs to be specified. Rules are defined as `"automation-name" = { <arguments> }` format. Multiple automations can be defined and multiple rules can be specified within an automation. A `service_account` can be provided to execute the automation using the defined service account. If this is missing it defaults to the compute engine default service account (`<project-id>-compute@developer.gserviceaccount.com`).
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
automations = {
|
|
"advance-rollout" = {
|
|
description = "advance_rollout_rule"
|
|
service_account = "<service_account_name>@<project_id>.iam.gserviceaccount.com"
|
|
advance_rollout_rule = {
|
|
source_phases = ["canary"]
|
|
wait = "200s"
|
|
}
|
|
},
|
|
"repair-rollout" = {
|
|
description = "repair_rollout_rule"
|
|
service_account = "<service_account_name>@<project_id>.iam.gserviceaccount.com"
|
|
repair_rollout_rule = {
|
|
jobs = ["predeploy", "deploy", "postdeploy", "verify"]
|
|
phases = ["canary-10", "stable"]
|
|
rollback = {
|
|
destination_phase = "stable"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=4
|
|
```
|
|
|
|
## Deployment Policy
|
|
|
|
This example provides a way to define a deployment policy along with the delivery pipeline. Each deploy policy can be defined as `"deploy_policy_name" = { <arguments> }` format. Rollout restrictions are defined as `"restriction_name" = { <arguments> }` format.
|
|
By default, the deployment policy defined below applies to all delivery pipelines. If this requires a change, modify the selector option. Selector types supported are: "DELIVERY_PIPELINE" and "TARGET".
|
|
|
|
```hcl
|
|
module "cloud_deploy" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
}
|
|
]
|
|
|
|
deploy_policies = {
|
|
"deploy-policy" = {
|
|
selectors = [{
|
|
id = "*"
|
|
type = "DELIVERY_PIPELINE"
|
|
}]
|
|
rollout_restrictions = {
|
|
"restriction-1" = {
|
|
time_zone = "Australia/Melbourne"
|
|
weekly_windows = [{
|
|
days_of_week = ["MONDAY", "TUESDAY"]
|
|
|
|
start_time = {
|
|
hours = "10"
|
|
minutes = "30"
|
|
seconds = "00"
|
|
nanos = "00"
|
|
}
|
|
|
|
end_time = {
|
|
hours = "12"
|
|
minutes = "30"
|
|
seconds = "00"
|
|
nanos = "00"
|
|
}
|
|
}]
|
|
} }
|
|
}
|
|
}
|
|
}
|
|
# tftest modules=1 resources=3
|
|
```
|
|
|
|
## IAM for Delivery Pipeline and Target resource level
|
|
|
|
This example specifies the option to set IAM roles at the Delivery Pipeline and Target resource level. IAM bindings support the usual syntax.
|
|
`iam`, `iam_bindings`, `iam_bindings_additive`, `iam_by_principals` are supported for delivery pipelines and targets.
|
|
|
|
```hcl
|
|
module "cloud_run" {
|
|
source = "./fabric/modules/cloud-deploy"
|
|
project_id = var.project_id
|
|
region = var.region
|
|
name = "deployment-pipeline"
|
|
|
|
iam = { "roles/clouddeploy.developer" = ["user:allUsers"] }
|
|
|
|
targets = [
|
|
{
|
|
name = "dev-target"
|
|
description = "Dev Target"
|
|
profiles = ["dev"]
|
|
strategy = "CANARY"
|
|
cloud_run_configs = {
|
|
automatic_traffic_control = true
|
|
}
|
|
iam = { "roles/clouddeploy.operator" = ["user:allUsers"] }
|
|
}
|
|
]
|
|
}
|
|
# tftest modules=1 resources=4
|
|
```
|
|
<!-- BEGIN TFDOC -->
|
|
## Variables
|
|
|
|
| name | description | type | required | default |
|
|
|---|---|:---:|:---:|:---:|
|
|
| [name](variables.tf#L191) | Cloud Deploy Delivery Pipeline name. | <code>string</code> | ✓ | |
|
|
| [project_id](variables.tf#L201) | Project id used for resources, if not explicitly specified. | <code>string</code> | ✓ | |
|
|
| [region](variables.tf#L206) | Region used for resources, if not explicitly specified. | <code>string</code> | ✓ | |
|
|
| [annotations](variables.tf#L17) | Resource annotations. | <code>map(string)</code> | | <code>{}</code> |
|
|
| [automations](variables.tf#L24) | Configuration for automations associated with the deployment pipeline in a name => attributes format. | <code title="map(object({ project_id = optional(string, null) region = optional(string, null) annotations = optional(map(string)) description = optional(string, null) labels = optional(map(string)) service_account = optional(string, null) suspended = optional(bool, false) advance_rollout_rule = optional(object({ id = optional(string, "advance-rollout") source_phases = optional(list(string), null) wait = optional(string, null) })) promote_release_rule = optional(object({ id = optional(string, "promote-release") wait = optional(string, null) destination_target_id = optional(string, null) destination_phase = optional(string, null) })) repair_rollout_rule = optional(object({ id = optional(string, "repair-rollout") phases = optional(list(string), null) jobs = optional(list(string), null) retry = optional(object({ attempts = optional(string, null) wait = optional(string, null) backoff_mode = optional(string, null) })) rollback = optional(object({ destination_phase = optional(string, null) disable_rollback_if_rollout_pending = optional(bool, true) })) })) timed_promote_release_rule = optional(object({ id = optional(string, "timed-promote-release") destination_target_id = optional(string, null) schedule = optional(string, null) time_zone = optional(string, null) destination_phase = optional(string, null) })) selector = optional(list(object({ id = optional(string, "*") labels = optional(map(string), {}) })), [{ id = "*" }]) }))">map(object({…}))</code> | | <code>{}</code> |
|
|
| [deploy_policies](variables.tf#L84) | Configurations for Deployment Policies in a name => attributes format. | <code title="map(object({ project_id = optional(string, null) region = optional(string, null) annotations = optional(map(string)) description = optional(string, null) labels = optional(map(string)) suspended = optional(bool, false) rollout_restrictions = map(object({ actions = optional(list(string), null) invokers = optional(list(string), null) time_zone = optional(string) weekly_windows = optional(list(object({ days_of_week = optional(list(string)) start_time = optional(object({ hours = optional(string) minutes = optional(string) seconds = optional(string) nanos = optional(string) })) end_time = optional(object({ hours = optional(string) minutes = optional(string) seconds = optional(string) nanos = optional(string) })) })), []) one_time_windows = optional(list(object({ start_date = optional(object({ day = optional(string) month = optional(string) year = optional(string) })) start_time = optional(object({ hours = optional(string) minutes = optional(string) seconds = optional(string) nanos = optional(string) })) end_date = optional(object({ day = optional(string) month = optional(string) year = optional(string) })) end_time = optional(object({ hours = optional(string) minutes = optional(string) seconds = optional(string) nanos = optional(string) })) })), []) })) selectors = optional(list(object({ id = optional(string, "*") type = optional(string, "DELIVERY_PIPELINE") labels = optional(map(string), {}) })), [{ id = "*", type = "DELIVERY_PIPELINE" }]) }))">map(object({…}))</code> | | <code>{}</code> |
|
|
| [description](variables.tf#L165) | Cloud Deploy Delivery Pipeline description. | <code>string</code> | | <code>"Terraform managed."</code> |
|
|
| [iam](variables-iam.tf#L17) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
|
| [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
|
| [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
|
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
|
| [labels](variables.tf#L176) | Cloud Deploy Delivery Pipeline resource labels. | <code>map(string)</code> | | <code>{}</code> |
|
|
| [suspended](variables.tf#L211) | Configuration to suspend a delivery pipeline. | <code>bool</code> | | <code>false</code> |
|
|
| [targets](variables.tf#L218) | Configuration for new targets associated with the delivery pipeline in a list format. Order of the targets are defined by the order within the list. | <code title="list(object({ project_id = optional(string, null) region = optional(string, null) name = string create_target = optional(bool, true) exclude_from_pipeline = optional(bool, false) annotations = optional(map(string)) description = optional(string, null) deployment_percentages = optional(list(number), [10]) execution_configs_usages = optional(list(string)) execution_configs_timeout = optional(string, null) labels = optional(map(string)) multi_target_target_ids = optional(list(string)) profiles = optional(list(string), []) predeploy_actions = optional(list(string)) postdeploy_actions = optional(list(string)) require_approval = optional(bool, false) strategy = optional(string, "STANDARD") target_deploy_parameters = optional(map(string), null) verify = optional(bool, false) cloud_run_configs = optional(object({ project_id = optional(string, null) region = optional(string, null) automatic_traffic_control = optional(bool, true) canary_revision_tags = optional(list(string), null) prior_revision_tags = optional(list(string), null) stable_revision_tags = optional(list(string), null) })) custom_canary_phase_configs = optional(map(object({ deployment_percentage = string predeploy_actions = optional(list(string)) postdeploy_actions = optional(list(string)) })), {}) delivery_pipeline_deploy_parameters = optional(list(object({ values = optional(map(string), null) matching_target_labels = optional(map(string), null) })), []) iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_by_principals = optional(map(list(string)), {}) }))">list(object({…}))</code> | | <code>[]</code> |
|
|
|
|
## Outputs
|
|
|
|
| name | description | sensitive |
|
|
|---|---|:---:|
|
|
| [automation_ids](outputs.tf#L18) | Automation ids. | |
|
|
| [deploy_policy_ids](outputs.tf#L23) | Deploy Policy ids. | |
|
|
| [pipeline_id](outputs.tf#L28) | Delivery pipeline id. | |
|
|
| [target_ids](outputs.tf#L33) | Target ids. | |
|
|
<!-- END TFDOC -->
|