Skip to content

Commit

Permalink
replace jsonpath for jq (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
marshallford authored Aug 11, 2024
1 parent 3769cf0 commit 6ab7f13
Show file tree
Hide file tree
Showing 25 changed files with 236 additions and 169 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ Run Ansible playbooks using Terraform.
```terraform
resource "ansible_navigator_run" "webservers_example" {
playbook = <<-EOT
- hosts: webservers
- name: Example
hosts: webservers
tasks:
- ansible.builtin.package:
- name: Install nginx
ansible.builtin.package:
name: nginx
EOT
inventory = yamlencode({
Expand All @@ -22,14 +24,32 @@ resource "ansible_navigator_run" "webservers_example" {
}
})
}
data "ansible_navigator_run" "uptime_example" {
playbook = <<-EOT
- name: Example
hosts: all
EOT
inventory = yamlencode({})
artifact_queries = {
"uptimes" = {
jq_filter = "[.plays[] | select(.name==\"Example\") | .tasks[] | select(.task==\"Gathering Facts\") | {host: .host, uptime_seconds: .res.ansible_facts.ansible_uptime_seconds }]"
}
}
}
output "uptimes" {
value = jsondecode(data.ansible_navigator_run.uptime_example.artifact_queries.uptimes.results[0])
}
```

## Features

1. Run Ansible playbooks against Terraform managed infrastructure (without the `local-exec` provisioner). Eliminates the need for additional scripting or pipeline steps.
2. Construct Ansible inventories using other data sources and resources. Set Ansible host and group variables to values and secrets from other providers.
3. Utilize Ansible [execution environments](https://ansible.readthedocs.io/en/latest/getting_started_ee/index.html) (containers images) to customize and run the Ansible software stack. Isolate Ansible and its related dependencies (Python/System packages, collections, etc) to simplify pipeline and workstation setup.
4. Write JSONPath queries against [playbook artifacts](https://access.redhat.com/documentation/en-us/red_hat_ansible_automation_platform/2.0-ea/html/ansible_navigator_creator_guide/assembly-troubleshooting-navigator_ansible-navigator#proc-review-artifact_troubleshooting-navigator). Extract values from the playbook run for use elsewhere in the Terraform configuration. Examples include: Ansible facts, remote file contents, task results -- the possibilities are endless!
4. Write [`jq`](https://jqlang.github.io/jq/) queries against [playbook artifacts](https://access.redhat.com/documentation/en-us/red_hat_ansible_automation_platform/2.0-ea/html/ansible_navigator_creator_guide/assembly-troubleshooting-navigator_ansible-navigator#proc-review-artifact_troubleshooting-navigator). Extract values from the playbook run for use elsewhere in the Terraform configuration. Examples include: Ansible facts, remote file contents, task results -- the possibilities are endless!
5. Control playbook re-run behavior using several "lifecycle" options, including an attribute for running the playbook on resource destruction. Implement conditional plays/tasks with the environment variable `ANSIBLE_TF_OPERATION`.
6. Connect to hosts securely by specifying SSH private keys and known host entries. No need manage `~/.ssh` files or setup `ssh-agent` in the environment which Terraform runs.

Expand Down
16 changes: 8 additions & 8 deletions docs/data-sources/navigator_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,23 @@ data "ansible_navigator_run" "inline" {
# 2. artifact queries -- get file contents
data "ansible_navigator_run" "artifact_query_file" {
playbook = <<-EOT
- name: Get file
- name: Example
hosts: all
tasks:
- name: resolv.conf
- name: Get file
ansible.builtin.slurp:
src: /etc/resolv.conf
EOT
inventory = "..."
inventory = yamlencode({})
artifact_queries = {
"resolv_conf" = {
jsonpath = "$.plays[?(@.__play_name=='Get file')].tasks[?(@.__task=='resolv.conf')].res.content"
jq_filter = ".plays[] | select(.name==\"Example\") | .tasks[] | select(.task==\"Get file\") | .res.content"
}
}
}
output "resolv_conf" {
value = base64decode(data.ansible_navigator_run.artifact_query_file.artifact_queries.resolv_conf.result)
value = base64decode(jsondecode(data.ansible_navigator_run.artifact_query_file.artifact_queries.resolv_conf.results[0]))
}
```

Expand All @@ -71,7 +71,7 @@ output "resolv_conf" {

- `ansible_navigator_binary` (String) Path to the `ansible-navigator` binary. By default `$PATH` is searched.
- `ansible_options` (Attributes) Ansible [playbook](https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html) run related configuration. (see [below for nested schema](#nestedatt--ansible_options))
- `artifact_queries` (Attributes Map) Query the playbook artifact with [JSONPath](https://goessner.net/articles/JsonPath/). The [playbook artifact](https://access.redhat.com/documentation/en-us/red_hat_ansible_automation_platform/2.0-ea/html/ansible_navigator_creator_guide/assembly-troubleshooting-navigator_ansible-navigator#proc-review-artifact_troubleshooting-navigator) contains detailed information about every play and task, as well as the stdout from the playbook run. (see [below for nested schema](#nestedatt--artifact_queries))
- `artifact_queries` (Attributes Map) Query the Ansible playbook artifact with [`jq`](https://jqlang.github.io/jq/) syntax. The [playbook artifact](https://access.redhat.com/documentation/en-us/red_hat_ansible_automation_platform/2.0-ea/html/ansible_navigator_creator_guide/assembly-troubleshooting-navigator_ansible-navigator#proc-review-artifact_troubleshooting-navigator) contains detailed information about every play and task, as well as the stdout from the playbook run. (see [below for nested schema](#nestedatt--artifact_queries))
- `execution_environment` (Attributes) [Execution environment](https://ansible.readthedocs.io/en/latest/getting_started_ee/index.html) (EE) related configuration. (see [below for nested schema](#nestedatt--execution_environment))
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
- `timezone` (String) IANA time zone, use `local` for the system time zone. Defaults to `UTC`.
Expand Down Expand Up @@ -110,11 +110,11 @@ Required:

Required:

- `jsonpath` (String) JSONPath expression.
- `jq_filter` (String) `jq` filter. Example: `.status, .stdout`.

Read-Only:

- `result` (String) Result of the query. Result may be empty if a field or map key cannot be located.
- `results` (List of String) Results of the `jq` filter in JSON format.


<a id="nestedatt--execution_environment"></a>
Expand Down
36 changes: 18 additions & 18 deletions docs/resources/navigator_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ resource "ansible_navigator_run" "existing" {
# 3. configure ansible with ansible.cfg placed in working directory (see example below)
resource "ansible_navigator_run" "working_directory" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
working_directory = "some-directory-with-ansible-cfg-file"
}
Expand All @@ -54,7 +54,7 @@ resource "ansible_navigator_run" "environment_variables" {
- "{{ lookup('ansible.builtin.env', 'SOME_VAR') }}"
- "{{ lookup('ansible.builtin.env', 'EDITOR') }}"
EOT
inventory = "..."
inventory = yamlencode({})
execution_environment = {
environment_variables_set = {
"SOME_VAR" = "some-value"
Expand All @@ -67,8 +67,8 @@ resource "ansible_navigator_run" "environment_variables" {
# 5. ansible playbook options
resource "ansible_navigator_run" "ansible_options" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
ansible_options = {
force_handlers = true # --force-handlers
skip_tags = ["tag1", "tag2"] # --skip-tags tag1,tag2
Expand All @@ -89,14 +89,14 @@ resource "ansible_navigator_run" "destroy" {
msg: "resource is being destroyed!"
when: destroy
EOT
inventory = "..."
inventory = yamlencode({})
run_on_destroy = true
}
# 7. triggers and replacement triggers
resource "ansible_navigator_run" "triggers" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
triggers = {
somekey = some_resource.example.id # re-run playbook when id changes
}
Expand All @@ -107,17 +107,17 @@ resource "ansible_navigator_run" "triggers" {
# 8. artifact queries -- get playbook stdout
resource "ansible_navigator_run" "artifact_query_stdout" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
artifact_queries = {
"stdout" = {
jsonpath = "$.stdout"
jq_filter = ".stdout"
}
}
}
output "playbook_stdout" {
value = join("\n", jsondecode(ansible_navigator_run.artifact_query_stdout.artifact_queries.stdout.result))
value = join("\n", jsondecode(ansible_navigator_run.artifact_query_stdout.artifact_queries.stdout.results[0]))
}
# 9. ssh private keys
Expand All @@ -126,8 +126,8 @@ resource "tls_private_key" "client" {
}
resource "ansible_navigator_run" "private_keys" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
ansible_options = {
private_keys = [
{
Expand All @@ -144,7 +144,7 @@ resource "tls_private_key" "server" {
}
resource "ansible_navigator_run" "known_hosts" {
playbook = "..."
playbook = "# example"
inventory = yamlencode({
all = {
vars = {
Expand Down Expand Up @@ -195,7 +195,7 @@ pipelining=True

- `ansible_navigator_binary` (String) Path to the `ansible-navigator` binary. By default `$PATH` is searched.
- `ansible_options` (Attributes) Ansible [playbook](https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html) run related configuration. (see [below for nested schema](#nestedatt--ansible_options))
- `artifact_queries` (Attributes Map) Query the playbook artifact with [JSONPath](https://goessner.net/articles/JsonPath/). The [playbook artifact](https://access.redhat.com/documentation/en-us/red_hat_ansible_automation_platform/2.0-ea/html/ansible_navigator_creator_guide/assembly-troubleshooting-navigator_ansible-navigator#proc-review-artifact_troubleshooting-navigator) contains detailed information about every play and task, as well as the stdout from the playbook run. (see [below for nested schema](#nestedatt--artifact_queries))
- `artifact_queries` (Attributes Map) Query the Ansible playbook artifact with [`jq`](https://jqlang.github.io/jq/) syntax. The [playbook artifact](https://access.redhat.com/documentation/en-us/red_hat_ansible_automation_platform/2.0-ea/html/ansible_navigator_creator_guide/assembly-troubleshooting-navigator_ansible-navigator#proc-review-artifact_troubleshooting-navigator) contains detailed information about every play and task, as well as the stdout from the playbook run. (see [below for nested schema](#nestedatt--artifact_queries))
- `execution_environment` (Attributes) [Execution environment](https://ansible.readthedocs.io/en/latest/getting_started_ee/index.html) (EE) related configuration. (see [below for nested schema](#nestedatt--execution_environment))
- `replacement_triggers` (Map of String) Arbitrary map of values that, when changed, will recreate the resource. Similar to `triggers`, but will cause `id` to change. Useful when combined with `run_on_destroy`.
- `run_on_destroy` (Boolean) Run playbook on destroy. The environment variable `ANSIBLE_TF_OPERATION` is set to `delete` during the run to allow for conditional plays, tasks, etc. Defaults to `false`.
Expand Down Expand Up @@ -237,11 +237,11 @@ Required:

Required:

- `jsonpath` (String) JSONPath expression.
- `jq_filter` (String) `jq` filter. Example: `.status, .stdout`.

Read-Only:

- `result` (String) Result of the query. Result may be empty if a field or map key cannot be located.
- `results` (List of String) Results of the `jq` filter in JSON format.


<a id="nestedatt--execution_environment"></a>
Expand Down
4 changes: 2 additions & 2 deletions examples/complete/aws/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ resource "ansible_navigator_run" "this" {
}
artifact_queries = {
"stdout" = {
jsonpath = "$.stdout"
jq_filter = ".stdout"
}
}
depends_on = [aws_iam_user_policy.ssh_ssm]
}

output "playbook_stdout" {
value = join("\n", jsondecode(ansible_navigator_run.this.artifact_queries.stdout.result))
value = join("\n", jsondecode(ansible_navigator_run.this.artifact_queries.stdout.results[0]))
}
4 changes: 2 additions & 2 deletions examples/complete/libvirt/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ resource "ansible_navigator_run" "this" {
}
artifact_queries = {
"stdout" = {
jsonpath = "$.stdout"
jq_filter = ".stdout"
}
}
}

output "playbook_stdout" {
value = join("\n", jsondecode(ansible_navigator_run.this.artifact_queries.stdout.result))
value = join("\n", jsondecode(ansible_navigator_run.this.artifact_queries.stdout.result[0]))
}
10 changes: 5 additions & 5 deletions examples/data-sources/ansible_navigator_run/data-source.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ data "ansible_navigator_run" "inline" {
# 2. artifact queries -- get file contents
data "ansible_navigator_run" "artifact_query_file" {
playbook = <<-EOT
- name: Get file
- name: Example
hosts: all
tasks:
- name: resolv.conf
- name: Get file
ansible.builtin.slurp:
src: /etc/resolv.conf
EOT
inventory = "..."
inventory = yamlencode({})
artifact_queries = {
"resolv_conf" = {
jsonpath = "$.plays[?(@.__play_name=='Get file')].tasks[?(@.__task=='resolv.conf')].res.content"
jq_filter = ".plays[] | select(.name==\"Example\") | .tasks[] | select(.task==\"Get file\") | .res.content"
}
}
}

output "resolv_conf" {
value = base64decode(data.ansible_navigator_run.artifact_query_file.artifact_queries.resolv_conf.result)
value = base64decode(jsondecode(data.ansible_navigator_run.artifact_query_file.artifact_queries.resolv_conf.results[0]))
}
30 changes: 15 additions & 15 deletions examples/resources/ansible_navigator_run/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ resource "ansible_navigator_run" "existing" {

# 3. configure ansible with ansible.cfg placed in working directory (see example below)
resource "ansible_navigator_run" "working_directory" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
working_directory = "some-directory-with-ansible-cfg-file"
}

Expand All @@ -39,7 +39,7 @@ resource "ansible_navigator_run" "environment_variables" {
- "{{ lookup('ansible.builtin.env', 'SOME_VAR') }}"
- "{{ lookup('ansible.builtin.env', 'EDITOR') }}"
EOT
inventory = "..."
inventory = yamlencode({})
execution_environment = {
environment_variables_set = {
"SOME_VAR" = "some-value"
Expand All @@ -52,8 +52,8 @@ resource "ansible_navigator_run" "environment_variables" {

# 5. ansible playbook options
resource "ansible_navigator_run" "ansible_options" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
ansible_options = {
force_handlers = true # --force-handlers
skip_tags = ["tag1", "tag2"] # --skip-tags tag1,tag2
Expand All @@ -74,14 +74,14 @@ resource "ansible_navigator_run" "destroy" {
msg: "resource is being destroyed!"
when: destroy
EOT
inventory = "..."
inventory = yamlencode({})
run_on_destroy = true
}

# 7. triggers and replacement triggers
resource "ansible_navigator_run" "triggers" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
triggers = {
somekey = some_resource.example.id # re-run playbook when id changes
}
Expand All @@ -92,17 +92,17 @@ resource "ansible_navigator_run" "triggers" {

# 8. artifact queries -- get playbook stdout
resource "ansible_navigator_run" "artifact_query_stdout" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
artifact_queries = {
"stdout" = {
jsonpath = "$.stdout"
jq_filter = ".stdout"
}
}
}

output "playbook_stdout" {
value = join("\n", jsondecode(ansible_navigator_run.artifact_query_stdout.artifact_queries.stdout.result))
value = join("\n", jsondecode(ansible_navigator_run.artifact_query_stdout.artifact_queries.stdout.results[0]))
}

# 9. ssh private keys
Expand All @@ -111,8 +111,8 @@ resource "tls_private_key" "client" {
}

resource "ansible_navigator_run" "private_keys" {
playbook = "..."
inventory = "..."
playbook = "# example"
inventory = yamlencode({})
ansible_options = {
private_keys = [
{
Expand All @@ -129,7 +129,7 @@ resource "tls_private_key" "server" {
}

resource "ansible_navigator_run" "known_hosts" {
playbook = "..."
playbook = "# example"
inventory = yamlencode({
all = {
vars = {
Expand Down
Loading

0 comments on commit 6ab7f13

Please sign in to comment.