From ea9d724c6e7ba84e52effdb0f2f9e00ffd103dfc Mon Sep 17 00:00:00 2001 From: Jim Enright Date: Fri, 1 Oct 2021 14:13:29 +0100 Subject: [PATCH 1/5] Add parameters for Terraform deployment engine Signed-off-by: Jim Enright --- roles/cloudera_deploy/defaults/main.yml | 1 + roles/cloudera_deploy/tasks/init.yml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/roles/cloudera_deploy/defaults/main.yml b/roles/cloudera_deploy/defaults/main.yml index 35e16e0..a3b304e 100644 --- a/roles/cloudera_deploy/defaults/main.yml +++ b/roles/cloudera_deploy/defaults/main.yml @@ -47,5 +47,6 @@ default_parcel_distro: el7 # el8, bionic, focal default_download_link_expiry: 3600 # Default Deployment Controls +default_infra_deployment_engine: ansible default_infra_type: aws default_infra_region: us-east-1 diff --git a/roles/cloudera_deploy/tasks/init.yml b/roles/cloudera_deploy/tasks/init.yml index fec411c..d4c8f83 100644 --- a/roles/cloudera_deploy/tasks/init.yml +++ b/roles/cloudera_deploy/tasks/init.yml @@ -173,7 +173,13 @@ name_prefix: "{{ name_prefix | default(default_name_prefix) }}" tags: "{{ tags | default(omit) }}" region: "{{ infra_region | default(default_infra_region) }}" + infra_deployment_engine: "{{ infra_deployment_engine | default(default_infra_deployment_engine) }}" infra_type: "{{ infra_type | default(default_infra_type) }}" + terraform_template_dir: "{{ terraform_template_dir | default(omit) }}" + terraform_workspace_dir: "{{ terraform_workspace_dir | default(omit) }}" + terraform_state_storage: "{{ terraform_state_storage | default(omit) }}" + terraform_remote_state_bucket: "{{ terraform_remote_state_bucket | default(omit) }}" + terraform_remote_state_lock_table: "{{ terraform_remote_state_lock_table | default(omit) }}" ssh: public_key_id: "{{ public_key_id | default(omit) }}" public_key_file: "{{ public_key_file | default(omit) }}" From 79a0816fb3e0a2181f37040249c5dd92aee9eb9d Mon Sep 17 00:00:00 2001 From: Jim Enright Date: Tue, 5 Oct 2021 15:42:05 +0100 Subject: [PATCH 2/5] Update README with details of Terraform deployment Signed-off-by: Jim Enright --- readme.adoc | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/readme.adoc b/readme.adoc index 9cac8e4..7a90d23 100644 --- a/readme.adoc +++ b/readme.adoc @@ -447,6 +447,46 @@ WARNING: Setting a deployment to a lower runlevel, e.g. from `run` to `infra` wi For further details on the various _runlevel_-like tags for CDP Public Cloud, see the https://github.com/cloudera-labs/cloudera.exe/blob/main/docs/runlevels.md[Runlevel Guide] in the `cloudera.exe` project. +=== Terraform Deployment Engine + +Terraform can optionally be used to create the cloud infrastructure. This will attempt to create the cloud provider assets at the `infra` (network, storage and compute) and `plat` (IAM policies and roles) runlevels using Terraform resources. A list of Terraform related parameters are shown in the table below. + +.List of parameters used by Terraform deployment engine +[cols="1,1,1,1"] +|=== +|Parameter|Description|Default Value|Notes + +|`infra_deployment_engine` +|The engine (ansible or terraform) that will be used to create the infrastructure resources. +| `ansible` +| Needs to be set to `terraform` for Terraform-deployment. + +|`terraform_template_dir` +|Directory where processed Jinja template files for Terraform are placed. A timestamped artefact directory with a copy of the Terraform files from each execution of cloudera-deploy are also placed under this directory. +| `{{ playbook_dir }}/terraform_processed_template_code` +| + +|`terraform_workspace_dir` +|Directory from where the terraform apply is run. The latest template files are copied to this directory as part of the setup steps. +| `{{ playbook_dir }}/.namespaces` +| + +|`terraform_state_storage` +|The type of backend storage to use for the Terraform state. | +| `local` +| Current options are `local` or `remote_s3` + +|`terraform_remote_state_bucket` +|The name of the Terraform state storage bucket. | +| +| Required if using `remote_s3` state storage + +|`terraform_remote_state_lock_table` +|The name of the table to track locks of remote Terraform state. +| +| Required if using `remote_s3` state storage +|=== + == Definitions Cloudera Deploy uses a set of configuration files within a directory to define and coordinate a deployment. This directory also stores any artifacts created during the deployment, such as Ansible inventory files, CDP environment readouts, etc. From f176ca44bb270e7ae853d838158c1e5e283a080e Mon Sep 17 00:00:00 2001 From: Jim Enright Date: Mon, 13 Dec 2021 14:40:10 +0000 Subject: [PATCH 3/5] Update to Terraform deployment engine following PR feedback Signed-off-by: Jim Enright --- main.yml | 17 +++++ readme.adoc | 24 +++---- roles/cloudera_deploy/defaults/main.yml | 3 + .../tasks/auto_terraform_state.yml | 61 ++++++++++++++++ roles/cloudera_deploy/tasks/init.yml | 72 ++++++++++++++++++- 5 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 roles/cloudera_deploy/tasks/auto_terraform_state.yml diff --git a/main.yml b/main.yml index bbb2489..eb1ed77 100644 --- a/main.yml +++ b/main.yml @@ -38,6 +38,23 @@ name: cloudera_deploy tasks_from: clean_dynamic_inventory +# Teardown Terraform remote state resources if requested +- name: Teardown Terraform remote state resources + hosts: localhost + tags: [teardown,never] + gather_facts: yes + tasks: + - name: Remove remote state resources if requested + when: + - globals.infra_deployment_engine == 'terraform' + - globals.terraform_auto_remote_state | bool + - globals.terraform_state_storage in ['remote_s3'] + ansible.builtin.include_role: + name: cloudera_deploy + tasks_from: auto_terraform_state + vars: + auto_terraform_state_action: teardown + - name: Prepare for Cloudera Cluster Run hosts: localhost tags: always diff --git a/readme.adoc b/readme.adoc index 7a90d23..a32d350 100644 --- a/readme.adoc +++ b/readme.adoc @@ -461,30 +461,30 @@ Terraform can optionally be used to create the cloud infrastructure. This will a | `ansible` | Needs to be set to `terraform` for Terraform-deployment. -|`terraform_template_dir` -|Directory where processed Jinja template files for Terraform are placed. A timestamped artefact directory with a copy of the Terraform files from each execution of cloudera-deploy are also placed under this directory. -| `{{ playbook_dir }}/terraform_processed_template_code` -| - -|`terraform_workspace_dir` -|Directory from where the terraform apply is run. The latest template files are copied to this directory as part of the setup steps. -| `{{ playbook_dir }}/.namespaces` +|`terraform_base_dir` +| Top-level directory where all Terraform assets will be placed. Includes processed Jinja template files for Terraform, timestamped artefact of Terraform files and the workspace directory where terraform apply/destroy is run. +| `~/.config/cloudera-deploy/terraform` | |`terraform_state_storage` -|The type of backend storage to use for the Terraform state. | +|The type of backend storage to use for the Terraform state. | `local` | Current options are `local` or `remote_s3` +|`terraform_auto_remote_state` +| Flag to allow Cloudera Deploy automatically provision remote state resources as part of its initialization. This will also teardown these resources during cleanup. +| `False` +| + |`terraform_remote_state_bucket` -|The name of the Terraform state storage bucket. | +|The name of the Terraform state storage bucket. | -| Required if using `remote_s3` state storage +| Required if using `remote_s3` state storage. Value is derived from `name_prefix` if terraform_auto_remote_state is True. |`terraform_remote_state_lock_table` |The name of the table to track locks of remote Terraform state. | -| Required if using `remote_s3` state storage +| Required if using `remote_s3` state storage. Value is derived from `name_prefix` if terraform_auto_remote_state is True. |=== == Definitions diff --git a/roles/cloudera_deploy/defaults/main.yml b/roles/cloudera_deploy/defaults/main.yml index a3b304e..bcdebbe 100644 --- a/roles/cloudera_deploy/defaults/main.yml +++ b/roles/cloudera_deploy/defaults/main.yml @@ -50,3 +50,6 @@ default_download_link_expiry: 3600 default_infra_deployment_engine: ansible default_infra_type: aws default_infra_region: us-east-1 + +# Terraform defaults +default_terraform_base_dir: "{{ [default_config_path, 'terraform'] | path_join }}" \ No newline at end of file diff --git a/roles/cloudera_deploy/tasks/auto_terraform_state.yml b/roles/cloudera_deploy/tasks/auto_terraform_state.yml new file mode 100644 index 0000000..f6996f8 --- /dev/null +++ b/roles/cloudera_deploy/tasks/auto_terraform_state.yml @@ -0,0 +1,61 @@ +--- + +# Copyright 2021 Cloudera, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Resources for remote_s3 state storage + when: + - globals.terraform_state_storage == 'remote_s3' + block: + + # Determine resource state based on auto_terraform_state_action variable + - name: Set variables for resource creation + when: auto_terraform_state_action == "create" + ansible.builtin.set_fact: + __bucket_mode: create + __dynamodb_table_state: present + + - name: Set variables for resource teardown + when: auto_terraform_state_action == "teardown" + ansible.builtin.set_fact: + __bucket_mode: delete + __dynamodb_table_state: absent + + # Create or Teardown the resources + - name: AWS Bucket for Remote State Storage + amazon.aws.aws_s3: + region: "{{ globals.region }}" + bucket: "{{ globals.terraform_remote_state_bucket}}" + mode: "{{ __bucket_mode }}" + permission: private + register: __infra_aws_storage_locations_info + + - name: AWS DynamoDB for Remote State Locking + community.aws.dynamodb_table: + region: "{{ globals.region }}" + name: "{{ globals.terraform_remote_state_lock_table }}" + read_capacity: 1 + write_capacity: 1 + hash_key_name: LockID + hash_key_type: STRING + state: "{{ __dynamodb_table_state }}" + + - name: Print remote state configuration + when: auto_terraform_state_action == "create" + ansible.builtin.debug: + msg: + - "Resources for remote_s3 Terraform State created." + - "S3 Bucket Name: {{ globals.terraform_remote_state_bucket}}" + - "DynamoDB Locking Table: {{ globals.terraform_remote_state_lock_table}}" + verbosity: 3 \ No newline at end of file diff --git a/roles/cloudera_deploy/tasks/init.yml b/roles/cloudera_deploy/tasks/init.yml index d4c8f83..970fca4 100644 --- a/roles/cloudera_deploy/tasks/init.yml +++ b/roles/cloudera_deploy/tasks/init.yml @@ -175,9 +175,9 @@ region: "{{ infra_region | default(default_infra_region) }}" infra_deployment_engine: "{{ infra_deployment_engine | default(default_infra_deployment_engine) }}" infra_type: "{{ infra_type | default(default_infra_type) }}" - terraform_template_dir: "{{ terraform_template_dir | default(omit) }}" - terraform_workspace_dir: "{{ terraform_workspace_dir | default(omit) }}" + terraform_base_dir: "{{ terraform_base_dir | default(default_terraform_base_dir) | expanduser }}" terraform_state_storage: "{{ terraform_state_storage | default(omit) }}" + terraform_auto_remote_state: "{{ terraform_auto_remote_state | default(False) }}" terraform_remote_state_bucket: "{{ terraform_remote_state_bucket | default(omit) }}" terraform_remote_state_lock_table: "{{ terraform_remote_state_lock_table | default(omit) }}" ssh: @@ -223,6 +223,31 @@ fail_msg: "You must supply a valid Namespace" quiet: yes +- name: Check Deployment Engine variable + ansible.builtin.assert: + that: + - globals.infra_deployment_engine in ['ansible', 'terraform'] + fail_msg: "The 'infra_deployment_engine' variable must be one of 'ansible', 'terraform'" + +- name: Check Supplied terraform_base_dir variable + when: + - infra_deployment_engine == 'terraform' + ansible.builtin.assert: + that: + - globals.terraform_base_dir is defined + - globals.terraform_base_dir | length > 0 + fail_msg: "You must supply a 'terraform_base_dir' where Terraform assets will be placed" + quiet: yes + +- name: Check Supplied terraform_auto_remote_state variable + when: + - infra_deployment_engine == 'terraform' + ansible.builtin.assert: + that: + - (globals.terraform_auto_remote_state|bool is sameas true) or (globals.terraform_auto_remote_state|bool is sameas false) + fail_msg: "The terraform_auto_remote_state variable must be a boolean variable" + quiet: yes + # SSH - name: Use default SSH public key id when: globals.ssh.public_key_id is undefined @@ -411,3 +436,46 @@ fail_msg: >- Admin Password must comply with CDP Public requirements: 1 Upper, 1 Special, 1 Number, 8-64 chars. quiet: yes + +# Provision Terraform remote state resources if requested +- name: Initialize Terraform remote state resources + when: + - init__call_cloud_role | bool + - globals.infra_deployment_engine == 'terraform' + - globals.terraform_auto_remote_state | bool + - globals.terraform_state_storage in ['remote_s3'] + block: + # Set resource variable names if not already done + - name: Set variables for remote state bucket if not set + when: (globals.terraform_remote_state_bucket is not defined) or + ( (globals.terraform_remote_state_bucket) | length == 0) + ansible.builtin.set_fact: + globals: "{{ globals | default({}) | combine(remote_state_vars, recursive=True) }}" + vars: + remote_state_vars: + terraform_remote_state_bucket: "{{ [globals.name_prefix, 'state-bucket'] | join('-') }}" + + - name: Set variables for remote state lock table if not set + when: (globals.terraform_remote_state_lock_table is not defined) or + (globals.terraform_remote_state_lock_table | length == 0) + ansible.builtin.set_fact: + globals: "{{ globals | default({}) | combine(remote_state_vars, recursive=True) }}" + vars: + remote_state_vars: + terraform_remote_state_lock_table: "{{ [globals.name_prefix, 'state-lock-table'] | join('-') }}" + + - name: Create remote state resources + when: "'teardown' not in ansible_run_tags" + ansible.builtin.include_role: + name: cloudera_deploy + tasks_from: auto_terraform_state + vars: + auto_terraform_state_action: create + +- name: Prepare for Download Mirror Population if requested and necessary + when: + - use_download_mirror | default(default_enable_download_mirror) | bool + - "'teardown' not in ansible_run_tags" + ansible.builtin.include_role: + name: cloudera_deploy + tasks_from: prepare_download_mirror \ No newline at end of file From 22dafe22927a610d23c4b7afd4c212b4ded9a9d2 Mon Sep 17 00:00:00 2001 From: Jim Enright Date: Mon, 13 Dec 2021 16:49:45 +0000 Subject: [PATCH 4/5] Use ansible tags to determine action on remote state resources Signed-off-by: Jim Enright --- main.yml | 2 -- .../tasks/auto_terraform_state.yml | 21 ++++--------------- roles/cloudera_deploy/tasks/init.yml | 2 -- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/main.yml b/main.yml index eb1ed77..a7bd17d 100644 --- a/main.yml +++ b/main.yml @@ -52,8 +52,6 @@ ansible.builtin.include_role: name: cloudera_deploy tasks_from: auto_terraform_state - vars: - auto_terraform_state_action: teardown - name: Prepare for Cloudera Cluster Run hosts: localhost diff --git a/roles/cloudera_deploy/tasks/auto_terraform_state.yml b/roles/cloudera_deploy/tasks/auto_terraform_state.yml index f6996f8..82bf2ed 100644 --- a/roles/cloudera_deploy/tasks/auto_terraform_state.yml +++ b/roles/cloudera_deploy/tasks/auto_terraform_state.yml @@ -19,25 +19,12 @@ - globals.terraform_state_storage == 'remote_s3' block: - # Determine resource state based on auto_terraform_state_action variable - - name: Set variables for resource creation - when: auto_terraform_state_action == "create" - ansible.builtin.set_fact: - __bucket_mode: create - __dynamodb_table_state: present - - - name: Set variables for resource teardown - when: auto_terraform_state_action == "teardown" - ansible.builtin.set_fact: - __bucket_mode: delete - __dynamodb_table_state: absent - # Create or Teardown the resources - name: AWS Bucket for Remote State Storage amazon.aws.aws_s3: region: "{{ globals.region }}" bucket: "{{ globals.terraform_remote_state_bucket}}" - mode: "{{ __bucket_mode }}" + mode: "{{ ('teardown' not in ansible_run_tags) | ternary('create', 'delete') }}" # Check ansible tag to determine action permission: private register: __infra_aws_storage_locations_info @@ -48,11 +35,11 @@ read_capacity: 1 write_capacity: 1 hash_key_name: LockID - hash_key_type: STRING - state: "{{ __dynamodb_table_state }}" + hash_key_type: STRING + state: "{{ ('teardown' not in ansible_run_tags) | ternary('present', 'absent') }}" # Check ansible tag to determine action - name: Print remote state configuration - when: auto_terraform_state_action == "create" + when: "'teardown' not in ansible_run_tags" ansible.builtin.debug: msg: - "Resources for remote_s3 Terraform State created." diff --git a/roles/cloudera_deploy/tasks/init.yml b/roles/cloudera_deploy/tasks/init.yml index 970fca4..65670bc 100644 --- a/roles/cloudera_deploy/tasks/init.yml +++ b/roles/cloudera_deploy/tasks/init.yml @@ -469,8 +469,6 @@ ansible.builtin.include_role: name: cloudera_deploy tasks_from: auto_terraform_state - vars: - auto_terraform_state_action: create - name: Prepare for Download Mirror Population if requested and necessary when: From 7b59184b1930809eccf7ff4637fccb7faa413aa0 Mon Sep 17 00:00:00 2001 From: Jim Enright Date: Mon, 13 Dec 2021 19:13:42 +0000 Subject: [PATCH 5/5] Consolidate Teardown tasks in main.yml into a single play Signed-off-by: Jim Enright --- main.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/main.yml b/main.yml index a7bd17d..cc286e9 100644 --- a/main.yml +++ b/main.yml @@ -28,7 +28,7 @@ - name: Import the Cloud Interactions Playbook import_playbook: cloud.yml -- name: Teardown Dynamic Inventory when requested +- name: Teardown Cleanup hosts: localhost tags: [teardown,never] gather_facts: yes @@ -38,13 +38,7 @@ name: cloudera_deploy tasks_from: clean_dynamic_inventory -# Teardown Terraform remote state resources if requested -- name: Teardown Terraform remote state resources - hosts: localhost - tags: [teardown,never] - gather_facts: yes - tasks: - - name: Remove remote state resources if requested + - name: Remove Terraform remote state resources if requested when: - globals.infra_deployment_engine == 'terraform' - globals.terraform_auto_remote_state | bool