{"id":16374,"date":"2021-05-31T07:29:07","date_gmt":"2021-05-31T05:29:07","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/"},"modified":"2024-09-11T15:06:49","modified_gmt":"2024-09-11T13:06:49","slug":"use-ansible-like-a-programming-language","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/","title":{"rendered":"Use Ansible like a Programming Language"},"content":{"rendered":"<p>I did not intend to create a new blog on Ansible so fast, but here it comes.<br \/>\n<a href=\"https:\/\/www.dbi-services.com\/blog\/starting-with-simple-ansible-playbooks\/\">My previous blog<\/a> on that topic was a first step in Ansible world. Now, it is time to use some of the advanced features.<!--more--><\/p>\n<h3>Context<\/h3>\n<p>We recently had an issue at a customer site, where some of the servers were having time synchronization issues. I discovered that ntp configuration was not the same across all servers which, from my experience. can lead to time drifts. For most of the web application, time is not so important constraint, unless your application used SAML2 Single Sign On mechanism.<\/p>\n<p>My goal, here, is to check ntp status on all servers in one shot.<\/p>\n<h3>First playbook<\/h3>\n<p>My first attempt was a very basic playbook:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">---\n- name: NTP Check\n  hosts: all\n  gather_facts: no\n  tasks:\n    - name: Run ntpdc -p\n      shell: \/usr\/sbin\/ntpdc -p\n      register: output\n    - name: Display output\n      debug:\n        var: output.stdout_lines\n<\/pre>\n<p>I use a variable named output (which is created with <strong>register <\/strong>option) and display content for each servers of the inventory (<strong>output.stdout_lines<\/strong>).<\/p>\n<p>I faced an issue on two hosts of my list:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">ok: [server-123456] =&gt; {\n\"output.stdout_lines\": \"VARIABLE IS NOT DEFINED!\"\n}<\/pre>\n<p>This was due a difference in ntp client. These two hosts run chrony instead of ntpd, which means I have to run different commands depending on the time synchronization client.<\/p>\n<h3>Second attempt<\/h3>\n<p>I took the easiest way: If \/etc\/ntp.conf exists, I run ntpdc command. If not, I used the chronyc command. And, finally, display merged result whatever the ntp client.<\/p>\n<p>Playbook becomes:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">---\n- name: NTP Check\n  hosts: all\n  gather_facts: no\n  tasks:\n    - name: Determine ntp client\n      stat:\n      path: \/etc\/ntp.conf\n      register: ntpd\n    - name: Run ntpdc -p\n      shell: \/usr\/sbin\/ntpdc -p\n      register: output\n      when: ntpd.stat.exists\n    - name: Run chronyc\n      shell: \/usr\/bin\/chronyc sources\n      register: output\n      when: not ntpd.stat.exists\n    - name: Display output\n      debug:\n        var: output.stdout_lines<\/pre>\n<p>From this playbook, I learn that even if &#8220;when&#8221; condition is not met, and task will be skipped, output register will be erased for these hosts (and &#8220;VARIABLE IS NOT DEFINED!&#8221; will be displayed). Which means that output will be populated for my two hosts that use chrony client. Unfortunately, as per <a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/playbooks_variables.html\">Ansible documentation<\/a>, this is the expected behavior:<\/p>\n<blockquote><p>&#8220;If a task fails or is skipped, Ansible still registers a variable with a failure or skipped status, unless the task is skipped based on tags. See Tags for information on adding and using tags.&#8221;<\/p><\/blockquote>\n<p>tags will not help me here as I want to have all results in same output and in one command.<\/p>\n<h3>Third try<\/h3>\n<p>As Ansible can manage errors via exception, I wondered if it would be possible to simply run chrony command if ntpdc command fails.<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: [6,11,17]\">---\n- name: NTP Check\n  hosts: all\n  gather_facts: no\n  tasks:\n    - name: Run ntpdc -p (block)\n      block:\n        - name: Run ntpdc -p\n          command: \/usr\/sbin\/ntpdc -p\n          register: output\n      rescue:\n        - name: Run chronyc (block)\n          block:\n          - name: Run chronyc\n            command: \/usr\/bin\/chronyc sources\n            register: output\n      always:\n        - name: Display output\n          debug:\n            var: output.stdout_lines\n<\/pre>\n<p>If first task block &#8220;Run ntpdc&#8221; fails, it will jump to <strong>rescue<\/strong> block and run chronyc block. Then, whatever ntpdc or chronyc block was run, <strong>always<\/strong> will be run with output always defined.<br \/>\nAll servers will have similar output as below:<\/p>\n<ul>\n<li>For a ntp client:<\/li>\n<\/ul>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">ok: [server-123456] =&gt; {\n    \"output.stdout_lines\": [\n        \" remote local st poll reach delay offset disp\",\n        \"=======================================================================\",\n        \"*ntp01 10.10.10.10 1 512 377 0.00047 -0.730333 0.11720\",\n        \"=ntp02 10.10.10.11 1 512 377 0.00087 -0.733377 0.11292\"\n    ]\n}\n<\/pre>\n<ul>\n<li>For a chrony client:<\/li>\n<\/ul>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">ok: [server-456789] =&gt; {\n    \"output.stdout_lines\": [\n    \"210 Number of sources = 5\",\n    \"MS Name\/IP address Stratum Poll Reach LastRx Last sample \",\n    \"===============================================================================\",\n    \"^* ntp04 1 6 377 38 -14us[ -46us] +\/- 233us\",\n    \"^- ntp05 3 10 377 388 +7566us[+7585us] +\/- 116ms\",\n    \"^? ntp06 2 6 377 43 +2947ms[+2947ms] +\/- 11.0s\",\n    \"^- ntp07 3 10 377 191 -8696us[-8716us] +\/- 120ms\",\n    \"^? ntp08 2 10 377 999 +2953ms[+2953ms] +\/- 11.0s\"\n    ]\n}\n<\/pre>\n<p>Note that for these examples, servers were using different ntp client but also different ntp sources which is what I wanted to check.<\/p>\n<h3>What next ?<\/h3>\n<p>These are still basic playbooks, but I wrote them in few minutes and they fill a need. It is from needs that come ideas.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I did not intend to create a new blog on Ansible so fast, but here it comes. My previous blog on that topic was a first step in Ansible world. Now, it is time to use some of the advanced features.<\/p>\n","protected":false},"author":109,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1321,1320],"tags":[150],"type_dbi":[],"class_list":["post-16374","post","type-post","status-publish","format-standard","hentry","category-ansible","category-devops","tag-ansible"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.2) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Use Ansible like a Programming Language - dbi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Use Ansible like a Programming Language\" \/>\n<meta property=\"og:description\" content=\"I did not intend to create a new blog on Ansible so fast, but here it comes. My previous blog on that topic was a first step in Ansible world. Now, it is time to use some of the advanced features.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-05-31T05:29:07+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-09-11T13:06:49+00:00\" \/>\n<meta name=\"author\" content=\"DevOps\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"DevOps\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/\"},\"author\":{\"name\":\"DevOps\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4cd1b5f8a3de93f05a16ab8d7d2b7735\"},\"headline\":\"Use Ansible like a Programming Language\",\"datePublished\":\"2021-05-31T05:29:07+00:00\",\"dateModified\":\"2024-09-11T13:06:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/\"},\"wordCount\":468,\"commentCount\":0,\"keywords\":[\"Ansible\"],\"articleSection\":[\"Ansible\",\"DevOps\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/\",\"name\":\"Use Ansible like a Programming Language - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\"},\"datePublished\":\"2021-05-31T05:29:07+00:00\",\"dateModified\":\"2024-09-11T13:06:49+00:00\",\"author\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4cd1b5f8a3de93f05a16ab8d7d2b7735\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.dbi-services.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Use Ansible like a Programming Language\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4cd1b5f8a3de93f05a16ab8d7d2b7735\",\"name\":\"DevOps\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/cdd2dd7441774355062c0f0f68612296b059cd1e2ff6c7af0b15dba0ed64a85f?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/cdd2dd7441774355062c0f0f68612296b059cd1e2ff6c7af0b15dba0ed64a85f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/cdd2dd7441774355062c0f0f68612296b059cd1e2ff6c7af0b15dba0ed64a85f?s=96&d=mm&r=g\",\"caption\":\"DevOps\"},\"url\":\"https:\/\/www.dbi-services.com\/blog\/author\/devops\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Use Ansible like a Programming Language - dbi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/","og_locale":"en_US","og_type":"article","og_title":"Use Ansible like a Programming Language","og_description":"I did not intend to create a new blog on Ansible so fast, but here it comes. My previous blog on that topic was a first step in Ansible world. Now, it is time to use some of the advanced features.","og_url":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/","og_site_name":"dbi Blog","article_published_time":"2021-05-31T05:29:07+00:00","article_modified_time":"2024-09-11T13:06:49+00:00","author":"DevOps","twitter_card":"summary_large_image","twitter_misc":{"Written by":"DevOps","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/"},"author":{"name":"DevOps","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4cd1b5f8a3de93f05a16ab8d7d2b7735"},"headline":"Use Ansible like a Programming Language","datePublished":"2021-05-31T05:29:07+00:00","dateModified":"2024-09-11T13:06:49+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/"},"wordCount":468,"commentCount":0,"keywords":["Ansible"],"articleSection":["Ansible","DevOps"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/","url":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/","name":"Use Ansible like a Programming Language - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2021-05-31T05:29:07+00:00","dateModified":"2024-09-11T13:06:49+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4cd1b5f8a3de93f05a16ab8d7d2b7735"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/use-ansible-like-a-programming-language\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Use Ansible like a Programming Language"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/4cd1b5f8a3de93f05a16ab8d7d2b7735","name":"DevOps","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/cdd2dd7441774355062c0f0f68612296b059cd1e2ff6c7af0b15dba0ed64a85f?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/cdd2dd7441774355062c0f0f68612296b059cd1e2ff6c7af0b15dba0ed64a85f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/cdd2dd7441774355062c0f0f68612296b059cd1e2ff6c7af0b15dba0ed64a85f?s=96&d=mm&r=g","caption":"DevOps"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/devops\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/16374","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/109"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=16374"}],"version-history":[{"count":1,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/16374\/revisions"}],"predecessor-version":[{"id":34729,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/16374\/revisions\/34729"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=16374"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=16374"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=16374"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=16374"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}