diff --git a/lib/ansible/modules/cloud/misc/terraform.py b/lib/ansible/modules/cloud/misc/terraform.py index bf10b84282..5c1e8e1d14 100644 --- a/lib/ansible/modules/cloud/misc/terraform.py +++ b/lib/ansible/modules/cloud/misc/terraform.py @@ -38,6 +38,21 @@ options: - The path to the root of the Terraform directory with the vars.tf/main.tf/etc to use. required: true + workspace: + description: + - The terraform workspace to work with. + required: false + default: default + version_added: 2.7 + purge_workspace: + description: + - Only works with state = absent + - If true, the workspace will be deleted after the "terraform destroy" action. + - The 'default' workspace will not be deleted. + required: false + default: false + type: bool + version_added: 2.7 plan_file: description: - The path to an existing Terraform plan file to apply. If this is not @@ -166,6 +181,43 @@ def init_plugins(bin_path, project_path): module.fail_json(msg="Failed to initialize Terraform modules:\r\n{0}".format(err)) +def get_workspace_context(bin_path, project_path): + workspace_ctx = {"current": "default", "all": []} + command = [bin_path, 'workspace', 'list'] + rc, out, err = module.run_command(command, cwd=project_path) + if rc != 0: + module.fail_json(msg="Failed to list Terraform workspaces:\r\n{0}".format(err)) + for item in out.split('\n'): + stripped_item = item.strip() + if not stripped_item: + continue + elif stripped_item.startswith('* '): + workspace_ctx["current"] = stripped_item.replace('* ', '') + else: + workspace_ctx["all"].append(stripped_item) + return workspace_ctx + + +def _workspace_cmd(bin_path, project_path, action, workspace): + command = [bin_path, 'workspace', action, workspace] + rc, out, err = module.run_command(command, cwd=project_path) + if rc != 0: + module.fail_json(msg="Failed to {0} workspace:\r\n{1}".format(action, err)) + return rc, out, err + + +def create_workspace(bin_path, project_path, workspace): + _workspace_cmd(bin_path, project_path, 'new', workspace) + + +def select_workspace(bin_path, project_path, workspace): + _workspace_cmd(bin_path, project_path, 'select', workspace) + + +def remove_workspace(bin_path, project_path, workspace): + _workspace_cmd(bin_path, project_path, 'delete', workspace) + + def build_plan(bin_path, project_path, variables_args, state_file, targets, plan_path=None): if plan_path is None: f, plan_path = tempfile.mkstemp(suffix='.tfplan') @@ -198,6 +250,8 @@ def main(): argument_spec=dict( project_path=dict(required=True, type='path'), binary_path=dict(type='path'), + workspace=dict(required=False, type='str', default='default'), + purge_workspace=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent', 'planned']), variables=dict(type='dict'), variables_file=dict(type='path'), @@ -206,7 +260,7 @@ def main(): targets=dict(type='list', default=[]), lock=dict(type='bool', default=True), lock_timeout=dict(type='int',), - force_init=dict(type='bool', default=False) + force_init=dict(type='bool', default=False), ), required_if=[('state', 'planned', ['plan_file'])], supports_check_mode=True, @@ -214,6 +268,8 @@ def main(): project_path = module.params.get('project_path') bin_path = module.params.get('binary_path') + workspace = module.params.get('workspace') + purge_workspace = module.params.get('purge_workspace') state = module.params.get('state') variables = module.params.get('variables') or {} variables_file = module.params.get('variables_file') @@ -229,6 +285,13 @@ def main(): if force_init: init_plugins(command[0], project_path) + workspace_ctx = get_workspace_context(command[0], project_path) + if workspace_ctx["current"] != workspace: + if workspace not in workspace_ctx["all"]: + create_workspace(command[0], project_path, workspace) + else: + select_workspace(command[0], project_path, workspace) + variables_args = [] for k, v in variables.items(): variables_args.extend([ @@ -300,7 +363,13 @@ def main(): else: outputs = json.loads(outputs_text) - module.exit_json(changed=changed, state=state, outputs=outputs, stdout=out, stderr=err, command=' '.join(command)) + # Restore the Terraform workspace found when running the module + if workspace_ctx["current"] != workspace: + select_workspace(command[0], project_path, workspace_ctx["current"]) + if state == 'absent' and workspace != 'default' and purge_workspace is True: + remove_workspace(command[0], project_path, workspace) + + module.exit_json(changed=changed, state=state, workspace=workspace, outputs=outputs, stdout=out, stderr=err, command=' '.join(command)) if __name__ == '__main__':