From d83fcd580cd77f4260abb0e381b45f8279df7e7c Mon Sep 17 00:00:00 2001 From: Kim Ngo <6362111+findkim@users.noreply.github.com> Date: Mon, 4 Oct 2021 10:24:41 -0500 Subject: [PATCH 01/20] CTS: add TLS config for TFE connection (#11166) Co-authored-by: mrspanishviking --- website/content/docs/nia/configuration.mdx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/website/content/docs/nia/configuration.mdx b/website/content/docs/nia/configuration.mdx index 8b7a64c40a..e1951ec3a4 100644 --- a/website/content/docs/nia/configuration.mdx +++ b/website/content/docs/nia/configuration.mdx @@ -335,6 +335,20 @@ driver "terraform-cloud" { - We recommend creating a dedicated team and team API token to isolate automation by Consul-Terraform-Sync from other Terraform Cloud operations. - `workspace_prefix` - (string) Specifies a prefix to prepend to the automatically-generated workspace names used for automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be ``. For example, if you configure the prefix as "cts_", then a task with the name "task_firewall" will have the workspace name "cts_task_firewall". - `required_providers` - (obj: required) Declare each Terraform provider used across all tasks. This can be configured the same as how you would configure [Terraform `terraform.required_providers`](https://www.terraform.io/docs/configuration/provider-requirements.html#requiring-providers) field to specify the source and version for each provider. Consul-Terraform-Sync will process these requirements when preparing each task that uses the provider. +- `tls` - Configure TLS to allow HTTPS connections to [Terraform Enterprise](https://www.terraform.io/docs/enterprise/install/installer.html#tls-key-amp-cert). + - `enabled` - (bool) Enable TLS. Providing a value for any of the TLS options will enable this parameter implicitly. + - `ca_cert` - (string) The CA file to use for communicating with Terraform Enterprise over TLS. + - `ca_path` - (string) The path to a directory of CA certificates to use for communicating with Terraform Enterprise over TLS. + - `cert` - (string) The client certificate file to use for communicating with Terraform Enterprise over TLS. + - `key` - (string) The client key file to use for communicating with Terraform Enterprise over TLS. + - `server_name` - (string) The server name to use as the SNI host when connecting via TLS. + - `verify` - (bool: true) Enables TLS peer verification. The default is enabled, which will check the global CA chain to make sure the given certificates are valid. + - If you are using a self-signed certificate that you have not added to the CA chain, you may want to disable SSL verification to ignore any certificate warnings. However, please understand this is a potential security vulnerability. + ```hcl + tls { + verify = false + } + ``` Consul-Terraform-Sync generates local artifacts to prepare configuration versions used for workspace runs. The location of the files created can be set with the [`working_dir`](/docs/nia/configuration#working_dir) option or configured per task. When a task is configured with a local module and is run with the Terraform Cloud driver, the local module is copied and uploaded as a part of the configuration version. From be11385944a2d44a1158706e6d33d4483e5deae7 Mon Sep 17 00:00:00 2001 From: Melissa Kam Date: Mon, 4 Oct 2021 11:51:21 -0500 Subject: [PATCH 02/20] nia/docs: Remove deprecated tag option from service config --- website/content/docs/nia/configuration.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/website/content/docs/nia/configuration.mdx b/website/content/docs/nia/configuration.mdx index e1951ec3a4..3c942c34b9 100644 --- a/website/content/docs/nia/configuration.mdx +++ b/website/content/docs/nia/configuration.mdx @@ -100,7 +100,6 @@ service { - `id` - (string) ID identifies the service for Consul-Terraform-Sync. This is used to explicitly identify the service config for a task to use. If no ID is provided, the service is identified by the service name within a [task definition](#task). - `name` - (string: required) The Consul logical name of the service (required). - `namespace` - (string: "default") The namespace of the service. If not provided, the namespace will be inferred from the Consul-Terraform-Sync ACL token, or default to the `default` namespace. -- `tag` - (string) **This field is deprecated in Consul-Terraform-Sync 0.2.0 and will be removed in 0.4.0. Use `filter` with the `Service.Tags` selector instead.** Tag is used to filter nodes based on the tag for the service. - `filter` - (string) Specifies the expression used to filter nodes for the service. For more details on supported filters, see the Consul documentation on [filtering service nodes](/api-docs/health#filtering-2). - `cts_user_defined_meta` - (map[string]) User-defined metadata is a map of strings that will be appended to the [service input variable](/docs/nia/installation/requirements#module-specifications) for compatible Terraform modules. Not all modules may use this value. To determine if your task uses metadata or what the expected keys and format are, reference documentation for the module(s) configured for your tasks. - If multiple tasks depend on the same service but require different metadata, you can declare different sets of metadata for the same service. Define multiple service blocks for the service with unique IDs (and identical names) for those blocks. The metadata can then be separated per task based on the service IDs. From 162fe5b502b37ffcd59c29e8f11d7621ef6693e8 Mon Sep 17 00:00:00 2001 From: Melissa Kam Date: Mon, 4 Oct 2021 12:07:46 -0500 Subject: [PATCH 03/20] nia/docs: Remove references to v0.4.0-beta --- website/content/docs/nia/configuration.mdx | 4 ---- website/content/docs/nia/network-drivers/index.mdx | 2 -- website/content/docs/nia/network-drivers/terraform-cloud.mdx | 2 -- website/content/docs/nia/tasks.mdx | 2 -- website/content/docs/nia/terraform-modules.mdx | 2 -- 5 files changed, 12 deletions(-) diff --git a/website/content/docs/nia/configuration.mdx b/website/content/docs/nia/configuration.mdx index 3c942c34b9..6afa67c0da 100644 --- a/website/content/docs/nia/configuration.mdx +++ b/website/content/docs/nia/configuration.mdx @@ -223,8 +223,6 @@ task { #### Consul KV Condition --> **Beta:** This feature is currently only available in Consul-Terraform-Sync v0.4.0-beta. - A consul-kv condition block configures a task to only execute on changes to a Consul KV entry. The condition can be configured for a single Consul KV entry or for any Consul KV entries that are prefixed with a given path. See [Task Execution: Consul KV Condition](/docs/nia/tasks#consul-kv-condition) for more information on how tasks are triggered with a consul-kv condition. @@ -302,8 +300,6 @@ driver "terraform" { which is available with Consul Enterprise. --> **Beta:** The integration with the HashiCorp managed service version of Terraform Cloud is currently only available in Consul-Terraform-Sync v0.4.0-beta. Integration with the self-hosted version of Terraform Cloud is available as of v0.3.0. - The Terraform Cloud driver enables Consul-Terraform-Sync Enterprise to integrate with **Terraform Cloud**, including both the [self-hosted distribution](https://www.hashicorp.com/products/terraform/editions/enterprise) and the [managed service](https://www.hashicorp.com/products/terraform/editions/cloud). With this driver, Consul-Terraform-Sync automates Terraform runs and remote operations for workspaces. An overview of features enabled with Terraform Cloud can be viewed within the [Network Drivers](/docs/nia/network-drivers) documentation. diff --git a/website/content/docs/nia/network-drivers/index.mdx b/website/content/docs/nia/network-drivers/index.mdx index d9bfd6c786..3186619e09 100644 --- a/website/content/docs/nia/network-drivers/index.mdx +++ b/website/content/docs/nia/network-drivers/index.mdx @@ -13,8 +13,6 @@ Consul-Terraform-Sync is a HashiCorp solution to Network Infrastructure Automati The following table highlights some of the additional features Terraform and Terraform Cloud offer when used as a network driver for Consul-Terraform-Sync. Visit the [Terraform product page](https://www.hashicorp.com/products/terraform) or [contact our sales team](https://www.hashicorp.com/contact-sales) for a comprehensive list of features. --> **Beta:** The integration with the HashiCorp managed service version of Terraform Cloud is currently only available in Consul-Terraform-Sync v0.4.0-beta. Integration with the self-hosted version of Terraform Cloud is available as of v0.3.0. - | Network Driver | Description | Features | | -------------- | ----------- | -------- | | [Terraform driver](/docs/nia/network-drivers/terraform) | Consul-Terraform-Sync automates a local installation of the [Terraform CLI](https://www.terraform.io/) | - Local Terraform execution
- Local workspace directories
- [Backend options](/docs/nia/configuration#backend) available for state storage
| diff --git a/website/content/docs/nia/network-drivers/terraform-cloud.mdx b/website/content/docs/nia/network-drivers/terraform-cloud.mdx index 67359a8835..e7e6fe5bbc 100644 --- a/website/content/docs/nia/network-drivers/terraform-cloud.mdx +++ b/website/content/docs/nia/network-drivers/terraform-cloud.mdx @@ -12,8 +12,6 @@ description: >- which is available with Consul Enterprise. --> **Beta:** The integration with the HashiCorp managed service version of Terraform Cloud is currently only available in Consul-Terraform-Sync v0.4.0-beta. Integration with the self-hosted version of Terraform Cloud is available as of v0.3.0. - Consul-Terraform-Sync is more powerful when you integrate it with [Terraform Cloud](https://www.terraform.io/cloud). Integrating with Terraform Cloud provides features, such as enhanced workspaces and insight into Terraform operations as Consul-Terraform-Sync dynamically updates your network infrastructure. Consul-Terraform-Sync is compatible with both the [self-hosted](https://www.hashicorp.com/products/terraform/editions/enterprise) and [managed service](https://www.hashicorp.com/products/terraform/editions/cloud) versions of Terraform Cloud. diff --git a/website/content/docs/nia/tasks.mdx b/website/content/docs/nia/tasks.mdx index 2849aa3496..3e27ae5394 100644 --- a/website/content/docs/nia/tasks.mdx +++ b/website/content/docs/nia/tasks.mdx @@ -134,8 +134,6 @@ One particular condition configuration, [`regexp`](/docs/nia/configuration#regex ### Consul KV Condition --> **Beta:** This feature is currently only available in Consul-Terraform-Sync v0.4.0-beta. - Tasks with a consul-kv condition monitor and execute on Consul KV changes for KV pairs that satisfy the condition configuration. The consul-kv condition operates by monitoring the [Consul KV API](/api-docs/kv#read-key) and executing the task when a configured KV entry is created, deleted, or updated. Based on the `recurse` option, the condition either monitors a single Consul KV pair for a given path or monitors all pairs that are prefixed by that path. In the example below, because `recurse` is set to true, the `path` option is treated as a prefix. Changes to an entry with the key `my-key` and an entry with the key `my-key/another-key` would both trigger the task. If `recurse` were set to false, then only changes to `my-key` would trigger the task. diff --git a/website/content/docs/nia/terraform-modules.mdx b/website/content/docs/nia/terraform-modules.mdx index 503e73f71c..b314f75c4f 100644 --- a/website/content/docs/nia/terraform-modules.mdx +++ b/website/content/docs/nia/terraform-modules.mdx @@ -126,8 +126,6 @@ Similarly, if you include the `catalog_services` variable in your module, we rec ### Consul KV Variable --> **Beta:** This feature is currently only available in Consul-Terraform-Sync v0.4.0-beta. - If you are creating a module for a [consul-kv condition](/docs/nia/tasks#consul-kv-condition), then you have the option to add the `consul_kv` variable, which contains a map of the keys and values for the Consul KV pairs. If your module would benefit from consuming this information, you can copy the `consul_kv` variable declaration to your `variables.tf` file in addition to the other variables. ```hcl From c6cce84418e9afcd4ae2771edd4d7cc675a7deee Mon Sep 17 00:00:00 2001 From: Melissa Kam Date: Tue, 5 Oct 2021 10:09:47 -0500 Subject: [PATCH 04/20] docs/nia: Clarify that all TFC tiers are supported --- website/content/docs/nia/network-drivers/terraform-cloud.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/content/docs/nia/network-drivers/terraform-cloud.mdx b/website/content/docs/nia/network-drivers/terraform-cloud.mdx index e7e6fe5bbc..9b35e504ee 100644 --- a/website/content/docs/nia/network-drivers/terraform-cloud.mdx +++ b/website/content/docs/nia/network-drivers/terraform-cloud.mdx @@ -12,7 +12,7 @@ description: >- which is available with Consul Enterprise. -Consul-Terraform-Sync is more powerful when you integrate it with [Terraform Cloud](https://www.terraform.io/cloud). Integrating with Terraform Cloud provides features, such as enhanced workspaces and insight into Terraform operations as Consul-Terraform-Sync dynamically updates your network infrastructure. Consul-Terraform-Sync is compatible with both the [self-hosted](https://www.hashicorp.com/products/terraform/editions/enterprise) and [managed service](https://www.hashicorp.com/products/terraform/editions/cloud) versions of Terraform Cloud. +Consul-Terraform-Sync is more powerful when you integrate it with [Terraform Cloud](https://www.terraform.io/cloud). Integrating with Terraform Cloud provides features, such as enhanced workspaces and insight into Terraform operations as Consul-Terraform-Sync dynamically updates your network infrastructure. Consul-Terraform-Sync is compatible with both the [self-hosted](https://www.hashicorp.com/products/terraform/editions/enterprise) and [managed service](https://www.hashicorp.com/products/terraform/editions/cloud) versions of Terraform Cloud. It also supports all [tiers](https://www.hashicorp.com/products/terraform/pricing) of the Terraform Cloud managed service. This page describes how the Terraform Cloud driver operates within Consul-Terraform-Sync. From d0cd720ea9dd4e1a6ba17e5daccf67c4d084172a Mon Sep 17 00:00:00 2001 From: lornasong Date: Wed, 13 Oct 2021 10:58:12 -0400 Subject: [PATCH 05/20] docs/nia: scheduled tasks (#11283) * docs/nia: scheduled tasks Add basic scheduled task documentation * Add source input documentation Co-authored-by: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> * Fix some links * Update Optional Input Variables section * Apply suggestions from code review Co-authored-by: Melissa Kam <3768460+mkam@users.noreply.github.com> * Add source input documentation Co-authored-by: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> * updated docs with new "source input" terminology where necessary * Apply suggestions from code review applied Karl's recommendations Co-authored-by: mrspanishviking * addressed code review comments - added rendered input examples to terraform-modules.mdx - added hyperlinks to terraform-modules.mdx in configuration.mdx - re-worded initial description of Services Source Input in terraform-modules.mdx * addressed code review comments - fixed spacing of regexp parameter for services source input - reworded description of source input in terraform-modules.mdx * Update from feedback - Add "currently" to clarify source input is currently only supported with schedule condition - Reword inspect mode behavaior for scheduled tasks Co-authored-by: Michael Wilkerson <62034708+wilkermichael@users.noreply.github.com> Co-authored-by: Melissa Kam <3768460+mkam@users.noreply.github.com> Co-authored-by: Michael Wilkerson Co-authored-by: mrspanishviking --- website/content/docs/nia/architecture.mdx | 10 +- website/content/docs/nia/configuration.mdx | 98 +++++++++- website/content/docs/nia/index.mdx | 22 ++- website/content/docs/nia/tasks.mdx | 82 ++++++-- .../content/docs/nia/terraform-modules.mdx | 182 +++++++++++++++++- 5 files changed, 359 insertions(+), 35 deletions(-) diff --git a/website/content/docs/nia/architecture.mdx b/website/content/docs/nia/architecture.mdx index 59f5318611..beaebb4044 100644 --- a/website/content/docs/nia/architecture.mdx +++ b/website/content/docs/nia/architecture.mdx @@ -37,7 +37,7 @@ proxy when an instance goes unhealthy. ## Tasks A task is the action triggered by the updated data monitored in Consul. It -takes the that dynamic service data and translates it into a call to the +takes the dynamic service data and translates it into a call to the infrastructure application to configure it with the updates. It uses a driver to push out these updates, the initial driver being a local Terraform run. An example of a task is to automate a firewall security policy rule with @@ -48,13 +48,13 @@ discovered IP addresses for a set of Consul services. A driver encapsulates the resources required to communicate the updates to the network infrastructure. The following [drivers](/docs/nia/network-drivers#terraform) are supported: - * Terraform driver - * Terraform Cloud driver +- Terraform driver +- Terraform Cloud driver Each driver includes a set of providers that [enables support](/docs/nia/terraform-modules) for a wide variety of infrastructure applications. ## Security Guidelines -The [Secure Consul-Terraform-Sync for Production](https://learn.hashicorp.com/tutorials/consul/consul-terraform-sync-secure?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) -tutorial contains a checklist of best practices to secure your +The [Secure Consul-Terraform-Sync for Production](https://learn.hashicorp.com/tutorials/consul/consul-terraform-sync-secure?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) +tutorial contains a checklist of best practices to secure your Consul-Terraform-Sync installation for a production environment. diff --git a/website/content/docs/nia/configuration.mdx b/website/content/docs/nia/configuration.mdx index 6afa67c0da..f5fc39d87a 100644 --- a/website/content/docs/nia/configuration.mdx +++ b/website/content/docs/nia/configuration.mdx @@ -27,7 +27,7 @@ buffer_period { } ``` -- `buffer_period` - Configures the default buffer period for all [tasks](#task) to dampen the effects of flapping services to downstream network devices. It defines the minimum and maximum amount of time to wait for the cluster to reach a consistent state and accumulate changes before triggering task executions. The default is enabled to reduce the number of times downstream infrastructure is updated within a short period of time. This is useful to enable in systems that have a lot of flapping. +- `buffer_period` - Configures the default buffer period for all dynamic [tasks](#task) to dampen the effects of flapping services to downstream network devices. It defines the minimum and maximum amount of time to wait for the cluster to reach a consistent state and accumulate changes before triggering task executions. The default is enabled to reduce the number of times downstream infrastructure is updated within a short period of time. This is useful to enable in systems that have a lot of flapping. Buffer periods do not apply to scheduled tasks. - `enabled` - (bool: true) Enable or disable buffer periods globally. Specifying `min` will also enable it. - `min` - (string: "5s") The minimum period of time to wait after changes are detected before triggering related tasks. - `max` - (string: "20s") The maximum period of time to wait after changes are detected before triggering related tasks. If `min` is set, the default period for `max` is 4 times the value of `min`. @@ -130,8 +130,10 @@ task { - `providers` - (list[string]) Providers is the list of provider names the task is dependent on. This is used to map [Terraform provider configuration](#terraform-provider) to the task. - `services` - (list[string]) Required depending on [`condition`](#condition) configuration. Services is the list of logical service names or service IDs the task executes on. Consul-Terraform-Sync monitors the Consul Catalog for changes to these services and triggers the task to run. Any service value not explicitly defined by a `service` block with a matching ID is assumed to be a logical service name in the default namespace. Alternative to configuring `services`, a `condition` can be configured so that the task does not trigger on changes to services (default behavior) but instead trigger on a different condition. See [Task Condition](#task-condition) configuration for more details. - `source` - (string: required) Source is the location the driver uses to discover the Terraform module used for automation. The source is the module path which can be local or remote on the [Terraform Registry](https://registry.terraform.io/) or private module registry. Read more on [Terraform module source and other supported types here](https://www.terraform.io/docs/modules/sources.html). + - To use a private module with the [`terraform` driver](#terraform-driver), run the command [`terraform login [hostname]`](https://learn.hashicorp.com/tutorials/terraform/cloud-login) to authenticate the local Terraform CLI prior to starting Consul-Terraform-Sync. - To use a private module with the [`terraform_cloud` driver](#terraform-cloud-driver), no extra steps are needed. + ```hcl // local module example: "./terraform-cts-hello" source = "" @@ -142,6 +144,7 @@ task { // private module example: "my.tfe.hostname.io/my-org/hello/cts" source = "///" ``` + - `variable_files` - (list[string]) Specifies list of paths to [Terraform variable definition files (`.tfvars`)](https://www.terraform.io/docs/configuration/variables.html#variable-definitions-tfvars-files). The content of these files should consist of only variable name assignments. The variable assignments must match the corresponding variable declarations made available by the Terraform module for the task. - Variables are loaded in the order they appear in the files. Duplicate variables are overwritten with the later value. _Unless specified by the module, configure arguments for Terraform providers using [`terraform_provider` blocks](#terraform-provider)._ ```hcl @@ -154,11 +157,12 @@ task { ``` - `version` - (string) The version of the provided source the task will use. For the [Terraform driver](#terraform-driver), this is the module version. The latest version will be used as the default if omitted. - `working_dir` - (string) The working directory to manage generated artifacts by Consul-Terraform-Sync for this task, including Terraform configuration files. By default, a working directory is created for each task as a subdirectory in the base [`working_dir`](#working_dir), e.g. `sync-tasks/task-name`. -- `buffer_period` - Configures the buffer period for the task to dampen the effects of flapping services to downstream network devices. It defines the minimum and maximum amount of time to wait for the cluster to reach a consistent state and accumulate changes before triggering task execution. The default is inherited from the top level [`buffer_period` block](#global-config-options). If configured, these values will take precedence over the global buffer period. This is useful to enable for a task that is dependent on services that have a lot of flapping. +- `buffer_period` - Configures the buffer period for a dynamic task to dampen the effects of flapping services to downstream network devices. It defines the minimum and maximum amount of time to wait for the cluster to reach a consistent state and accumulate changes before triggering task execution. The default is inherited from the top level [`buffer_period` block](#global-config-options). If configured, these values will take precedence over the global buffer period. This is useful to enable for a task that is dependent on services that have a lot of flapping. Buffer periods do not apply to scheduled tasks. - `enabled` - (bool) Enable or disable buffer periods for this task. Specifying `min` will also enable it. - `min` - (string: "5s") The minimum period of time to wait after changes are detected before triggering related tasks. - `max` - (string: "20s") The maximum period of time to wait after changes are detected before triggering related tasks. If `min` is set, the default period for `max` is 4 times the value of `min`. - `condition` - (obj) The requirement that, when met, triggers Consul-Terraform-Sync to execute the task. When unconfigured, the default condition is to trigger the task on changes in the services configured in [`services`](#services). Only one `condition` may be configured per task. Consul-Terraform-Sync supports different types of conditions, which each have their own configuration options. See [Task Condition](#task-condition) configuration for full details on configuration options for each condition type. +- `source_input` - (obj) Specifies a Consul object containing values or metadata to be provided to the Terraform Module. When source input is empty, source input will be determined by the condition or services list. Only one `source_input` may be configured per task. Consul-Terraform-Sync supports different types of source input, each source input has their own configuration options. The source input block is currently only supported with [schedule condition](#schedule-condition). See [Task Source Input](#task-source-input) configuration for full details on configuration options for each source input type. - `terraform_version` - (string) The version of Terraform to use for the Terraform Cloud workspace associated with the task. Defaults to the latest compatible version supported by the organization. This option is only available when used with the [Terraform Cloud driver](#terraform-cloud-driver); otherwise, set the version within the [Terraform driver](#terraform-driver). ### Task Condition @@ -183,6 +187,7 @@ task { } } ``` + - `regexp` - **(beta)** (string) Only services that have a name which matches the regular expression are used by the task. If `regexp` is configured, then [`task.services`](#services) must be omitted or empty. If both a list and a regex are needed, consider including the list as part of the regex or creating separate tasks. #### Catalog-Services Condition @@ -220,7 +225,6 @@ task { - `regexp` - (string) Optional if [`task.services`](/docs/nia/configuration#services) is configured. Either `regexp` or `task.services` or both must be configured. Only services that have a name which matches the regular expression are used by the task. If not provided, `regexp` will default to an exact match on `task.services` list. For example, if `task.services = ["api", "web"]`, then `regexp` will default to `^api$|^web$`. See [Task Execution: Catalog Services Condition](/docs/nia/tasks#catalog-services-condition) for more details on the relationship between `task.services` and `regexp`. Some resources for more information on regular expressions: [regular expression syntax](https://github.com/google/re2/wiki/Syntax), [try out regular expression string matching](https://golang.org/pkg/regexp/#Regexp.MatchString). - `source_includes_var` - (bool: false) Whether or not the module configured at [`task.source`](#source) includes the [`catalog_services` variable](/docs/nia/terraform-modules#catalog-services-variable). Please refer to the documentation of the selected module for guidance on how to configure this field. If configured inconsistently with the module, Consul-Terraform-Sync will error and exit. - #### Consul KV Condition A consul-kv condition block configures a task to only execute on changes to a Consul KV entry. The condition can be configured for a single Consul KV entry or for any Consul KV entries that are prefixed with a given path. @@ -249,7 +253,91 @@ task { - `recurse` - (bool: false) Setting to `true` instructs Consul-Terraform-Sync to treat the path as a prefix instead of a literal match. - `datacenter` - (string) The datacenter of the services to query for the task. If not provided, the datacenter will default to the datacenter of the agent that Consul-Terraform-Sync queries. - `namespace` - (string) The namespace of the services to query for the task. If not provided, the namespace will be inferred from the Consul-Terraform-Sync ACL token or default to the `default` namespace. -- `source_includes_var` - (bool: false) If set to `true`, then Consul-Terraform-Sync will include the [`consul_kv` variable](/docs/nia/terraform-modules#consul-kv-variable) as an input to the module specified in the [`task.source`](#source) field. Refer to the documentation of the selected module for guidance on how to configure this field. If configured inconsistently with the module, Consul-Terraform-Sync will error and exit. +- `source_includes_var` - (bool: false) If set to `true`, then Consul-Terraform-Sync will include the [`consul_kv` variable](/docs/nia/terraform-modules#consul-kv-variable) as a source input. Refer to the documentation of the selected module for guidance on how to configure this field. If configured inconsistently with the module, Consul-Terraform-Sync will error and exit. + +#### Schedule Condition + +A scheduled task has a schedule condition block, which defines the schedule for executing the task. Unlike a dynamic task, a scheduled task does not dynamically trigger on changes in Consul. + +Schedule tasks also rely on additional task configuration, separate from the condition block to determine the source input information to provide to the task module. See [`task.services`](#services) or [`source_input`](#source_input) block configuration for details on how to configure source input. + +See [Task Execution: Schedule Condition](/docs/nia/tasks#schedule-condition) for more information on how tasks are triggered with schedule conditions. + +See [Terraform Module: Source Input](/docs/nia/terraform-modules#source-input) for more information on source input options for a scheduled task. + +```hcl +task { + name = "scheduled_task" + description = "execute every Monday using service information from web and db" + services = ["web", "db"] + source = "path/to/module" + condition "schedule" { + cron = "* * * * Mon" + } +} +``` + +- `cron` - (string: required) The CRON expression that dictates the schedule to trigger the task. For more information on CRON expressions, see the [cronexpr parsing library](https://github.com/hashicorp/cronexpr). + +### Task Source Input + +You can add an optional `source_input` block to the `task` block. The `source_input` block specifies a Consul object containing values or metadata to be provided to the Terraform Module. Consul-Terraform-Sync supports the following source input types. + +~> **The source input block is currently only supported when using a schedule condition.** Adding a `source_input` block alongside any other type of condition will result in an error. To accomplish a similar behavior with other condition blocks, use the `source_includes_var` field. + +#### Services Source Input + +This `services` source input object defines services registered to Consul whose metadata will be used as [services source input to the Terraform Module](/docs/nia/terraform-modules/#services-source-input). The following parameters are supported: + +| Parameter | Description | Default | Required | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | ------------------------------------------------------------- | +| `regexp` | String value matching the names of Consul services to monitor. Only services that have a name matching the regular expression are used by the task.

If `regexp` is configured, then [`task.services`](#services) must be omitted or empty.

If both a list and a regex are needed, consider including the list as part of the regex or creating separate tasks. | empty string `""` | Optional unless the `task.services` option is not configured. | + +In the following example, the scheduled task queries all Consul services with `web` as the suffix. The metadata of matching services are provided to the Terraform module. + +```hcl +task { + name = "schedule_condition_task" + description = "execute every Monday using information from service names starting with web" + source = "path/to/module" + source_input “services” { + regexp = "^web.*" + } + condition "schedule" { + cron = "* * * * Mon" + } +} +``` + +#### Consul KV Source Input + +A Consul KV source input block defines changes to Consul KV that will be monitored. These changes will then be provided as [Consul KV source input to the Terraform Module](/docs/nia/terraform-modules/#consul-kv-source-input). The source input can be configured for a single Consul KV entry or for any Consul KV entries that are prefixed with a given path. The following parameters are supported: + +| Parameter | Description | Default | Required | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------- | +| `path` | String value that specifies the path of the key used by the task. The path can point to a single Consul KV entry or several entries within the path. | none | Required | +| `recurse` | Boolean value that enables Consul-Terraform-Sync to treat the path as a prefix. If set to `false`, the path will be treated as a literal match. | `false` | Optional | +| `datacenter` | String value specifying the name of a datacenter to query for the task. | Datacenter of the agent that Consul-Terraform-Sync queries. | Optional | +| `namespace` |
String value indicating the namespace of the services to query for the task. | In order of precedence:
1. Inferred from the Consul-Terraform-Sync ACL token
2. The `default` namespace. | Optional | + +In the following example, the scheduled task queries datacenter `dc1` in the `default` namespace for changes to the value held by the key `my-key`. + +```hcl +task { + name = "schedule_condition_task" + description = "execute every Monday using information from Consul KV entry my-key" + source = "path/to/module" + source_input "consul-kv" { + path = "my-key" + recurse = false + datacenter = "dc1" + namespace = "default" + } + condition "schedule" { + cron = "* * * * Mon" + } +} +``` ## Network Drivers @@ -328,7 +416,7 @@ driver "terraform-cloud" { - `organization` - (string) The Terraform Cloud organization that hosts the managed workspaces by Consul-Terraform-Sync. Can be overridden with the `TFC_ORGANIZATION` environment variable. - `token` - (string) Required [Team API token](https://www.terraform.io/docs/cloud/users-teams-organizations/api-tokens.html#team-api-tokens) used for authentication with Terraform Cloud and workspace management. Only workspace permissions are needed for Consul-Terraform-Sync. The token can also be provided using the `TFC_TOKEN` environment variable. - We recommend creating a dedicated team and team API token to isolate automation by Consul-Terraform-Sync from other Terraform Cloud operations. -- `workspace_prefix` - (string) Specifies a prefix to prepend to the automatically-generated workspace names used for automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be ``. For example, if you configure the prefix as "cts_", then a task with the name "task_firewall" will have the workspace name "cts_task_firewall". +- `workspace_prefix` - (string) Specifies a prefix to prepend to the automatically-generated workspace names used for automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be ``. For example, if you configure the prefix as "cts\_", then a task with the name "task_firewall" will have the workspace name "cts_task_firewall". - `required_providers` - (obj: required) Declare each Terraform provider used across all tasks. This can be configured the same as how you would configure [Terraform `terraform.required_providers`](https://www.terraform.io/docs/configuration/provider-requirements.html#requiring-providers) field to specify the source and version for each provider. Consul-Terraform-Sync will process these requirements when preparing each task that uses the provider. - `tls` - Configure TLS to allow HTTPS connections to [Terraform Enterprise](https://www.terraform.io/docs/enterprise/install/installer.html#tls-key-amp-cert). - `enabled` - (bool) Enable TLS. Providing a value for any of the TLS options will enable this parameter implicitly. diff --git a/website/content/docs/nia/index.mdx b/website/content/docs/nia/index.mdx index ea011b7d2d..58d59c5bac 100644 --- a/website/content/docs/nia/index.mdx +++ b/website/content/docs/nia/index.mdx @@ -19,12 +19,22 @@ Consul-Terraform-Sync executes one or more automation tasks with the most recent ## Glossary -**Condition** - A task-level defined environmental requirement that, when met, triggers the Consul-Terraform-Sync binary to execute the related task to update network infrastructure. +**Condition** - A task-level defined environmental requirement. When a task’s condition is met, Consul-Terraform-Sync executes that task to update network infrastructure. Depending on the condition type, the condition definition may also define and enable the source input that the task provides to the configured Terraform Module. **Consul-Terraform-Sync** - [GitHub repo](https://github.com/hashicorp/consul-terraform-sync) and binary/CLI name for the project that is used to perform Network Infrastructure Automation. +**Dynamic Tasks** - A dynamic task is a type of task that is dynamically triggered on a change to any relevant Consul catalog values e.g. service instances, Consul KV, catalog-services. See scheduled tasks for a type of non-dynamic task. + +-> **Note:** The terminology "tasks" used throughout the documentation refers to all types of tasks except when specifically stated otherwise. + **Network Drivers** - Consul-Terraform-Sync uses [network drivers](/docs/nia/network-drivers) to execute and update network infrastructure. Drivers transform Consul service-level information into downstream changes by processing and abstracting API and resource details tied to specific network infrastructure. +**Scheduled Tasks** - A scheduled task is a type of task that is triggered only on a schedule. It is configured with a [schedule condition](/docs/nia/configuration#schedule-condition). + +-> **Note:** The terminology "tasks" used throughout the documentation refers to all types of tasks except when specifically stated otherwise. + +**Source Input** - A source input defines objects that provide values or metadata to the Terraform module. See [source input](/docs/nia/terraform-modules#source-input) for the supported metadata and values. For example, a user can configure a Consul KV source input to provide KV pairs as variables to their respective Terraform Module. The source input can be included in two ways. It can be specified as a parameter in a condition using `source_includes_var` and also by using the `source_input` block. + **Network Infrastructure Automation (NIA)** - Enables dynamic updates to network infrastructure devices triggered when specific conditions, such as service changes and registration, are met. **Tasks** - A task is the translation of dynamic service information from the Consul Catalog into network infrastructure changes downstream. @@ -37,11 +47,11 @@ Consul-Terraform-Sync executes one or more automation tasks with the most recent ## Getting Started With Network Infrastructure Automation -The [Network Infrastructure Automation (NIA)](https://learn.hashicorp.com/collections/consul/network-infrastructure-automation?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) -collection contains examples on how to configure Consul-Terraform-Sync to -perform Network Infrastructure Automation. The collection contains also a -tutorial to secure your Consul-Terraform-Sync configuration for a production -environment and one to help you build you own Consul-Terraform-Sync compatible +The [Network Infrastructure Automation (NIA)](https://learn.hashicorp.com/collections/consul/network-infrastructure-automation?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) +collection contains examples on how to configure Consul-Terraform-Sync to +perform Network Infrastructure Automation. The collection contains also a +tutorial to secure your Consul-Terraform-Sync configuration for a production +environment and one to help you build you own Consul-Terraform-Sync compatible module. ## Community diff --git a/website/content/docs/nia/tasks.mdx b/website/content/docs/nia/tasks.mdx index 3e27ae5394..303cfb8de5 100644 --- a/website/content/docs/nia/tasks.mdx +++ b/website/content/docs/nia/tasks.mdx @@ -30,9 +30,13 @@ A task can be either enabled or disabled using the [task cli](/docs/nia/cli/task ## Task Execution -An enabled task can be configured to monitor and execute on different types of conditions. For example, on changes to services ([services condition](/docs/nia/tasks#services-condition)) or on service registration and deregistration ([catalog-services condition](/docs/nia/tasks#catalog-services-condition)). +An enabled task can be configured to monitor and execute on different types of conditions, such as changes to services ([services condition](/docs/nia/tasks#services-condition)) or service registration and deregistration ([catalog-services condition](/docs/nia/tasks#catalog-services-condition)). -A task can also monitor but not execute on other variables that are simply included to provide additional information to the task's module. For example, a task with a catalog-services condition may execute on registration changes but additionally monitor service instances for IP information. The details of what values are monitored and what values can execute the task are determined by the module. +A task can also monitor, but not execute on, other variables that provide additional information to the task's module. For example, a task with a catalog-services condition may execute on registration changes, and monitor service instances for IP information. + +A source input can be specified that implicitly includes variables to be provided to the task’s module. For example, a task can specify a Consul KV source input. The specified KV keys or key paths would be monitored for changes. Any changes detected would be included as input information for the modules. The module determines the details of what values are monitored and what values can execute the task. + +~> **The source input block is currently only supported when using a schedule condition.** Adding a source input block alongside any other type of condition will result in an error. To accomplish a similar behaviour with other condition blocks, use the `source_includes_var` field. Below are details on the types of execution conditions that Consul-Terraform-Sync supports. @@ -105,7 +109,8 @@ Tasks with a catalog-services condition monitor and execute on service registrat The catalog-services condition operates by monitoring the [Catalog List Services API](/api-docs/catalog#list-services) and executing the task when services are added or removed in the list of registered services. Note, the task does not execute on changes to the tags of the list of services. This is similar to how changes to service instance information, mentioned above, also does not execute a task. -Below is an example configuration for a task that will execute when a service with a name that matches the "web.*" regular expression in datacenter "dc1" has a registration change. It additionally monitors but does not execute on service instance changes to "web-api" in datacenter "dc2". +Below is an example configuration for a task that will execute when a service with a name that matches the "web.\*" regular expression in datacenter "dc1" has a registration change. It additionally monitors but does not execute on service instance changes to "web-api" in datacenter "dc2". + ```hcl task { name = "catalog_service_condition_task" @@ -158,6 +163,49 @@ task { If the task condition's [`source_includes_var`](/docs/nia/configuration#source_includes_var-1) field is set to `true`, then the value of the Consul KV pair(s) will be available in the [`consul_kv` input variable](/docs/nia/terraform-modules#consul-kv-variable). To use the variable, add `consul_kv` as an input variable to the module, in addition to the required `services` variable. The condition type and `source_includes_var` configuration value should be documented in the module so that users can reference them when configuring a task. +### Schedule Condition + +All scheduled tasks must be configured with a schedule condition. The schedule condition sets the cadence to trigger a task with a [`cron`](/docs/nia/configuration#cron) configuration. The schedule condition block does not support parameters to configure source input. As a result, inputs must be configured separately. You can configure [`task.services`](/docs/nia/configuration#services) or a [`source_input` block](/docs/nia/configuration#source_input) to set the source input. + +Below is an example configuration for a task that will execute every Monday, which is set by the schedule condition’s [`cron`](/docs/nia/configuration#cron) configuration. The source input is defined by the `task.services` configuration. When the task is triggered on Monday, it will retrieve the latest information on 'web' and 'db' from Consul and provide this to the module’s input variables. + +```hcl +task { + name = "scheduled_task" + description = "execute every Monday using service information from web and db" + services = ["web", "db"] + source = "path/to/module" + condition "schedule" { + cron = "* * * * Mon" + } +} +``` + +Below are the available options for source input types and how to configure them: + +- [Services source input](/docs/nia/terraform-modules/#services-source-input): configure through [`task.services`](/docs/nia/configuration#services) or [`source_input "services"`](/docs/nia/configuration#services-source-input) +- [Consul KV source input](/docs/nia/terraform-modules/#consul-kv-source-input): configure through [`source_input "consul-kv"`](/docs/nia/configuration#consul-kv-source-input) + +#### Running Behavior + +Scheduled tasks generally run on schedule, but they can be triggered on demand when running Consul-Terraform-Sync in the following ways: + +- [Long-running mode](/docs/nia/cli#long-running-mode): At the beginning of the long-running mode, Consul-Terraform-Sync first passes through a once-mode phase in which all tasks are executed once. Scheduled tasks will trigger once during this once-mode phase. This behavior also applies to tasks that are not scheduled. After once-mode has completed, scheduled tasks subsequently trigger on schedule. + +- [Inspect mode](/docs/nia/cli#inspect-mode): When running in inspect mode, the terminal will output a plan of proposed updates that would be made if the tasks were to trigger at that moment and then exit. The outputted plan for a scheduled task is also the proposed updates that would be made if the task was triggered at that moment, even if off-schedule. + +- [Once mode](/docs/nia/cli#once-mode): During the once mode, all tasks are only triggered one time. Scheduled tasks will execute during once mode even if not on the schedule. + +- [Enable CLI](/docs/nia/cli/task#task-enable): When a task is enabled through the CLI, any type of task, including scheduled tasks, will be triggered at that time. + +#### Buffer Period + +Because scheduled tasks trigger on a configured cadence, buffer periods are disabled for scheduled tasks. Any configured `buffer_period` at the global level or task level will only apply to dynamic tasks and not scheduled ones. + +#### Events + +[Events](#event) are stored each time a task executes. For scheduled tasks, an event will be stored each time the task triggers on schedule regardless of if there was a change in Consul catalog. + ## Task Automation Consul-Terraform-Sync will attempt to execute each enabled task once upon startup to synchronize infrastructure with the current state of Consul. The daemon will stop and exit if any error occurs while preparing the automation environment or executing a task for the first time. This helps ensure tasks have proper configuration and are executable before the daemon transitions into running tasks in full automation as service changes are discovered over time. As a result, it is not recommended to configure a task as disabled from the start. After all tasks have successfully executed once, task failures during automation will be logged and retried or attempted again after a subsequent change. @@ -167,17 +215,21 @@ Tasks are executed near-real time when service changes are detected. For service ## Status Information Status-related information is collected and offered via [status API](/docs/nia/api#status) to provide visibility into what and how the tasks are running. Information is offered in three-levels (lowest to highest): - - Event data - - Task status - - Overall status + +- Event data +- Task status +- Overall status These three levels form a hierarchy where each level of data informs the one higher. The lowest-level, event data, is collected each time a task runs to update network infrastructure. This event data is then aggregated to inform individual task statuses. The count distribution of all the task statuses inform the overall status's task summary. ### Event -Each time a task's services has an update, Consul-Terraform-Sync takes a series of steps in order to update network infrastructure. This process starts with updating the task's templates to fetch new service data from Consul and ends with any post-actions after modifying network infrastructure. An event is a data structure that captures information on this process of updating network infrastructure. It stores information to help understand if the update to network infrastructure was successful or not, and it stores any errors that occurred. Because disabled tasks do not update network infrastructures, they therefore do not have store events until re-enabled. +When a task is triggered, Consul-Terraform-Sync takes a series of steps in order to update the network infrastructure. These steps consist of fetching the latest data from Consul for the task's source inputs and then updating the network infrastructure accordingly. An event captures information across this process. It stores information to help understand if the update to network infrastructure was successful or not and any errors that may have occurred. + +A dynamic task will store an event when it is triggered by a change in Consul. A scheduled task will store an event when it is triggered on schedule, regardless if there is a change in Consul. A disabled task does not update network infrastructures, so it will not store events until until re-enabled. Sample event: + ```json { "id": "ef202675-502f-431f-b133-ed64d15b0e0e", @@ -199,17 +251,14 @@ For complete information on the event structure, see [events in our API document Each time a task runs to update network infrastructure, event data is stored for that run. 5 most recent events are stored for each task, and these stored events are used to determine task status. For example, if the most recent stored event is not successful but the others are, then the task's health status is "errored". Sample task status: + ```json { - "task_name": "task_b", - "status": "errored", - "providers": [ - "null" - ], - "services": [ - "web", - ], - "events_url": "/v1/status/tasks/task_b?include=events", + "task_name": "task_b", + "status": "errored", + "providers": ["null"], + "services": ["web"], + "events_url": "/v1/status/tasks/task_b?include=events" } ``` @@ -220,6 +269,7 @@ Task status information can be retrieved with [task status API](/docs/nia/api#ta Overall status returns a summary of the health statuses across all tasks. The summary is the count of tasks in each health status category. Sample overall status: + ```json { "task_summary": { diff --git a/website/content/docs/nia/terraform-modules.mdx b/website/content/docs/nia/terraform-modules.mdx index b314f75c4f..a1c08d2a5b 100644 --- a/website/content/docs/nia/terraform-modules.mdx +++ b/website/content/docs/nia/terraform-modules.mdx @@ -22,9 +22,185 @@ Below are the two required elements for module compatibility with Consul-Terrafo ### Optional Input Variables -Below are additional input variables provided by Consul-Terraform-Sync that may be included in a module alongside the `services` input variable. Details for each input variable contain guidance on when to include them in a module. +In addition to the required `services` input variable, Consul-Terraform-Sync provides additional, optional input variables to be used within your module. Support for an optional input variable requires two changes: - - [**`catalog_services` input variable**](#catalog-services-variable) - This variable can optionally be included in modules for the [catalog-services condition](/docs/nia/tasks#catalog-services-condition). The declaration of the `catalog_services` input variable can be included at the top of the suggested `variables.tf` file alongside the required `services` input variable and other input variables. This variable functions as the response object from the [Consul catalog list services API](/api-docs/catalog#list-services) and surfaces registered services information. It is structured as a map of lists. +1. Updating the Terraform Module to declare the input variable in the suggested `variables.tf` +1. Adding configuration to the Consul-Terraform-Sync task block to define the source input values that should be provided to the input variables + +See below sections for more information on [defining source input](#source-input) and [declaring optional input variables](#how-to-create-a-compatible-terraform-module) in your Terraform module. + +### Source Input + +A source input allows for a task to satisfy the input requirements defined by the Terraform Module’s [input variables](https://www.terraform.io/docs/language/values/variables.html), and is configured alongside a task’s condition. Both the source input and condition define objects to be monitored, but for differing reasons. The condition defines monitored objects with criteria. When this criteria is satisfied, Consul-Terraform-Sync will then trigger a task. The source input however, defines monitored objects with the intent of providing values or metadata about these objects to the Terraform Module. The source input and condition objects can be the same, such as when `task.services` is provided without a source input block and condition block, but they can also be defined using separate blocks. In this way the source input does not need to be tied to the provided condition in order to satisfy the Terraform Module. + +There are a few ways that a source input can be defined: + +- [**`services` list**](/docs/nia/configuration#services) - The list of services to act as a source input. +- **`source_includes_var` condition field** - If the condition supports this field, and it is set to true, then the condition’s objects will be used as a source input. For example, if a module is defined supporting [`catalog_services` input variable](#catalog-services-variable) then, this field can be set to true in the [catalog-services condition](/docs/nia/tasks#catalog-services-condition). +- [**`source_input` block**](/docs/nia/configuration#source-input) - This block specifically defines a source input. + +Multiple ways of defining a source input adds configuration flexibility, and allows for optional additional input variables to be supported by Consul-Terraform-Sync alongside the `services` input variable. + +These optional input variables include: + +- [**`catalog_services` variable**](#catalog-services-variable) +- [**`consul_kv` variable**](#consul-kv-variable) + +#### Services Source Input + +Tasks configured with a services source input monitor for changes to services. Monitoring is either performed on a configured list of services or on any services matching a provided regex. + +Sample rendered services input: + +```hcl +services = { + "web.test-server.dc1" = { + id = "web" + name = "web" + kind = "" + address = "127.0.0.1" + port = 80 + meta = {} + tags = ["example"] + namespace = "" + status = "passing" + node = "pm8902" + node_id = "307625d3-a1cf-9e85-ff81-12017ca4d848" + node_address = "127.0.0.1" + node_datacenter = "dc1" + node_tagged_addresses = { + lan = "127.0.0.1" + lan_ipv4 = "127.0.0.1" + wan = "127.0.0.1" + wan_ipv4 = "127.0.0.1" + } + node_meta = { + consul-network-segment = "" + } + }, +} +``` + +In order to configure a task with the services source input, the list of services that will be used for the input must be configured in one of the following ways: + +- the task's [`services`](/docs/nia/configuration#services) +- a [`condition "services"` block](/docs/nia/configuration#services-condition) configured with `regexp` and `source_includes_var` set to true +- a [`source_input "services"` block](/docs/nia/configuration#services-source-input) configured with `regexp` + +The services source input operates by monitoring the [Health List Nodes For Service API](/api-docs/health#list-nodes-for-service) and provides the latest service information to the input variables. A complete list of service information that would be provided to the module is expanded below: + +| Attribute | Description | +| ----------------------- | ------------------------------------------------------------------------------------------------- | +| `id` | A unique Consul ID for this service. The service id is unique per Consul agent. | +| `name` | The logical name of the service. Many service instances may share the same logical service name. | +| `address` | IP address of the service host -- if empty, node address should be used. | +| `port` | Port number of the service | +| `meta` | List of user-defined metadata key/value pairs for the service | +| `tags` | List of tags for the service | +| `namespace` | Consul Enterprise namespace of the service instance | +| `status` | Representative status for the service instance based on an aggregate of the list of health checks | +| `node` | Name of the Consul node on which the service is registered | +| `node_id` | ID of the node on which the service is registered. | +| `node_address` | The IP address of the Consul node on which the service is registered. | +| `node_datacenter` | Data center of the Consul node on which the service is registered. | +| `node_tagged_addresses` | List of explicit LAN and WAN IP addresses for the agent | +| `node_meta` | List of user-defined metadata key/value pairs for the node | + +Below is an example configuration for a task that will execute on a schedule and provide information about the services matching the `regexp` parameter to the task’s module. Note that because `regexp` is set, `task.services` is omitted, and since a schedule is being used to trigger task execution, a `condition "services"` block cannot be used. + +```hcl +task { + name = "services_condition_task" + description = "execute on changes to services whose name starts with web" + providers = ["my-provider"] + source = "path/to/services-condition-module" + condition "schedule" { + cron = "* * * * Mon" + } + source_input "services" { + regexp = "^web.*" + } +} +``` + +#### Consul KV Source Input + +Tasks configured with a Consul KV source input monitor Consul KV for changes to KV pairs that satisfy the provided configuration. The Consul KV source input operates by monitoring the [Consul KV API](/api-docs/kv#read-key) and provides these key values to the task’s module. + +Sample rendered consul KV input: + +```hcl +consul_kv = { + "my-key" = "some value" +} +``` + +To configure a task with the Consul KV source input, the KVs which will be used for the input must be configured in one of the following ways: + +- a [`condition "consul-kv"` block](/docs/nia/configuration#consul-kv-condition) configured with the `source_includes_var` set to true. +- a [`source_input "consul-kv"` block](/docs/nia/configuration#consul-kv-source-input). + +Below is a similar example to the one provided in the [Consul KV Condition](/docs/nia/tasks#consul-kv-condition) section. However, the difference in this example is that instead of triggering based on a change to Consul KV, this task will instead execute on a schedule. Once execution is triggered, Consul KV information is then provided to the task’s module. + +```hcl +task { + name = "consul_kv_schedule_task" + description = "executes on Monday monitoring Consul KV" + providers = ["my-provider"] + services = ["web-api"] + source = "path/to/consul-kv-module" + source_input "consul-kv" { + path = "my-key" + recurse = true + datacenter = "dc1" + namespace = "default" + } + condition "schedule" { + cron = "* * * * Mon" + } +} +``` + +#### Catalog Services Source Input + +Tasks configured with a Catalog Services source input monitors for service and tag information provided by the [Catalog List Services API](/api-docs/catalog#list-services). The source input is a map of service names to a list of tags. + +Sample rendered catalog-services input: + +```hcl +catalog_services = { + "api" = ["prod", "staging"] + "consul" = [] + "web" = ["blue", "green"] +} +``` + +To configure a task with the Catalog Services source input, the catalog services which will be used for the input must be configured in one of the following ways: + +- a [`condition "catalog-services"` block](/docs/nia/configuration#consul-kv-condition) configured with `source_includes_var` set to true. + +-> **Note:** Currently there is no support for a `source_input “catalog-services”` block. + +Example of a catalog-services condition which supports source input through source_includes_var: + +```hcl +task { + name = "catalog_services_condition_task" + description = "execute on registration/deregistration of services" + providers = ["my-provider"] + services = ["web-api"] + source = "path/to/catalog-services-module" + condition "catalog-services" { + datacenter = "dc1" + namespace = "default" + regexp = "web.*" + source_includes_var = true + node_meta { + key = "value" + } + } +} +``` ## How to Create a Compatible Terraform Module @@ -147,7 +323,7 @@ Network infrastructure differs vastly across teams and organizations, and the au 4. Set reasonable default values for variables that are optional, or omit default values for variables that are required module arguments. 5. Set the [sensitive argument](https://www.terraform.io/docs/language/values/variables.html#suppressing-values-in-cli-output) for variables that contain secret or sensitive values. When set, Terraform will redact the value from output when Terraform commands are run. -Terraform is an explicit configuration language and requires variables to be declared, typed, and passed explicitly through as module arguments. Consul-Terraform-Sync abstracts this by creating intermediate variables at the root level from values intended for the module. These values are configured by practitioners within the [`task` block](/docs/nia/configuration#variable_files). Value assignments are parsed to interpolate the corresponding variable declaration and are written to the appropriate Terraform files. A few assumptions are made for the intermediate variables: the variables users provide Consul-Terraform-Sync are declared and supported by the module, matching name and type. +Terraform is an explicit configuration language and requires variables to be declared, typed, and passed explicitly through as module arguments. Consul-Terraform-Sync abstracts this by creating intermediate variables at the root level from the source input. These values are configured by practitioners within the [`task` block](/docs/nia/configuration#variable_files). Value assignments are parsed to interpolate the corresponding variable declaration and are written to the appropriate Terraform files. A few assumptions are made for the intermediate variables: the variables users provide Consul-Terraform-Sync are declared and supported by the module, matching name and type. ### Module Guidelines From 55ffd5ffd9067d6c38ba9ec7ec8a5a052b4a2fc2 Mon Sep 17 00:00:00 2001 From: Lorna Song Date: Wed, 13 Oct 2021 11:31:11 -0400 Subject: [PATCH 06/20] Remove extra '\' --- website/content/docs/nia/configuration.mdx | 2 +- website/content/docs/nia/tasks.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/content/docs/nia/configuration.mdx b/website/content/docs/nia/configuration.mdx index f5fc39d87a..95af3cc13d 100644 --- a/website/content/docs/nia/configuration.mdx +++ b/website/content/docs/nia/configuration.mdx @@ -416,7 +416,7 @@ driver "terraform-cloud" { - `organization` - (string) The Terraform Cloud organization that hosts the managed workspaces by Consul-Terraform-Sync. Can be overridden with the `TFC_ORGANIZATION` environment variable. - `token` - (string) Required [Team API token](https://www.terraform.io/docs/cloud/users-teams-organizations/api-tokens.html#team-api-tokens) used for authentication with Terraform Cloud and workspace management. Only workspace permissions are needed for Consul-Terraform-Sync. The token can also be provided using the `TFC_TOKEN` environment variable. - We recommend creating a dedicated team and team API token to isolate automation by Consul-Terraform-Sync from other Terraform Cloud operations. -- `workspace_prefix` - (string) Specifies a prefix to prepend to the automatically-generated workspace names used for automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be ``. For example, if you configure the prefix as "cts\_", then a task with the name "task_firewall" will have the workspace name "cts_task_firewall". +- `workspace_prefix` - (string) Specifies a prefix to prepend to the automatically-generated workspace names used for automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be ``. For example, if you configure the prefix as "cts_", then a task with the name "task_firewall" will have the workspace name "cts_task_firewall". - `required_providers` - (obj: required) Declare each Terraform provider used across all tasks. This can be configured the same as how you would configure [Terraform `terraform.required_providers`](https://www.terraform.io/docs/configuration/provider-requirements.html#requiring-providers) field to specify the source and version for each provider. Consul-Terraform-Sync will process these requirements when preparing each task that uses the provider. - `tls` - Configure TLS to allow HTTPS connections to [Terraform Enterprise](https://www.terraform.io/docs/enterprise/install/installer.html#tls-key-amp-cert). - `enabled` - (bool) Enable TLS. Providing a value for any of the TLS options will enable this parameter implicitly. diff --git a/website/content/docs/nia/tasks.mdx b/website/content/docs/nia/tasks.mdx index 303cfb8de5..e3b050e89a 100644 --- a/website/content/docs/nia/tasks.mdx +++ b/website/content/docs/nia/tasks.mdx @@ -109,7 +109,7 @@ Tasks with a catalog-services condition monitor and execute on service registrat The catalog-services condition operates by monitoring the [Catalog List Services API](/api-docs/catalog#list-services) and executing the task when services are added or removed in the list of registered services. Note, the task does not execute on changes to the tags of the list of services. This is similar to how changes to service instance information, mentioned above, also does not execute a task. -Below is an example configuration for a task that will execute when a service with a name that matches the "web.\*" regular expression in datacenter "dc1" has a registration change. It additionally monitors but does not execute on service instance changes to "web-api" in datacenter "dc2". +Below is an example configuration for a task that will execute when a service with a name that matches the "web.*" regular expression in datacenter "dc1" has a registration change. It additionally monitors but does not execute on service instance changes to "web-api" in datacenter "dc2". ```hcl task { From 62980ffaa2b2f9ba84cfc8f8a92d6cca7906aa16 Mon Sep 17 00:00:00 2001 From: FFMMM Date: Wed, 13 Oct 2021 09:25:30 -0700 Subject: [PATCH 07/20] fix: only add prom autopilot gauges to servers (#11241) Signed-off-by: FFMMM --- .changelog/11241.txt | 3 +++ agent/setup.go | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 .changelog/11241.txt diff --git a/.changelog/11241.txt b/.changelog/11241.txt new file mode 100644 index 0000000000..debe857949 --- /dev/null +++ b/.changelog/11241.txt @@ -0,0 +1,3 @@ +```release-note:bug +telemetry: Consul Clients no longer emit Autopilot metrics. +``` \ No newline at end of file diff --git a/agent/setup.go b/agent/setup.go index 02991d94ff..2db06049bc 100644 --- a/agent/setup.go +++ b/agent/setup.go @@ -83,7 +83,8 @@ func NewBaseDeps(configLoader ConfigLoader, logOut io.Writer) (BaseDeps, error) return d, fmt.Errorf("failed to setup node ID: %w", err) } - gauges, counters, summaries := getPrometheusDefs(cfg.Telemetry) + isServer := result.RuntimeConfig.ServerMode + gauges, counters, summaries := getPrometheusDefs(cfg.Telemetry, isServer) cfg.Telemetry.PrometheusOpts.GaugeDefinitions = gauges cfg.Telemetry.PrometheusOpts.CounterDefinitions = counters cfg.Telemetry.PrometheusOpts.SummaryDefinitions = summaries @@ -187,7 +188,7 @@ func newConnPool(config *config.RuntimeConfig, logger hclog.Logger, tls *tlsutil // getPrometheusDefs reaches into every slice of prometheus defs we've defined in each part of the agent, and appends // all of our slices into one nice slice of definitions per metric type for the Consul agent to pass to go-metrics. -func getPrometheusDefs(cfg lib.TelemetryConfig) ([]prometheus.GaugeDefinition, []prometheus.CounterDefinition, []prometheus.SummaryDefinition) { +func getPrometheusDefs(cfg lib.TelemetryConfig, isServer bool) ([]prometheus.GaugeDefinition, []prometheus.CounterDefinition, []prometheus.SummaryDefinition) { // TODO: "raft..." metrics come from the raft lib and we should migrate these to a telemetry // package within. In the mean time, we're going to define a few here because they're key to monitoring Consul. raftGauges := []prometheus.GaugeDefinition{ @@ -204,7 +205,6 @@ func getPrometheusDefs(cfg lib.TelemetryConfig) ([]prometheus.GaugeDefinition, [ // Build slice of slices for all gauge definitions var gauges = [][]prometheus.GaugeDefinition{ cache.Gauges, - consul.AutopilotGauges, consul.RPCGauges, consul.SessionGauges, grpc.StatsGauges, @@ -216,6 +216,11 @@ func getPrometheusDefs(cfg lib.TelemetryConfig) ([]prometheus.GaugeDefinition, [ raftGauges, } + // TODO(ffmmm): conditionally add only leader specific metrics to gauges, counters, summaries, etc + if isServer { + gauges = append(gauges, consul.AutopilotGauges) + } + // Flatten definitions // NOTE(kit): Do we actually want to create a set here so we can ensure definition names are unique? var gaugeDefs []prometheus.GaugeDefinition From b16fe098af34e518cd36165905d447c2a3e0efee Mon Sep 17 00:00:00 2001 From: Blake Covarrubias Date: Wed, 13 Oct 2021 10:15:25 -0700 Subject: [PATCH 08/20] docs: Redirect /docs/commands/acl/role to new URL --- website/redirects.next.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/website/redirects.next.js b/website/redirects.next.js index 1300162fbe..42c355f5c2 100644 --- a/website/redirects.next.js +++ b/website/redirects.next.js @@ -1083,6 +1083,11 @@ module.exports = [ destination: '/docs/upgrading/compatibility', permanent: true, }, + { + source: '/docs/commands/acl/role', + destination: '/commands/acl/role', + permanent: true, + }, { source: '/docs/commands/acl/role/create', destination: '/commands/acl/role/create', From 8c58495318c511506c667102baf877d3e271eeca Mon Sep 17 00:00:00 2001 From: bear359 <92474478+bear359@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:12:59 -0600 Subject: [PATCH 09/20] Add files via upload (#11303) --- .../public/img/consul_ecosystem_diagram2.png | Bin 0 -> 68144 bytes .../img/consul_integration_program_steps.png | Bin 0 -> 16846 bytes website/public/img/hcp_consul_partner_badge.png | Bin 0 -> 15510 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/public/img/consul_ecosystem_diagram2.png create mode 100644 website/public/img/consul_integration_program_steps.png create mode 100644 website/public/img/hcp_consul_partner_badge.png diff --git a/website/public/img/consul_ecosystem_diagram2.png b/website/public/img/consul_ecosystem_diagram2.png new file mode 100644 index 0000000000000000000000000000000000000000..f4cb4ad74262ca56d752ebd2bbb78fa890c8963b GIT binary patch literal 68144 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
  • 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Ν% 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�#ieR`g(_j20xPN9jQMKG~5^)QbFE4ZSz+kGe9&SCoRb zal~I57PSAJ0|kqJ>O7Wc(Q5Ss5FWz*ML_>I`wa%Txpt=&7~ub@hgm3e;~ERIzY6gc zJ}oZ|_yVTQ|3z1;Mv-DZoINZJDZ{~^pV0*m_+Rw80-$3#K|}p}|4lT6Ks?l5zI-{> z*Fh2j!LR>EBmh(uh6h=~HdRTZH|I+NOe-p?s@~qkFVpXF>XcEK<#;QiEdUZZ-c@}D zC@Z{rmkboqGGR3B2U$8tdT>8q1)#oFakvFUx-2gGFEfDI&$-AZu#N+wYPrVi+f<0r z;Ag?vgTDblGvcuELqJL+tB?(FT%kYfJA*O8f;oI<-3d!}K!b68d9qwPal=48b=yGa zcd^QC1H~0<_eOS1FHtW~H|+DgC`iM_0Lr&>Eg*mxD1G^*{{Vh>J1~DeH?onKA55a} zS-ualizJMUj9iriGPWT^sCHy{_>VD&WifJCVpy4Cmk-og(&`DpZ4IsX{a2+DQ2+LL zedw+V=~jn()24N9D<(h{iO5HhbOC20c5}fhzuN$o-WaEL1OD^t^wI#MuwWzO zA{IpziDnu-**`L!s)<(}KR?v@>Wj9p!T1$`?4oy!r^~%lcv< z03z1ZV^}}03UvJOiDvM<5o|D6^r$ug*X4G_DTHOP-bQxzPae}J*%>5nqnxj=f!_1= z>#k)S)s=b7wA%)0c)1<}{^zZ!BW~?P#m&3hgFA@-b)p&t!Gh5}@by$dUA{urH0b{W DPHr`~ literal 0 HcmV?d00001 diff --git a/website/public/img/consul_integration_program_steps.png b/website/public/img/consul_integration_program_steps.png new file mode 100644 index 0000000000000000000000000000000000000000..7710c66234bf361a6ad720caffabf05dec8e188f GIT binary patch literal 16846 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 H67v56{cfx)FpWWa*BjL6GhxmR@>+C0>62 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 literal 0 HcmV?d00001 From e4a30035b63d67a84c0b3a22825e8ed235ab6ce8 Mon Sep 17 00:00:00 2001 From: bear359 <92474478+bear359@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:43:40 -0600 Subject: [PATCH 10/20] Update partnerships.mdx (#11304) * Update partnerships.mdx * Temporarily removing badge until we can resize it Co-authored-by: findkim <6362111+findkim@users.noreply.github.com> --- .../content/docs/integrate/partnerships.mdx | 149 +++++++++++------- 1 file changed, 88 insertions(+), 61 deletions(-) diff --git a/website/content/docs/integrate/partnerships.mdx b/website/content/docs/integrate/partnerships.mdx index 50e8413af2..78c8b11c81 100644 --- a/website/content/docs/integrate/partnerships.mdx +++ b/website/content/docs/integrate/partnerships.mdx @@ -6,26 +6,39 @@ description: Guide to partnership integrations for Consul. # Consul Integration Program -The HashiCorp Consul Integration Program enables vendors to build integrations with HashiCorp Consul that are tested and approved by HashiCorp. The program is intended to be largely self-service with links to resources, code samples, documentation, and clear integration steps. +The HashiCorp Consul Integration Program enables prospective partners to build integrations with HashiCorp Consul that are reviewed and verified by HashiCorp. Consul can be consumed in two ways: **self-managed**, or **HashiCorp Cloud Platform**, a hosted version of Consul operated by HashiCorp. HCP Consul is secure by default and offers an enterprise-level SLA to deploy an organization’s most important applications. -## Types of Consul Integrations +All integrations are available with Consul’s self-managed version. In some cases, these integrations can also be validated against HCP Consul. Upon completion of the validation with HCP Consul, a partner will receive a HCP Consul Verified badge which will be displayed on their partner page and utilized on the partner’s website as well. -By leveraging Consul’s RESTful HTTP API system, vendors are able to build extensible integrations at the data plane, platform, and the infrastructure layer to extend Consul’s functionalities. These integrations can be performed with the OSS (open source) version of Consul. Integrations with advanced network segmentation, advanced federation, and advanced read scalability need to be tested against Consul Enterprise, since these features are only supported by Consul Enterprise. +The program is intended to be largely self-service with links to resources, code samples, documentation, and clear integration steps. -[![Consul Architecture](/img/consul_ecosystem_diagram.png)](/img/consul_ecosystem_diagram.png) +## Categories of Consul Integrations -**Data Plane**: These integrations automate IP updates of load balancers by leveraging Consul service discovery, automate firewall security policy updates by leveraging Consul intentions within a centralized management tool, extend sidecar proxies to support Consul connect, and extend API gateways to allow Consul to route incoming traffic to the proxies for Connect-enabled services. +By leveraging Consul’s RESTful HTTP API system, prospective partners are able to build extensible integrations at the data plane, platform, and the infrastructure layer to extend Consul’s functionalities. These integrations can be performed both with the OSS (open source) version of Consul, Consul Enterprise, and HCP Consul. For features in Consul Enterprise and HCP Consul, refer to the links below: -**Control Plane**: Consul has a client-server architecture and is the control plane for the service mesh. No integrations at this layer. +[HCP Consul Features](https://cloud.hashicorp.com/docs/consul/features) +[Consul Enterprise Features](/docs/enterprise) -**Platform**: These integrations leverage automation of Consul agent deployment, configuration, and management through cloud and PaaS provisioning and orchestration tools such as Kubernetes and Pivotal Cloud Foundry (PCF). They include the Consul agent running in both client and server mode. +**The Consul Ecosystem Architecture** -**Infrastructure**: These integrations extend Consul’s certificate management, secure ACL configuration, observability metrics and logging, and service discovery that allows for dynamic service mapping with ITSM tools. +[![Consul Architecture](/img/consul_ecosystem_diagram2.png)](/img/consul_ecosystem_diagram2.png) + +**Data Plane**: These integrations extend Consul’s certificate management, secure ACL configuration, observability metrics and logging, and service discovery that allows for dynamic service mapping APM and logging tools, extend sidecar proxies to support Consul connect, and extend API gateways to allow Consul to route incoming traffic to the proxies for Connect-enabled services. + +**Control Plane**: Consul has a client-server architecture and is the control plane for the service mesh. + +**Platform**: These integrations leverage automation of Consul agent deployment, configuration, and management. Designed to be platform agnostic, Consul can be deployed in a variety of form factors, including major Public Cloud providers (AWS, GCP, Azure) as well as in bare-metal, virtual machine, and container (Docker, Kubernetes) environments. They include the Consul agent running in both client and server mode. + +**Infrastructure**: There are two integration options in this category: natively through a direct integration with Consul or via Consul-Terraform-Sync (CTS). By leveraging Consul’s powerful **Network Infrastructure Automation (NIA)*** capabilities through CTS, changes in an infrastructure are seamlessly automated when Consul detects a change in its service catalog. For example, these integrations could be used to automate IP updates of load balancers or firewall security policies by leveraging Consul service discovery. + +**Network Infrastructure Automation (NIA)***: These integrations leverage Consul’s service catalog to seamlessly integrate with Consul-Terraform-Sync (CTS) to automate changes in network infrastructure via a publisher-subscriber method. More details can be found [here](https://www.consul.io/docs/integrate/nia-integration). ## Development Process The Consul integration development process is described in the steps below. By following these steps, Consul integrations can be developed alongside HashiCorp to ensure new integrations are reviewed, approved and released as quickly as possible. +[![Integration Program Steps](/img/consul_integration_program_steps.png)](/img/consul_integration_program_steps) + 1. Engage: Initial contact between vendor and HashiCorp 2. Enable: Documentation, code samples and best practices for developing the integration 3. Develop and Test: Integration development and testing by vendor @@ -43,71 +56,85 @@ Here are links to resources, documentation, examples and best practices to guide #### Data Plane: +**Proxy** + +- [How to Integrate a Sidecar Proxy Documentation](/docs/connect/proxies/integrate) +- [Example of Envoy Integration](/docs/connect/proxies/envoy) + +**API Gateway** + +- [Ambassador Integration documentation](https://learn.hashicorp.com/tutorials/consul/service-mesh-gateway-ambassador) +- [F5 Terminating Gateway Integration Documentation](https://www.hashicorp.com/integrations/f5-networks/consul) +- [Traefik Integration with Consul Service Mesh](https://traefik.io/blog/integrating-consul-connect-service-mesh-with-traefik-2-5/) +- [Kong's Ingress Controller Integration with Consul](https://www.hashicorp.com/integrations/kong/consul) + +**Application Performance Monitoring (APM)** + +- [Consul Telemetry Documentation](/docs/agent/telemetry) +- [Monitoring Consul with Datadog APM](https://www.datadoghq.com/blog/consul-datadog/) +- [Monitoring Consul with Dynatrace APM](https://www.dynatrace.com/news/blog/automatic-intelligent-observability-into-your-hashicorp-consul-service-mesh/) + +**Logging** + +- [Monitor Consul with Logz.io](https://www.hashicorp.com/integrations/logz-io/consul) +- [Monitor Consul with Splunk SignalFx](https://www.hashicorp.com/integrations/splunksignalfx/consul) +- [Consul Datacenter Monitoring with New Relic](https://www.hashicorp.com/integrations/new-relic/consul) + +#### Platform: + +- [Consul-AWS for AWS Cloud Map](https://learn.hashicorp.com/tutorials/consul/sync-aws-services) +- [Consul Integration with AWS ECS](/docs/ecs/get-started/install) +- [Consul Integration with Layer5 Meshery](https://www.hashicorp.com/integrations/layer5-io/consul) +- [Consul Integration with VMware Tanzu Application Service](https://learn.hashicorp.com/tutorials/consul/sync-pivotal-cloud-services) + +#### Infrastructure: + +-> **Note**: The types of integration areas below could be developed to natively work with Consul or through leveraging Consul-Terraform-Sync and Consul’s network automation capabilities. + +**Firewalls** + + **Network Infrastructure Automation (using CTS):** + + - [Automated Firewalling with Check Point](https://www.hashicorp.com/integrations/checkpoint-software/consul) + - [Automated Firewalling with Palo Alto Networks](https://www.hashicorp.com/integrations/pan/consul) + +**Software-Defined Networking \(SDN\)** + +- [Automating Cisco ACI with Consul](https://www.hashicorp.com/integrations/cisco/consul) + **Load Balancer** - [Load Balancing with NGINX and Consul Template](https://learn.hashicorp.com/tutorials/consul/load-balancing-nginx) - [Load Balancing with HAProxy Service Discovery](https://learn.hashicorp.com/tutorials/consul/load-balancing-haproxy) -**Proxy** + **Network Infrastructure Automation \(using CTS\):** + + - [Automate F5 BIG-IP with Consul NIA](https://learn.hashicorp.com/tutorials/consul/consul-terraform-sync-f5-bigip-fast?in=consul/network-infrastructure-automation) + - [Automate VMware Advanced Load Balancers (Avi) with Consul NIA](https://www.hashicorp.com/integrations/_vmware/consul) -- [How to Integrate a Sidecar Proxy Documentation](/docs/connect/proxies/integrate) -- [Example of Envoy Integration](/docs/connect/proxies/envoy) -- [Consul’s source code](https://github.com/hashicorp/consul) +**Application Delivery Controllers \(ADC\):** -**Firewall** - -- [Consul Connect Intentions](/docs/connect/intentions) -- [Consul Connect Intentions Command Line](/commands/intention) -- [Consul Connect Intentions API](/api/connect/intentions) - -**API Gateway** - -- [Ambassador Integration documentation](/docs/platform/k8s/ambassador) - -#### Platform: - -- [Consul-AWS for AWS Cloud Map](https://learn.hashicorp.com/tutorials/consul/sync-aws-services) -- [Consul Agent Cloud Auto-joining](/docs/agent/cloud-auto-join) - -#### Infrastructure: - -**Certificate Authority (CA)** - -- [Consul Certificate Management Documentation](/docs/connect/ca) -- [Securing RPC Communication with TLS Encryption](https://learn.hashicorp.com/tutorials/consul/tls-encryption-secure) -- [Consul Connect CA API](/api/connect/ca) - -**Identity and Access Management (IAM)** - -- [ACL Documentation and Guides](/docs/acl) -- [ACL API Documentation](/api/acl/acl) -- [Securing Consul with ACLs](https://learn.hashicorp.com/tutorials/consul/access-control-setup-production) - -**Application Performance Monitoring (APM)** - -- [Consul Telemetry Documentation](/docs/agent/telemetry) -- [Consul Cluster Monitoring and Metrics](https://learn.hashicorp.com/tutorials/consul/monitor-datacenter-health) -- [Monitoring Consul with Telegraf](https://learn.hashicorp.com/tutorials/consul/monitor-health-telegraf) - -**Logging** - -- [Consul Monitor Command Line](/commands/monitor) -- [Enable syslog via CLI](/docs/agent/options#enable_syslog) -- [Enable syslog via config file](/docs/agent/options#_syslog) - -**Information Technology Service Management (ITSM)** - -- [Consul Service Registry](https://learn.hashicorp.com/tutorials/consul/get-started-service-discovery) -- [DNS Query Interface](https://learn.hashicorp.com/tutorials/consul/get-started-service-discovery#querying-services) -- [HTTP API with Edge Triggers](https://learn.hashicorp.com/tutorials/consul/get-started-service-discovery#http-api) +- [Automate A10 ADC with Consul NIA](https://learn.hashicorp.com/tutorials/consul/consul-terraform-sync-a10-adc?in=consul/network-infrastructure-automation) +- [Automate Citrix ADC with Consul NIA](https://www.hashicorp.com/integrations/citrix-adc/consul) ### 3. Develop and Test The only knowledge necessary to write a plugin is basic command-line skills and knowledge of the [Go programming language](http://www.golang.org). Use the plugin interface to develop your integration. All integrations should contain unit and acceptance testing. +**HCP Consul**: The process to configure a testing instance of HCP consul [is very simple](https://learn.hashicorp.com/tutorials/cloud/get-started-consul). HCP has been designed as a HashiCorp managed service so configuration is minimal as only Consul client agents need to be installed. Furthermore, HashiCorp provides all new users an initial credit which should last approximately 2 months using a [development cluster](https://cloud.hashicorp.com/pricing/consul). When deployed with AWS free tier services, there should be no cost beyond the time spent by the designated tester. + +Please note that HCP Consul is currently only deployed on AWS so the partner’s application should be able to be deployed or run in AWS. For more information, please refer to [Peering an HVN to an AWS VPC for HCP Consul](https://www.youtube.com/watch?v=vuKjkIGYZlU). + +#### HCP Consul Resource Links: + +- [Getting Started with HCP Consul](https://learn.hashicorp.com/tutorials/cloud/get-started-consul?in=consul/cloud-get-started) +- [Peering an HVN to a VPC for HCP Consul](https://www.youtube.com/watch?v=vuKjkIGYZlU) +- [Connecting a Consul Client to HCP Consul](https://learn.hashicorp.com/tutorials/cloud/consul-client-virtual-machines?in=consul/cloud-get-started) +- [Monitoring HCP Consul with Datadog](https://docs.datadoghq.com/integrations/guide/hcp-consul/) + ### 4. Review and Approval -HashiCorp will review and approve your Consul integration. Please send an email to [consul-integration-dev@hashicorp.com](mailto:consul-integration-dev@hashicorp.com) with any relevant documentation, demos or other resources and let us know your integration is ready for review. +HashiCorp will review and approve your Consul integration. Please send an email to [technologypartners@hashicorp.com](mailto:technologypartners@hashicorp.com) with any relevant documentation, demos or other resources and let us know your integration is ready for review. ### 5. Release @@ -125,9 +152,9 @@ Below is a checklist of steps that should be followed during the Consul integrat - Complete the [Consul Integration Program webform](https://docs.google.com/forms/d/e/1FAIpQLSf-RyVR9F0lmosao8Nnur0TTDjnl99gttnK3QP1OkfRefVKSw/viewform) - Develop and test your Consul integration following examples, documentation and best practices -- When the integration is completed and ready for HashiCorp review, send us the documentation, demos and any other resources for review at: [consul-integration-dev@hashicorp.com](mailto:consul-integration-dev@hashicorp.com) +- When the integration is completed and ready for HashiCorp review, send us the documentation, demos and any other resources for review at: [technologypartners@hashicorp.com](mailto:technologypartners@hashicorp.com) - Plan to continue to support the integration with additional functionality and responding to customer issues. ## Contact Us -For any questions or feedback, please contact us at: [consul-integration-dev@hashicorp.com](mailto:consul-integration-dev@hashicorp.com) +For any questions or feedback, please contact us at: [technologypartners@hashicorp.com](mailto:technologypartners@hashicorp.com) From 38a3f65b080c65be119462d278e4a0df8696de7b Mon Sep 17 00:00:00 2001 From: Noel Quiles <3746694+EnMod@users.noreply.github.com> Date: Thu, 14 Oct 2021 00:33:38 -0400 Subject: [PATCH 11/20] website: Add Fathom analytics (#11243) * Impl Fathom analytics * Use analytics package instead of direct impl * Remove explicit fathom-client dep --- website/package-lock.json | 30 ++++++++++++++++++++++++++++++ website/package.json | 1 + website/pages/_app.js | 3 +++ 3 files changed, 34 insertions(+) diff --git a/website/package-lock.json b/website/package-lock.json index c706710ba1..12262d983d 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@hashicorp/mktg-global-styles": "^4.0.0", "@hashicorp/mktg-logos": "^1.2.0", + "@hashicorp/platform-analytics": "^0.1.0", "@hashicorp/platform-code-highlighting": "^0.1.2", "@hashicorp/platform-runtime-error-monitoring": "^0.1.0", "@hashicorp/platform-util": "^0.1.0", @@ -768,6 +769,17 @@ "node": ">=4" } }, + "node_modules/@hashicorp/platform-analytics": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@hashicorp/platform-analytics/-/platform-analytics-0.1.0.tgz", + "integrity": "sha512-kR/E0KWemjazSrSFN9l22v8JqlQvkgjgkZHPJEhGiBt05LMtt2ugxIakv+gnzW5lU9434nnr7oDooUyj4eufPA==", + "dependencies": { + "fathom-client": "^3.2.0" + }, + "peerDependencies": { + "react": ">= 16.x" + } + }, "node_modules/@hashicorp/platform-cli": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@hashicorp/platform-cli/-/platform-cli-1.2.0.tgz", @@ -8028,6 +8040,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fathom-client": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fathom-client/-/fathom-client-3.2.0.tgz", + "integrity": "sha512-WF/qA5wXYSuA5K8uiIhGNbErOcTAmfLEWrBxWP2px2dEc9waH9zxjdh9k0F907VCBhdJrv+f7V3HT/Pmro40zA==" + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -20556,6 +20573,14 @@ } } }, + "@hashicorp/platform-analytics": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@hashicorp/platform-analytics/-/platform-analytics-0.1.0.tgz", + "integrity": "sha512-kR/E0KWemjazSrSFN9l22v8JqlQvkgjgkZHPJEhGiBt05LMtt2ugxIakv+gnzW5lU9434nnr7oDooUyj4eufPA==", + "requires": { + "fathom-client": "^3.2.0" + } + }, "@hashicorp/platform-cli": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@hashicorp/platform-cli/-/platform-cli-1.2.0.tgz", @@ -26332,6 +26357,11 @@ "reusify": "^1.0.4" } }, + "fathom-client": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fathom-client/-/fathom-client-3.2.0.tgz", + "integrity": "sha512-WF/qA5wXYSuA5K8uiIhGNbErOcTAmfLEWrBxWP2px2dEc9waH9zxjdh9k0F907VCBhdJrv+f7V3HT/Pmro40zA==" + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", diff --git a/website/package.json b/website/package.json index 4fa1f98c9e..5d3090cb04 100644 --- a/website/package.json +++ b/website/package.json @@ -6,6 +6,7 @@ "dependencies": { "@hashicorp/mktg-global-styles": "^4.0.0", "@hashicorp/mktg-logos": "^1.2.0", + "@hashicorp/platform-analytics": "^0.1.0", "@hashicorp/platform-code-highlighting": "^0.1.2", "@hashicorp/platform-runtime-error-monitoring": "^0.1.0", "@hashicorp/platform-util": "^0.1.0", diff --git a/website/pages/_app.js b/website/pages/_app.js index 15d518b412..92f2cacf6e 100644 --- a/website/pages/_app.js +++ b/website/pages/_app.js @@ -1,6 +1,7 @@ import './style.css' import '@hashicorp/platform-util/nprogress/style.css' +import useFathomAnalytics from '@hashicorp/platform-analytics' import Router from 'next/router' import Head from 'next/head' import NProgress from '@hashicorp/platform-util/nprogress' @@ -21,7 +22,9 @@ const { ConsentManager, openConsentManager } = createConsentManager({ }) export default function App({ Component, pageProps }) { + useFathomAnalytics() useAnchorLinkAnalytics() + return ( Date: Thu, 14 Oct 2021 13:54:27 +0100 Subject: [PATCH 12/20] ui: Move the Role remove dialog to use InformedAction (#11298) --- .changelog/11298.txt | 3 ++ .../app/components/role-selector/index.hbs | 41 ++++++++++++------- .../components/role-selector/pageobject.js | 8 ++-- 3 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 .changelog/11298.txt diff --git a/.changelog/11298.txt b/.changelog/11298.txt new file mode 100644 index 0000000000..a71a3d4d8d --- /dev/null +++ b/.changelog/11298.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixed styling of Role remove dialog on the Token edit page +``` diff --git a/ui/packages/consul-ui/app/components/role-selector/index.hbs b/ui/packages/consul-ui/app/components/role-selector/index.hbs index cd607f1196..1054af5525 100644 --- a/ui/packages/consul-ui/app/components/role-selector/index.hbs +++ b/ui/packages/consul-ui/app/components/role-selector/index.hbs @@ -141,24 +141,35 @@ as |modal|>
  • -
    -
    -
    - Confirm Remove -
    + + <:header> + Confirm Remove + + <:body>

    Are you sure you want to remove this role?

    -
    -
      -
    • - -
    • -
    • - -
    • -
    -
    + + <:actions as |Actions|> + + + Remove + + + + + Cancel + + + +
  • {{/if}} diff --git a/ui/packages/consul-ui/app/components/role-selector/pageobject.js b/ui/packages/consul-ui/app/components/role-selector/pageobject.js index f095df9353..c8d79bf423 100644 --- a/ui/packages/consul-ui/app/components/role-selector/pageobject.js +++ b/ui/packages/consul-ui/app/components/role-selector/pageobject.js @@ -6,9 +6,11 @@ export default (clickable, deletable, collection, alias, roleForm) => (scope = ' roles: alias('selectedOptions'), selectedOptions: collection( '[data-test-roles] [data-test-tabular-row]', - deletable({ - actions: clickable('label'), - }) + { + actions: clickable('label > button'), + delete: clickable('[data-test-delete]'), + confirmDelete: clickable('.informed-action button'), + } ), }; }; From a1a0ce1dad29cb0920df0ea1265ab9ee825ba844 Mon Sep 17 00:00:00 2001 From: John Cowen Date: Thu, 14 Oct 2021 15:14:26 +0100 Subject: [PATCH 13/20] ci: Only install UI dependencies for CI frontend-cache (#11313) --- .circleci/config.yml | 2 +- ui/GNUmakefile | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 199ce432cf..02f3ad639a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -602,7 +602,7 @@ jobs: - run: name: install yarn packages - command: cd ui && make + command: cd ui && make deps - save_cache: key: *YARN_CACHE_KEY diff --git a/ui/GNUmakefile b/ui/GNUmakefile index 91158150f2..290bda9aed 100644 --- a/ui/GNUmakefile +++ b/ui/GNUmakefile @@ -15,11 +15,17 @@ dist-docker: dist clean: rm -rf ./dist -# Build a distribution of the UI using the minimal amount of dependencies +# Build a distribution of the UI dist: clean cd packages/consul-ui && \ $(MAKE) +# Install deps for the UI only +deps: clean + cd packages/consul-ui && \ + $(MAKE) deps + + # Build a distribution of the UI for Vercel previews. # The distribution must be copied into the ui/ subfolder # in order to mirror the go binary From 53ea1f634ac8c6e8e4c3cf36e96c5f1d04a44ee6 Mon Sep 17 00:00:00 2001 From: freddygv Date: Thu, 14 Oct 2021 08:32:45 -0600 Subject: [PATCH 14/20] Ensure partition is handled by auto-encrypt --- agent/auto-config/tls.go | 2 +- agent/connect/uri_agent_oss.go | 12 +++++++++++- agent/consul/connect_ca_endpoint.go | 4 ++-- agent/consul/leader_connect_ca.go | 3 +-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/agent/auto-config/tls.go b/agent/auto-config/tls.go index c152203082..ab647b515b 100644 --- a/agent/auto-config/tls.go +++ b/agent/auto-config/tls.go @@ -216,7 +216,7 @@ func (ac *AutoConfig) generateCSR() (csr string, key string, err error) { Host: unknownTrustDomain, Datacenter: ac.config.Datacenter, Agent: ac.config.NodeName, - // TODO(rb)(partitions): populate the partition field from the agent config + Partition: ac.config.PartitionOrDefault(), } caConfig, err := ac.config.ConnectCAConfiguration() diff --git a/agent/connect/uri_agent_oss.go b/agent/connect/uri_agent_oss.go index bf13697ee3..0936d680a2 100644 --- a/agent/connect/uri_agent_oss.go +++ b/agent/connect/uri_agent_oss.go @@ -2,7 +2,17 @@ package connect -import "fmt" +import ( + "fmt" + + "github.com/hashicorp/consul/agent/structs" +) + +// GetEnterpriseMeta will synthesize an EnterpriseMeta struct from the SpiffeIDAgent. +// in OSS this just returns an empty (but never nil) struct pointer +func (id SpiffeIDAgent) GetEnterpriseMeta() *structs.EnterpriseMeta { + return &structs.EnterpriseMeta{} +} func (id SpiffeIDAgent) uriPath() string { return fmt.Sprintf("/agent/client/dc/%s/id/%s", id.Datacenter, id.Agent) diff --git a/agent/consul/connect_ca_endpoint.go b/agent/consul/connect_ca_endpoint.go index c1f6a19be9..3df8068809 100644 --- a/agent/consul/connect_ca_endpoint.go +++ b/agent/consul/connect_ca_endpoint.go @@ -186,8 +186,8 @@ func (s *ConnectCA) Sign( "we are %s", serviceID.Datacenter, s.srv.config.Datacenter) } } else if isAgent { - // TODO(partitions): support auto-config in different partitions - structs.DefaultEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext) + entMeta := structs.DefaultEnterpriseMetaInPartition(agentID.PartitionOrDefault()) + entMeta.FillAuthzContext(&authzContext) if authz.NodeWrite(agentID.Agent, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } diff --git a/agent/consul/leader_connect_ca.go b/agent/consul/leader_connect_ca.go index ed4f30c6cc..78753b1541 100644 --- a/agent/consul/leader_connect_ca.go +++ b/agent/consul/leader_connect_ca.go @@ -1438,8 +1438,7 @@ func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID conne csr.URIs = uris } - // TODO(partitions): support auto-config in different partitions - entMeta.Merge(structs.DefaultEnterpriseMetaInDefaultPartition()) + entMeta.Merge(agentID.GetEnterpriseMeta()) } commonCfg, err := config.GetCommonConfig() From e22f0cc033e85f92d1eccc0d89a9decf4bf9fbc4 Mon Sep 17 00:00:00 2001 From: freddygv Date: Thu, 14 Oct 2021 08:57:40 -0600 Subject: [PATCH 15/20] Use stored entmeta to fill authzContext --- agent/consul/connect_ca_endpoint.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agent/consul/connect_ca_endpoint.go b/agent/consul/connect_ca_endpoint.go index 3df8068809..a08cf27cc5 100644 --- a/agent/consul/connect_ca_endpoint.go +++ b/agent/consul/connect_ca_endpoint.go @@ -186,8 +186,7 @@ func (s *ConnectCA) Sign( "we are %s", serviceID.Datacenter, s.srv.config.Datacenter) } } else if isAgent { - entMeta := structs.DefaultEnterpriseMetaInPartition(agentID.PartitionOrDefault()) - entMeta.FillAuthzContext(&authzContext) + agentID.GetEnterpriseMeta().FillAuthzContext(&authzContext) if authz.NodeWrite(agentID.Agent, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } From 0f6373ec87ce679c75aa89643e6c1f57fc53677e Mon Sep 17 00:00:00 2001 From: Kim Ngo <6362111+findkim@users.noreply.github.com> Date: Thu, 14 Oct 2021 12:48:34 -0500 Subject: [PATCH 16/20] Minor followup to nia 0.4.0 docs (#11306) * Minor updates to source-input docs Co-authored-by: lornasong --- website/content/docs/nia/configuration.mdx | 38 ++++++++++--------- website/content/docs/nia/tasks.mdx | 8 ++-- .../content/docs/nia/terraform-modules.mdx | 36 ++++++++++++++++-- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/website/content/docs/nia/configuration.mdx b/website/content/docs/nia/configuration.mdx index 9110f74551..aef59cb387 100644 --- a/website/content/docs/nia/configuration.mdx +++ b/website/content/docs/nia/configuration.mdx @@ -253,7 +253,7 @@ task { - `recurse` - (bool: false) Setting to `true` instructs Consul-Terraform-Sync to treat the path as a prefix instead of a literal match. - `datacenter` - (string) The datacenter of the services to query for the task. If not provided, the datacenter will default to the datacenter of the agent that Consul-Terraform-Sync queries. - `namespace` - (string) The namespace of the services to query for the task. If not provided, the namespace will be inferred from the Consul-Terraform-Sync ACL token or default to the `default` namespace. -- `source_includes_var` - (bool: false) If set to `true`, then Consul-Terraform-Sync will include the [`consul_kv` variable](/docs/nia/terraform-modules#consul-kv-variable) as a source input. Refer to the documentation of the selected module for guidance on how to configure this field. If configured inconsistently with the module, Consul-Terraform-Sync will error and exit. +- `source_includes_var` - (bool: false) If set to `true`, then Consul-Terraform-Sync will include the [`consul_kv` variable](/docs/nia/terraform-modules#consul-kv-variable) as an input to the module specified in `task.source`. Refer to the documentation of the selected module for guidance on how to configure this field. If configured inconsistently with the module, Consul-Terraform-Sync will error and exit. #### Schedule Condition @@ -289,9 +289,9 @@ You can add an optional `source_input` block to the `task` block. The `source_in This `services` source input object defines services registered to Consul whose metadata will be used as [services source input to the Terraform Module](/docs/nia/terraform-modules/#services-source-input). The following parameters are supported: -| Parameter | Description | Default | Required | -| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | ------------------------------------------------------------- | -| `regexp` | String value matching the names of Consul services to monitor. Only services that have a name matching the regular expression are used by the task.

    If `regexp` is configured, then [`task.services`](#services) must be omitted or empty.

    If both a list and a regex are needed, consider including the list as part of the regex or creating separate tasks. | empty string `""` | Optional unless the `task.services` option is not configured. | +| Parameter | Required |Description | Default | +| --------- | --------- | ---------- | ------- | +| `regexp` | Optional | String value matching the names of Consul services to monitor. Only services that have a name matching the regular expression are used by the task.

    If `regexp` is configured, then [`task.services`](#services) must be omitted or empty.

    If both a list and a regex are needed, consider including the list as part of the regex or creating separate tasks. | none | In the following example, the scheduled task queries all Consul services with `web` as the suffix. The metadata of matching services are provided to the Terraform module. @@ -300,12 +300,14 @@ task { name = "schedule_condition_task" description = "execute every Monday using information from service names starting with web" source = "path/to/module" - source_input “services” { - regexp = "^web.*" - } + condition "schedule" { cron = "* * * * Mon" } + + source_input “services” { + regexp = "^web.*" + } } ``` @@ -313,29 +315,31 @@ task { A Consul KV source input block defines changes to Consul KV that will be monitored. These changes will then be provided as [Consul KV source input to the Terraform Module](/docs/nia/terraform-modules/#consul-kv-source-input). The source input can be configured for a single Consul KV entry or for any Consul KV entries that are prefixed with a given path. The following parameters are supported: -| Parameter | Description | Default | Required | -| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------- | -| `path` | String value that specifies the path of the key used by the task. The path can point to a single Consul KV entry or several entries within the path. | none | Required | -| `recurse` | Boolean value that enables Consul-Terraform-Sync to treat the path as a prefix. If set to `false`, the path will be treated as a literal match. | `false` | Optional | -| `datacenter` | String value specifying the name of a datacenter to query for the task. | Datacenter of the agent that Consul-Terraform-Sync queries. | Optional | -| `namespace` |
    String value indicating the namespace of the services to query for the task. | In order of precedence:
    1. Inferred from the Consul-Terraform-Sync ACL token
    2. The `default` namespace. | Optional | +| Parameter | Required | Description | Default | +| --------- | -------- | ----------- | ------- | +| `path` | Required | String value that specifies the path of the key used by the task. The path can point to a single Consul KV entry or several entries within the path. | none | +| `recurse` | Optional | Boolean value that enables Consul-Terraform-Sync to treat the path as a prefix. If set to `false`, the path will be treated as a literal match. | `false` | +| `datacenter` | Optional | String value specifying the name of a datacenter to query for the task. | Datacenter of the agent that Consul-Terraform-Sync queries. | +| `namespace` | Optional |
    String value indicating the namespace of the services to query for the task. | In order of precedence:
    1. Inferred from the Consul-Terraform-Sync ACL token
    2. The `default` namespace. | In the following example, the scheduled task queries datacenter `dc1` in the `default` namespace for changes to the value held by the key `my-key`. ```hcl task { - name = "schedule_condition_task" + name = "schedule_condition_task_kv" description = "execute every Monday using information from Consul KV entry my-key" source = "path/to/module" + + condition "schedule" { + cron = "* * * * Mon" + } + source_input "consul-kv" { path = "my-key" recurse = false datacenter = "dc1" namespace = "default" } - condition "schedule" { - cron = "* * * * Mon" - } } ``` diff --git a/website/content/docs/nia/tasks.mdx b/website/content/docs/nia/tasks.mdx index e3b050e89a..f868062542 100644 --- a/website/content/docs/nia/tasks.mdx +++ b/website/content/docs/nia/tasks.mdx @@ -83,8 +83,7 @@ task { source = "path/to/services-condition-module" services = ["api", "db", "web"] - condition "services" { - } + condition "services" {} } ``` @@ -167,7 +166,7 @@ If the task condition's [`source_includes_var`](/docs/nia/configuration#source_i All scheduled tasks must be configured with a schedule condition. The schedule condition sets the cadence to trigger a task with a [`cron`](/docs/nia/configuration#cron) configuration. The schedule condition block does not support parameters to configure source input. As a result, inputs must be configured separately. You can configure [`task.services`](/docs/nia/configuration#services) or a [`source_input` block](/docs/nia/configuration#source_input) to set the source input. -Below is an example configuration for a task that will execute every Monday, which is set by the schedule condition’s [`cron`](/docs/nia/configuration#cron) configuration. The source input is defined by the `task.services` configuration. When the task is triggered on Monday, it will retrieve the latest information on 'web' and 'db' from Consul and provide this to the module’s input variables. +Below is an example configuration for a task that will execute every Monday, which is set by the schedule condition’s [`cron`](/docs/nia/configuration#cron) configuration. The source input is defined by the `task.services` configuration. When the task is triggered on Monday, it will retrieve the latest information on "web" and "db" from Consul and provide this to the module’s input variables. ```hcl task { @@ -175,6 +174,7 @@ task { description = "execute every Monday using service information from web and db" services = ["web", "db"] source = "path/to/module" + condition "schedule" { cron = "* * * * Mon" } @@ -192,7 +192,7 @@ Scheduled tasks generally run on schedule, but they can be triggered on demand w - [Long-running mode](/docs/nia/cli#long-running-mode): At the beginning of the long-running mode, Consul-Terraform-Sync first passes through a once-mode phase in which all tasks are executed once. Scheduled tasks will trigger once during this once-mode phase. This behavior also applies to tasks that are not scheduled. After once-mode has completed, scheduled tasks subsequently trigger on schedule. -- [Inspect mode](/docs/nia/cli#inspect-mode): When running in inspect mode, the terminal will output a plan of proposed updates that would be made if the tasks were to trigger at that moment and then exit. The outputted plan for a scheduled task is also the proposed updates that would be made if the task was triggered at that moment, even if off-schedule. +- [Inspect mode](/docs/nia/cli#inspect-mode): When running in inspect mode, the terminal will output a plan of proposed updates that would be made if the tasks were to trigger at that moment and then exit. No changes are applied in this mode. The outputted plan for a scheduled task is also the proposed updates that would be made if the task was triggered at that moment, even if off-schedule. - [Once mode](/docs/nia/cli#once-mode): During the once mode, all tasks are only triggered one time. Scheduled tasks will execute during once mode even if not on the schedule. diff --git a/website/content/docs/nia/terraform-modules.mdx b/website/content/docs/nia/terraform-modules.mdx index a1c08d2a5b..e56def12af 100644 --- a/website/content/docs/nia/terraform-modules.mdx +++ b/website/content/docs/nia/terraform-modules.mdx @@ -31,7 +31,11 @@ See below sections for more information on [defining source input](#source-input ### Source Input -A source input allows for a task to satisfy the input requirements defined by the Terraform Module’s [input variables](https://www.terraform.io/docs/language/values/variables.html), and is configured alongside a task’s condition. Both the source input and condition define objects to be monitored, but for differing reasons. The condition defines monitored objects with criteria. When this criteria is satisfied, Consul-Terraform-Sync will then trigger a task. The source input however, defines monitored objects with the intent of providing values or metadata about these objects to the Terraform Module. The source input and condition objects can be the same, such as when `task.services` is provided without a source input block and condition block, but they can also be defined using separate blocks. In this way the source input does not need to be tied to the provided condition in order to satisfy the Terraform Module. +A source input allows for a task to satisfy the input requirements defined by the Terraform Module’s [input variables](https://www.terraform.io/docs/language/values/variables.html), and is configured alongside a task’s condition. Both the source input and condition define objects to be monitored, but for differing reasons. + +The condition defines monitored objects with criteria. When this criteria is satisfied, Consul-Terraform-Sync will then trigger a task. + +The source input however, defines monitored objects with the intent of providing values or metadata about these objects to the Terraform Module. The source input and condition objects can be the same, such as when `task.services` is provided without a source input block and condition block, but they can also be defined using separate blocks. In this way the source input does not need to be tied to the provided condition in order to satisfy the Terraform Module. There are a few ways that a source input can be defined: @@ -52,6 +56,8 @@ Tasks configured with a services source input monitor for changes to services. M Sample rendered services input: + + ```hcl services = { "web.test-server.dc1" = { @@ -81,6 +87,8 @@ services = { } ``` + + In order to configure a task with the services source input, the list of services that will be used for the input must be configured in one of the following ways: - the task's [`services`](/docs/nia/configuration#services) @@ -129,12 +137,16 @@ Tasks configured with a Consul KV source input monitor Consul KV for changes to Sample rendered consul KV input: + + ```hcl consul_kv = { "my-key" = "some value" } ``` + + To configure a task with the Consul KV source input, the KVs which will be used for the input must be configured in one of the following ways: - a [`condition "consul-kv"` block](/docs/nia/configuration#consul-kv-condition) configured with the `source_includes_var` set to true. @@ -167,21 +179,25 @@ Tasks configured with a Catalog Services source input monitors for service and t Sample rendered catalog-services input: + + ```hcl catalog_services = { - "api" = ["prod", "staging"] + "api" = ["prod", "staging"] "consul" = [] - "web" = ["blue", "green"] + "web" = ["blue", "green"] } ``` + + To configure a task with the Catalog Services source input, the catalog services which will be used for the input must be configured in one of the following ways: - a [`condition "catalog-services"` block](/docs/nia/configuration#consul-kv-condition) configured with `source_includes_var` set to true. -> **Note:** Currently there is no support for a `source_input “catalog-services”` block. -Example of a catalog-services condition which supports source input through source_includes_var: +Example of a catalog-services condition which supports source input through `source_includes_var`: ```hcl task { @@ -268,6 +284,8 @@ Keys of the `services` map are unique identifiers of the service across Consul a Terraform variables when passed as module arguments can be [lossy for object types](https://www.terraform.io/docs/configuration/types.html#conversion-of-complex-types). This allows Consul-Terraform-Sync to declare the full variable with every object attribute in the generated root module, and pass the variable to a child module that contains a subset of these attributes for its variable declaration. Modules compatible with Consul-Terraform-Sync may simplify the `var.services` declaration within the module by omitting unused attributes. For example, the following services variable has 4 attributes with the rest omitted. + + ```hcl variable "services" { description = "Consul services monitored by Consul-Terraform-Sync" @@ -283,10 +301,14 @@ variable "services" { } ``` + + ### Catalog Services Variable If you are creating a module for a [catalog-services condition](/docs/nia/tasks#catalog-services-condition), then you have the option to add the `catalog_services` variable, which contains service registration and tag information. If your module would benefit from consuming this information, you can copy the `catalog_services` variable declaration to your `variables.tf` file in addition to the other variables. + + ```hcl variable "catalog_services" { description = "Consul catalog service names and tags monitored by Consul-Terraform-Sync" @@ -294,6 +316,8 @@ variable "catalog_services" { } ``` + + The keys of the `catalog_services` map are the names of the services that are registered with Consul at the given datacenter. The value for each service name is a list of all known tags for that service. We recommend that if you make a module with with a catalog-services condition, that you document this in the README. This way, users that want to configure a task with your module will know to configure a catalog-services [condition](/docs/nia/configuration#condition) block. @@ -304,6 +328,8 @@ Similarly, if you include the `catalog_services` variable in your module, we rec If you are creating a module for a [consul-kv condition](/docs/nia/tasks#consul-kv-condition), then you have the option to add the `consul_kv` variable, which contains a map of the keys and values for the Consul KV pairs. If your module would benefit from consuming this information, you can copy the `consul_kv` variable declaration to your `variables.tf` file in addition to the other variables. + + ```hcl variable "consul_kv" { description = "Keys and values of the Consul KV pairs monitored by Consul-Terraform-Sync" @@ -311,6 +337,8 @@ variable "consul_kv" { } ``` + + If your module contains the `consul_kv` variable, we recommend documenting the usage in the README file so that users know to set the [`source_includes_var`](/docs/nia/configuration#source_includes_var-1) configuration to `true` in the `consul-kv` condition. Setting the field to `true` instructs Consul-Terraform-Sync to declare the `consul_kv` variable in the generated root module and pass the variable to a child module. Therefore, if this field is configured inconsistently, Consul-Terraform-Sync will error and exit. ### Module Input Variables From cc2abb79baa5989821ede371a19464fa691ec000 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Thu, 14 Oct 2021 15:38:05 -0500 Subject: [PATCH 17/20] acl: small OSS refactors to help ensure that auth methods with namespace rules work with partitions (#11323) --- .changelog/11323.txt | 3 +++ agent/consul/acl_endpoint_oss.go | 2 +- agent/structs/acl_oss.go | 4 ---- 3 files changed, 4 insertions(+), 5 deletions(-) create mode 100644 .changelog/11323.txt diff --git a/.changelog/11323.txt b/.changelog/11323.txt new file mode 100644 index 0000000000..c6e7c34ded --- /dev/null +++ b/.changelog/11323.txt @@ -0,0 +1,3 @@ +```release-note:bug +acl: **(Enterprise only)** ensure that auth methods with namespace rules work with partitions +``` diff --git a/agent/consul/acl_endpoint_oss.go b/agent/consul/acl_endpoint_oss.go index a8defcf133..61788f1836 100644 --- a/agent/consul/acl_endpoint_oss.go +++ b/agent/consul/acl_endpoint_oss.go @@ -19,5 +19,5 @@ func computeTargetEnterpriseMeta( method *structs.ACLAuthMethod, verifiedIdentity *authmethod.Identity, ) (*structs.EnterpriseMeta, error) { - return method.TargetEnterpriseMeta(verifiedIdentity.EnterpriseMeta), nil + return &structs.EnterpriseMeta{}, nil } diff --git a/agent/structs/acl_oss.go b/agent/structs/acl_oss.go index e0b69b0577..d122010330 100644 --- a/agent/structs/acl_oss.go +++ b/agent/structs/acl_oss.go @@ -70,10 +70,6 @@ func (p *ACLPolicy) EnterprisePolicyMeta() *acl.EnterprisePolicyMeta { return nil } -func (m *ACLAuthMethod) TargetEnterpriseMeta(_ *EnterpriseMeta) *EnterpriseMeta { - return &m.EnterpriseMeta -} - func (t *ACLToken) NodeIdentityList() []*ACLNodeIdentity { if len(t.NodeIdentities) == 0 { return nil From feaf45214bf013a5a9288eddb602e6e4de9cdc4d Mon Sep 17 00:00:00 2001 From: Kim Ngo <6362111+findkim@users.noreply.github.com> Date: Fri, 15 Oct 2021 10:33:34 -0500 Subject: [PATCH 18/20] docs: add partner badge (#11315) * docs: add partner badge * updates analytics package Co-authored-by: Bryce Kalow --- website/content/docs/integrate/partnerships.mdx | 12 +++++++----- website/package-lock.json | 14 +++++++------- website/package.json | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/website/content/docs/integrate/partnerships.mdx b/website/content/docs/integrate/partnerships.mdx index 78c8b11c81..2c5d235800 100644 --- a/website/content/docs/integrate/partnerships.mdx +++ b/website/content/docs/integrate/partnerships.mdx @@ -10,18 +10,20 @@ The HashiCorp Consul Integration Program enables prospective partners to build i All integrations are available with Consul’s self-managed version. In some cases, these integrations can also be validated against HCP Consul. Upon completion of the validation with HCP Consul, a partner will receive a HCP Consul Verified badge which will be displayed on their partner page and utilized on the partner’s website as well. +HCP Consul Badge + The program is intended to be largely self-service with links to resources, code samples, documentation, and clear integration steps. ## Categories of Consul Integrations By leveraging Consul’s RESTful HTTP API system, prospective partners are able to build extensible integrations at the data plane, platform, and the infrastructure layer to extend Consul’s functionalities. These integrations can be performed both with the OSS (open source) version of Consul, Consul Enterprise, and HCP Consul. For features in Consul Enterprise and HCP Consul, refer to the links below: -[HCP Consul Features](https://cloud.hashicorp.com/docs/consul/features) -[Consul Enterprise Features](/docs/enterprise) +- [HCP Consul Features](https://cloud.hashicorp.com/docs/consul/features) +- [Consul Enterprise Features](/docs/enterprise) **The Consul Ecosystem Architecture** -[![Consul Architecture](/img/consul_ecosystem_diagram2.png)](/img/consul_ecosystem_diagram2.png) +Consul Architecture **Data Plane**: These integrations extend Consul’s certificate management, secure ACL configuration, observability metrics and logging, and service discovery that allows for dynamic service mapping APM and logging tools, extend sidecar proxies to support Consul connect, and extend API gateways to allow Consul to route incoming traffic to the proxies for Connect-enabled services. @@ -31,13 +33,13 @@ By leveraging Consul’s RESTful HTTP API system, prospective partners are able **Infrastructure**: There are two integration options in this category: natively through a direct integration with Consul or via Consul-Terraform-Sync (CTS). By leveraging Consul’s powerful **Network Infrastructure Automation (NIA)*** capabilities through CTS, changes in an infrastructure are seamlessly automated when Consul detects a change in its service catalog. For example, these integrations could be used to automate IP updates of load balancers or firewall security policies by leveraging Consul service discovery. -**Network Infrastructure Automation (NIA)***: These integrations leverage Consul’s service catalog to seamlessly integrate with Consul-Terraform-Sync (CTS) to automate changes in network infrastructure via a publisher-subscriber method. More details can be found [here](https://www.consul.io/docs/integrate/nia-integration). +-> **Network Infrastructure Automation (NIA)***: These integrations leverage Consul’s service catalog to seamlessly integrate with Consul-Terraform-Sync (CTS) to automate changes in network infrastructure via a publisher-subscriber method. More details can be found [here](https://www.consul.io/docs/integrate/nia-integration). ## Development Process The Consul integration development process is described in the steps below. By following these steps, Consul integrations can be developed alongside HashiCorp to ensure new integrations are reviewed, approved and released as quickly as possible. -[![Integration Program Steps](/img/consul_integration_program_steps.png)](/img/consul_integration_program_steps) +Integration Program Steps 1. Engage: Initial contact between vendor and HashiCorp 2. Enable: Documentation, code samples and best practices for developing the integration diff --git a/website/package-lock.json b/website/package-lock.json index 12262d983d..c6b36d0c5c 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@hashicorp/mktg-global-styles": "^4.0.0", "@hashicorp/mktg-logos": "^1.2.0", - "@hashicorp/platform-analytics": "^0.1.0", + "@hashicorp/platform-analytics": "^0.2.0", "@hashicorp/platform-code-highlighting": "^0.1.2", "@hashicorp/platform-runtime-error-monitoring": "^0.1.0", "@hashicorp/platform-util": "^0.1.0", @@ -770,9 +770,9 @@ } }, "node_modules/@hashicorp/platform-analytics": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@hashicorp/platform-analytics/-/platform-analytics-0.1.0.tgz", - "integrity": "sha512-kR/E0KWemjazSrSFN9l22v8JqlQvkgjgkZHPJEhGiBt05LMtt2ugxIakv+gnzW5lU9434nnr7oDooUyj4eufPA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@hashicorp/platform-analytics/-/platform-analytics-0.2.0.tgz", + "integrity": "sha512-4Pmb4Fy/2eDCZPFu/O4wKK2L5VIqwsXfDPDGX3eA4Dk67ytZ//SXuW9oFtG97ACyW/p1i72EmwoqcrR6usDwtg==", "dependencies": { "fathom-client": "^3.2.0" }, @@ -20574,9 +20574,9 @@ } }, "@hashicorp/platform-analytics": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@hashicorp/platform-analytics/-/platform-analytics-0.1.0.tgz", - "integrity": "sha512-kR/E0KWemjazSrSFN9l22v8JqlQvkgjgkZHPJEhGiBt05LMtt2ugxIakv+gnzW5lU9434nnr7oDooUyj4eufPA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@hashicorp/platform-analytics/-/platform-analytics-0.2.0.tgz", + "integrity": "sha512-4Pmb4Fy/2eDCZPFu/O4wKK2L5VIqwsXfDPDGX3eA4Dk67ytZ//SXuW9oFtG97ACyW/p1i72EmwoqcrR6usDwtg==", "requires": { "fathom-client": "^3.2.0" } diff --git a/website/package.json b/website/package.json index 5d3090cb04..8b857ae6c6 100644 --- a/website/package.json +++ b/website/package.json @@ -6,7 +6,7 @@ "dependencies": { "@hashicorp/mktg-global-styles": "^4.0.0", "@hashicorp/mktg-logos": "^1.2.0", - "@hashicorp/platform-analytics": "^0.1.0", + "@hashicorp/platform-analytics": "^0.2.0", "@hashicorp/platform-code-highlighting": "^0.1.2", "@hashicorp/platform-runtime-error-monitoring": "^0.1.0", "@hashicorp/platform-util": "^0.1.0", From 92d0aa05f0f94d1ed88ea24761b47e83b529e435 Mon Sep 17 00:00:00 2001 From: Kim Ngo <6362111+findkim@users.noreply.github.com> Date: Fri, 15 Oct 2021 11:01:10 -0500 Subject: [PATCH 19/20] Add protocol example for TFC driver address (#11319) * Add protocol example for TFC driver address * Format cts code blocks --- website/content/docs/nia/configuration.mdx | 91 ++++++++++--------- .../docs/nia/installation/configure.mdx | 28 +++--- .../content/docs/nia/installation/install.mdx | 4 +- website/content/docs/nia/tasks.mdx | 54 +++++------ .../content/docs/nia/terraform-modules.mdx | 40 ++++---- 5 files changed, 115 insertions(+), 102 deletions(-) diff --git a/website/content/docs/nia/configuration.mdx b/website/content/docs/nia/configuration.mdx index aef59cb387..24a0aaef1d 100644 --- a/website/content/docs/nia/configuration.mdx +++ b/website/content/docs/nia/configuration.mdx @@ -14,16 +14,18 @@ The Consul-Terraform-Sync daemon is configured using configuration files and sup Top level options are reserved for configuring Consul-Terraform-Sync. ```hcl -log_level = "INFO" +log_level = "INFO" working_dir = "sync-tasks" -port = 8558 +port = 8558 + syslog { facility = "local2" } + buffer_period { enabled = true - min = "5s" - max = "20s" + min = "5s" + max = "20s" } ``` @@ -89,8 +91,8 @@ A `service` block is an optional block to explicitly define configuration of ser ```hcl service { - name = "web" - datacenter = "dc1" + name = "web" + datacenter = "dc1" description = "all instances of the service web in datacenter dc1" } ``` @@ -110,13 +112,13 @@ A `task` block configures which task to execute in automation. When the task sho ```hcl task { - name = "taskA" - description = "" - enabled = true, - providers = [] - services = ["web", "api"] - source = "org/example/module" - version = "1.0.0" + name = "taskA" + description = "" + enabled = true, + providers = [] + services = ["web", "api"] + source = "org/example/module" + version = "1.0.0" variable_files = [] condition "catalog-services" { regexp = ".*" @@ -147,14 +149,19 @@ task { - `variable_files` - (list[string]) Specifies list of paths to [Terraform variable definition files (`.tfvars`)](https://www.terraform.io/docs/configuration/variables.html#variable-definitions-tfvars-files). The content of these files should consist of only variable name assignments. The variable assignments must match the corresponding variable declarations made available by the Terraform module for the task. - Variables are loaded in the order they appear in the files. Duplicate variables are overwritten with the later value. _Unless specified by the module, configure arguments for Terraform providers using [`terraform_provider` blocks](#terraform-provider)._ + + + ```hcl - # example.tfvars address_group = "consul-services" tags = [ "consul-terraform-sync", "terraform" ] ``` + + + - `version` - (string) The version of the provided source the task will use. For the [Terraform driver](#terraform-driver), this is the module version. The latest version will be used as the default if omitted. - `working_dir` - (string) The working directory to manage generated artifacts by Consul-Terraform-Sync for this task, including Terraform configuration files. By default, a working directory is created for each task as a subdirectory in the base [`working_dir`](#working_dir), e.g. `sync-tasks/task-name`. - `buffer_period` - Configures the buffer period for a dynamic task to dampen the effects of flapping services to downstream network devices. It defines the minimum and maximum amount of time to wait for the cluster to reach a consistent state and accumulate changes before triggering task execution. The default is inherited from the top level [`buffer_period` block](#global-config-options). If configured, these values will take precedence over the global buffer period. This is useful to enable for a task that is dependent on services that have a lot of flapping. Buffer periods do not apply to scheduled tasks. @@ -177,10 +184,10 @@ See [Task Execution: Services Condition](/docs/nia/tasks#services-condition) for ```hcl task { - name = "services_condition_task" + name = "services_condition_task" description = "execute on changes to services with names starting with web" - providers = ["my-provider"] - source = "path/to/services-condition-module" + providers = ["my-provider"] + source = "path/to/services-condition-module" condition "services" { regexp = "^web.*" @@ -198,10 +205,10 @@ See [Task Execution: Catalog Services Condition](/docs/nia/tasks#catalog-service ```hcl task { - name = "catalog_service_condition_task" + name = "catalog_service_condition_task" description = "execute on service de/registrations with name matching 'web.*'" - source = "path/to/catalog-services-module" - providers = ["my-provider"] + source = "path/to/catalog-services-module" + providers = ["my-provider"] // configure depending on module. provides detailed information for these // services but does not execute task. refer to module docs on how to configure. @@ -233,11 +240,11 @@ See [Task Execution: Consul KV Condition](/docs/nia/tasks#consul-kv-condition) f ```hcl task { - name = "consul_kv_condition_task" + name = "consul_kv_condition_task" description = "execute on changes to Consul KV entry" - source = "path/to/consul-kv-module" - providers = ["my-provider"] - services = ["web-api"] + source = "path/to/consul-kv-module" + providers = ["my-provider"] + services = ["web-api"] condition "consul-kv" { path = "my-key" @@ -267,10 +274,10 @@ See [Terraform Module: Source Input](/docs/nia/terraform-modules#source-input) f ```hcl task { - name = "scheduled_task" + name = "scheduled_task" description = "execute every Monday using service information from web and db" - services = ["web", "db"] - source = "path/to/module" + services = ["web", "db"] + source = "path/to/module" condition "schedule" { cron = "* * * * Mon" } @@ -355,9 +362,9 @@ The Terraform driver block is used to configure Consul-Terraform-Sync for instal ```hcl driver "terraform" { - log = false + log = false persist_log = false - path = "" + path = "" backend "consul" { gzip = true @@ -365,7 +372,7 @@ driver "terraform" { required_providers { myprovider = { - source = "namespace/myprovider" + source = "namespace/myprovider" version = "1.3.0" } } @@ -401,7 +408,7 @@ Only one network driver can be configured per deployment of Consul-Terraform-Syn ```hcl driver "terraform-cloud" { - hostname = "my.tfe.hostname.io" + hostname = "https://app.terraform.io" organization = "my-org" token = "" // Optionally set the token to be securely queried from Vault instead of @@ -410,7 +417,7 @@ driver "terraform-cloud" { required_providers { myprovider = { - source = "namespace/myprovider" + source = "namespace/myprovider" version = "1.3.0" } } @@ -452,21 +459,21 @@ The below configuration captures the general design of defining a provider using driver "terraform" { required_providers { aws = { - source = "hashicorp/aws" + source = "hashicorp/aws" version = "3.33.0" } } } terraform_provider "aws" { - # Configuration options + // Configuration options region = "us-east-1" } task { - source = "some/source" + source = "some/source" providers = ["aws"] - services = ["web", "api"] + services = ["web", "api"] } ``` @@ -572,7 +579,7 @@ The example Consul-Terraform-Sync configuration below defines two similar tasks ```hcl terraform_provider "aws" { - alias = "a" + alias = "a" profile = "team-a" task_env { "AWS_ACCESS_KEY_ID" = "{{ env \"CTS_AWS_ACCESS_KEY_ID_A\" }}" @@ -580,7 +587,7 @@ terraform_provider "aws" { } terraform_provider "aws" { - alias = "b" + alias = "b" profile = "team-b" task_env { "AWS_ACCESS_KEY_ID" = "{{ env \"CTS_AWS_ACCESS_KEY_ID_B\" }}" @@ -592,15 +599,15 @@ terraform_provider "dns" { } task { - name = "task-a" - source = "org/module" + name = "task-a" + source = "org/module" providers = ["aws.a", "dns"] // ... } task { - name = "task-b" - source = "org/module" + name = "task-b" + source = "org/module" providers = ["aws.b", "dns"] // ... } diff --git a/website/content/docs/nia/installation/configure.mdx b/website/content/docs/nia/installation/configure.mdx index 89f2df02a2..cade6131f8 100644 --- a/website/content/docs/nia/installation/configure.mdx +++ b/website/content/docs/nia/installation/configure.mdx @@ -19,12 +19,12 @@ Review the Terraform module to be used for network automation and identify the T ```hcl task { - name = "website-x" + name = "website-x" description = "automate services for website-x" - source = "namespace/example/module" - version = "1.0.0" - providers = ["myprovider"] - services = ["web", "api"] + source = "namespace/example/module" + version = "1.0.0" + providers = ["myprovider"] + services = ["web", "api"] } ``` @@ -36,7 +36,7 @@ Configuring Terraform providers within Consul-Terraform-Sync requires 2 config c driver "terraform" { required_providers { myprovider = { - source = "namespace/myprovider" + source = "namespace/myprovider" version = "1.3.0" } } @@ -59,6 +59,8 @@ Piecing it all together, the configuration file for Consul-Terraform-Sync will h An example HCL configuration file is shown below to automate one task to execute a Terraform module on the condition when there are changes to two services. + + ```hcl log_level = "info" @@ -71,12 +73,12 @@ consul { } task { - name = "website-x" + name = "website-x" description = "automate services for website-x" - source = "namespace/example/module" - version = "1.0.0" - providers = ["myprovider"] - services = ["web", "api"] + source = "namespace/example/module" + version = "1.0.0" + providers = ["myprovider"] + services = ["web", "api"] buffer_period { min = "10s" } @@ -87,7 +89,7 @@ driver "terraform" { required_providers { myprovider = { - source = "namespace/myprovider" + source = "namespace/myprovider" version = "1.3.0" } } @@ -97,3 +99,5 @@ terraform_provider "myprovider" { address = "myprovider.example.com" } ``` + + diff --git a/website/content/docs/nia/installation/install.mdx b/website/content/docs/nia/installation/install.mdx index 4a7e0087a6..6d18959af3 100644 --- a/website/content/docs/nia/installation/install.mdx +++ b/website/content/docs/nia/installation/install.mdx @@ -46,7 +46,7 @@ Consul-Terraform-Sync connects with your Consul cluster in order to monitor the ```hcl consul { address = "localhost:8500" - token = "my-consul-acl-token" + token = "my-consul-acl-token" } ``` @@ -58,7 +58,7 @@ Once you have identified a Terraform provider for all of your network devices, y ```hcl terraform_provider "fake-firewall" { - address = "10.10.10.10" + address = "10.10.10.10" username = "admin" password = "password123" } diff --git a/website/content/docs/nia/tasks.mdx b/website/content/docs/nia/tasks.mdx index f868062542..6866f42066 100644 --- a/website/content/docs/nia/tasks.mdx +++ b/website/content/docs/nia/tasks.mdx @@ -13,12 +13,12 @@ Below is an example task configuration: ```hcl task { - name = "frontend-firewall-policies" + name = "frontend-firewall-policies" description = "Add firewall policy rules for frontend services" - providers = ["fake-firewall", "null"] - services = ["web", "image"] - source = "example/firewall-policy/module" - version = "1.0.0" + providers = ["fake-firewall", "null"] + services = ["web", "image"] + source = "example/firewall-policy/module" + version = "1.0.0" } ``` @@ -69,19 +69,19 @@ The services condition is the default behavior if no `condition` block is config ```hcl task { - name = "services_condition_task_1" + name = "services_condition_task_1" description = "execute on changes to api, db, and web services" - providers = ["my-provider"] - source = "path/to/services-condition-module" - services = ["api", "db", "web"] + providers = ["my-provider"] + source = "path/to/services-condition-module" + services = ["api", "db", "web"] } task { - name = "services_condition_task_2" + name = "services_condition_task_2" description = "execute on changes to api, db, and web services" - providers = ["my-provider"] - source = "path/to/services-condition-module" - services = ["api", "db", "web"] + providers = ["my-provider"] + source = "path/to/services-condition-module" + services = ["api", "db", "web"] condition "services" {} } @@ -91,10 +91,10 @@ Below is an example configuration for a task that will execute when a service wi ```hcl task { - name = "services_condition_task" + name = "services_condition_task" description = "execute on changes to services whose name starts with web" - providers = ["my-provider"] - source = "path/to/services-condition-module" + providers = ["my-provider"] + source = "path/to/services-condition-module" condition "services" { regexp = "^web.*" @@ -112,10 +112,10 @@ Below is an example configuration for a task that will execute when a service wi ```hcl task { - name = "catalog_service_condition_task" - source = "path/to/catalog-services-module" + name = "catalog_service_condition_task" + source = "path/to/catalog-services-module" providers = ["my-provider"] - services = ["web-api"] + services = ["web-api"] condition "catalog-services" { datacenter = "dc1" @@ -125,7 +125,7 @@ task { } service { - name = "web-api" + name = "web-api" datacenter = "dc2" } ``` @@ -144,11 +144,11 @@ Based on the `recurse` option, the condition either monitors a single Consul KV ```hcl task { - name = "consul_kv_condition_task" + name = "consul_kv_condition_task" description = "execute on changes to Consul KV entry" - source = "path/to/consul-kv-module" - providers = ["my-provider"] - services = ["web-api"] + source = "path/to/consul-kv-module" + providers = ["my-provider"] + services = ["web-api"] condition "consul-kv" { path = "my-key" @@ -170,10 +170,10 @@ Below is an example configuration for a task that will execute every Monday, whi ```hcl task { - name = "scheduled_task" + name = "scheduled_task" description = "execute every Monday using service information from web and db" - services = ["web", "db"] - source = "path/to/module" + services = ["web", "db"] + source = "path/to/module" condition "schedule" { cron = "* * * * Mon" diff --git a/website/content/docs/nia/terraform-modules.mdx b/website/content/docs/nia/terraform-modules.mdx index e56def12af..e62e5c37f0 100644 --- a/website/content/docs/nia/terraform-modules.mdx +++ b/website/content/docs/nia/terraform-modules.mdx @@ -118,10 +118,10 @@ Below is an example configuration for a task that will execute on a schedule and ```hcl task { - name = "services_condition_task" + name = "services_condition_task" description = "execute on changes to services whose name starts with web" - providers = ["my-provider"] - source = "path/to/services-condition-module" + providers = ["my-provider"] + source = "path/to/services-condition-module" condition "schedule" { cron = "* * * * Mon" } @@ -156,20 +156,22 @@ Below is a similar example to the one provided in the [Consul KV Condition](/doc ```hcl task { - name = "consul_kv_schedule_task" + name = "consul_kv_schedule_task" description = "executes on Monday monitoring Consul KV" - providers = ["my-provider"] - services = ["web-api"] - source = "path/to/consul-kv-module" - source_input "consul-kv" { - path = "my-key" - recurse = true - datacenter = "dc1" - namespace = "default" - } + providers = ["my-provider"] + services = ["web-api"] + source = "path/to/consul-kv-module" + condition "schedule" { cron = "* * * * Mon" } + + source_input "consul-kv" { + path = "my-key" + recurse = true + datacenter = "dc1" + namespace = "default" + } } ``` @@ -201,11 +203,11 @@ Example of a catalog-services condition which supports source input through `sou ```hcl task { - name = "catalog_services_condition_task" + name = "catalog_services_condition_task" description = "execute on registration/deregistration of services" - providers = ["my-provider"] - services = ["web-api"] - source = "path/to/catalog-services-module" + providers = ["my-provider"] + services = ["web-api"] + source = "path/to/catalog-services-module" condition "catalog-services" { datacenter = "dc1" namespace = "default" @@ -312,7 +314,7 @@ If you are creating a module for a [catalog-services condition](/docs/nia/tasks# ```hcl variable "catalog_services" { description = "Consul catalog service names and tags monitored by Consul-Terraform-Sync" - type = map(list(string)) + type = map(list(string)) } ``` @@ -333,7 +335,7 @@ If you are creating a module for a [consul-kv condition](/docs/nia/tasks#consul- ```hcl variable "consul_kv" { description = "Keys and values of the Consul KV pairs monitored by Consul-Terraform-Sync" - type = map(string) + type = map(string) } ```