{"id":198,"date":"2016-08-02T20:20:20","date_gmt":"2016-08-02T20:20:20","guid":{"rendered":"http:\/\/svops.com\/blog\/?p=198"},"modified":"2016-08-02T20:20:20","modified_gmt":"2016-08-02T20:20:20","slug":"managing-elastic-watches-with-ansible","status":"publish","type":"post","link":"http:\/\/svops.com\/blog\/managing-elastic-watches-with-ansible\/","title":{"rendered":"Managing Elastic watches with Ansible"},"content":{"rendered":"<p>If you&#8217;re an Ansible convert, you want every part of your deployment to be managed by the tasks in your roles. \u00a0I recently wanted to manage my Elastic Watcher config with Ansible. \u00a0It was enough of a struggle (&#8220;learning process&#8221;) that I wanted to document it here.<\/p>\n<p>While I was developing my first few watches, I made stand-alone shell scripts that could be run to create\/update them. \u00a0This worked fine, and even handled the basic auth configuration in front of the master\u00a0nodes. \u00a0Basically it was:<\/p>\n<pre>curl -u username\u00a0-XPUT 'http:\/\/hostname\/_watcher\/watch\/watchname' -d '{ ... }'<\/pre>\n<p>It would prompt for the user&#8217;s password and send the information along.<\/p>\n<p>Ansible can run local commands, but doesn&#8217;t like interactive commands. \u00a0As such, the password prompt would block the task from completing. \u00a0Looking around for other solutions\u00a0led me to the &#8216;uri&#8217; module. \u00a0By default, it won&#8217;t prompt for the password either, but you can use the <a href=\"http:\/\/docs.ansible.com\/ansible\/playbooks_prompts.html\">prompts system<\/a>. \u00a0I chose to use the Ansible vault to store the password to avoid external sharing\/management of the password.<\/p>\n<p>The final config looks like this:<\/p>\n<pre>- set_fact:\r\n watch: \"{{ lookup('file','watcher.json') }}\"\r\n\r\n- name: Install watcher for cluster health\r\n uri:\r\n delegate_to: 127.0.0.1\r\n url: \"http:\/\/hostname\/\/_watcher\/watch\/watchname\"\r\n method: PUT\r\n user: \"ansible\"\r\n password: \"{{ ansible_password }}\"\r\n force_basic_auth: yes\r\n body: \"{{ watch }}\"\r\n run_once: true<\/pre>\n<p>The set_fact loads the contents of the file into a variable (otherwise, you&#8217;d have to list the entire json in uri&#8217;s <em>body<\/em> section).<\/p>\n<p>By using delegate_to, the PUT will be run on the local machine. \u00a0Coupled with run_once, it contacts the cluster and sets the config one time.<\/p>\n<p>On my desktop mac, it originally gave me errors about\u00a0httplib2, which I had to install:<\/p>\n<pre>sudo pip install httplib2<\/pre>\n<p>As mentioned before, the variable that contains the basic auth password comes from an ansible vault.<\/p>\n<p>One final &#8211; yet important &#8211; gotcha. \u00a0Ansible seemingly wants to process all data as templates. \u00a0Unfortunately, watcher uses the same variable syntax as Ansible, so your watcher definition may include lines with watcher variables:<\/p>\n<pre>{{ ctx.payload.status }}<\/pre>\n<p>Ansible will think these\u00a0are its own variables, and you&#8217;ll get errors when running the playbook:<\/p>\n<pre>fatal: [localhost] =&gt; One or more undefined variables: 'ctx' is undefined<\/pre>\n<p>The solution is to tell Ansible to ignore that section:<\/p>\n<pre>{% raw %}{{ ctx.payload.status }}{% endraw %}<\/pre>\n<p>This can be a little tedious, but I didn&#8217;t find a way to prevent\u00a0Ansible from interpreting the string.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re an Ansible convert, you want every part of your deployment to be managed by the tasks in your roles. \u00a0I recently wanted to manage my Elastic Watcher config with Ansible. \u00a0It was enough of a struggle (&#8220;learning process&#8221;) &hellip; <a href=\"http:\/\/svops.com\/blog\/managing-elastic-watches-with-ansible\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[17,16],"tags":[],"_links":{"self":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts\/198"}],"collection":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/comments?post=198"}],"version-history":[{"count":1,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts\/198\/revisions"}],"predecessor-version":[{"id":199,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/posts\/198\/revisions\/199"}],"wp:attachment":[{"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/media?parent=198"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/categories?post=198"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/svops.com\/blog\/wp-json\/wp\/v2\/tags?post=198"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}