{"id":581,"date":"2022-01-14T11:24:04","date_gmt":"2022-01-14T10:24:04","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/2022\/01\/14\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/"},"modified":"2022-04-06T08:26:38","modified_gmt":"2022-04-06T06:26:38","slug":"automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/","title":{"rendered":"Automating Linux patching with Ansible &#8211; How to keep all your environments consistent ?"},"content":{"rendered":"<p>Your may want to patch your Linux servers on a regular basis (e.g using &#8220;yum\/dnf update&#8221;). As always, it&#8217;s obviously recommended to :<\/p>\n<p>1) Patch the TEST systems<br \/>\n2) Check if there is no side effects<br \/>\n3) Wait few days or weeks<br \/>\n4) Patch the PROD systems<\/p>\n<p>The problem here is that between step 1 and 4 above, a new version of the packages can be available on the public repository you use.<br \/>\nConsequently after step 4, you&#8217;ll have a more recent version of you packages on PROD than on TEST, which is a situation you probably want to avoid.<\/p>\n<p><a href=\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible\/\" target=\"_blank\" rel=\"noopener\">In my previous post<\/a>, I explained how to update Linux OS with Ansible by patching all packages or installing security fixes only.<br \/>\nIn this one, I&#8217;ll show you how to keep your different environments consistent in term of packages version, even when the patching is delayed by several weeks.<\/p>\n<h3>Inventory<\/h3>\n<p>We will use the following simple inventory for this demo :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">[test]\n54.93.103.197   # srvtest1\n35.156.114.202  # srvtest2\n\n[prod]\n54.93.245.3     # srvprod1\n3.67.8.87       # srvprod1<\/pre>\n<p>The VMs are running in AWS and are deployed with <a href=\"https:\/\/www.terraform.io\/\" target=\"_blank\" rel=\"noopener\">Terraform<\/a> (have a look to <a href=\"https:\/\/www.dbi-services.com\/blog\/aws-ec2-instance-deployment-with-terraform\/\" target=\"_blank\" rel=\"noopener\">my other post<\/a> if you want to know how to do it).<br \/>\nBoth host groups have a variable &#8220;gv_env&#8221; defined :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">$ tree inventories\/group_vars\/\ninventories\/group_vars\/\n\u251c\u2500\u2500 prod.yml\n\u2514\u2500\u2500 test.yml\n\n0 directories, 2 files<\/pre>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">$ cat inventories\/group_vars\/*\ngv_env: prod\ngv_env: test<\/pre>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">$ ansible-inventory -i inventories\/hosts --graph --vars\n@all:\n  |--@prod:\n  |  |--3.67.8.87\n  |  |  |--{gv_env = prod}\n  |  |--54.93.245.3\n  |  |  |--{gv_env = prod}\n  |  |--{gv_env = prod}\n  |--@test:\n  |  |--35.156.114.202\n  |  |  |--{gv_env = test}\n  |  |--54.93.103.197\n  |  |  |--{gv_env = test}\n  |  |--{gv_env = test}\n  |--@ungrouped:<\/pre>\n<p>&nbsp;<\/p>\n<h3>Playbook<\/h3>\n<div>The content of my playbook file is very limited. Its purpose is only to call a role :<\/div>\n<div>\n<div>\n<div>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">---\n- name: Patch TEST or PROD servers\n  hosts: all\n  gather_facts: true\n\n  roles:\n    - os_update\n...<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<h3>Role<\/h3>\n<div>The main task of my role is also very short :<\/div>\n<div>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">---\n- include_tasks: test.yml\n  when:\n    - ev_env_to_patch == \"test\"\n    - inventory_hostname in groups['test']\n\n- include_tasks: prod.yml\n  when:\n    - ev_env_to_patch == \"prod\"\n    - inventory_hostname in groups['prod']\n...<\/pre>\n<\/div>\n<div>Its aim is to call the correct tasks file depending which servers (TEST or PROD) the playbook is running for.<\/div>\n<div>The choice of the servers to patch is defined by the variable ev_env_to_patch. Nothing else in this file, as all the steps are described in the tasks files.<\/div>\n<p>&nbsp;<\/p>\n<h3>Task &#8211; test.yml<\/h3>\n<p>The first one is to list all packages that will be modified. Thus, the user can double-check what will be installed before moving on, or cancel the process if desired :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">---\n- name: Get packages that can be upgraded on TEST servers\n  become: true\n  ansible.builtin.yum:\n    list: updates\n    state: latest\n    update_cache: yes\n  register: reg_yum_output_all\n\n- name: List packages that will be upgraded on TEST servers\n  ansible.builtin.debug:\n    msg: \"{{ reg_yum_output_all.results | map(attribute='name') | list }}\"\n\n- name: Request user confirmation\n  ansible.builtin.pause:\n    prompt: |\n\n      The packages listed above will be upgraded. Do you want to continue ?\n      -&gt; Press RETURN to continue.\n      -&gt; Press Ctrl+c and then \"a\" to abort.<\/pre>\n<p>&nbsp;<\/p>\n<p>Next step is to start the update :<\/p>\n<div>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">- name: Upgrade packages on TEST servers\n  become: true\n  ansible.builtin.yum:\n    name: '*'\n    state: latest\n    update_cache: yes\n    update_only: no\n  register: reg_upgrade_ok\n  when: \n    - reg_yum_output_all is defined\n\n- name: Print errors if upgrade failed\n  ansible.builtin.debug:\n    msg: \"Packages upgrade failed\"\n  when: reg_upgrade_ok is not defined<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>If the Kernel has been updated, it&#8217;s strongly recommended to reboot the server. To check if a reboot is required, we can use the command &#8220;needs-restarting&#8221; provided here by the package dnf-utils :<\/p>\n<div>\n<div>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">- name: Install yum-utils on TEST servers\n  become: true\n  ansible.builtin.yum:\n    name: 'yum-utils'\n    state: latest\n    update_cache: yes\n\n\n- name: Check if a reboot is required\n  become: true\n  command: needs-restarting -r\n  register: reg_reboot_required\n  ignore_errors: true\n  failed_when: false\n  changed_when: reg_reboot_required.rc != 0\n  notify:\n    - Reboot server<\/pre>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<p>As you probably noticed, the actions described so far are nearly the same as in my previous post.<br \/>\nOnce the server is back the important step is to save in a text file the packages version freshly installed :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">- name: Genereate the list of the upgraded packages on TEST servers\n  become: true\n  shell: \"rpm -qa &gt; \/tmp\/packages-set.txt\"\n  args:\n    warn: false<\/pre>\n<p>&nbsp;<\/p>\n<p>You might have imported GPG public keys into your system to verify the signature of a package before installing it. If this is the case, the keys are also added to the RPM database. Therefore it&#8217;s required to remove them from the file :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">- name: Delete the fake RPM packages gpg-pubkey from the list\n  become: true\n  ansible.builtin.lineinfile:\n    path: \/tmp\/packages-set.txt\n    regexp: 'gpg-pubkey-.*'\n    state: absent<\/pre>\n<p>&nbsp;<\/p>\n<p>Once the file is ready, we fetch it from the Ansible Control Node :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">- name: Fetch the file from the Ansible Control Node\n  become: true\n  ansible.builtin.fetch: \n    src: \/tmp\/packages-set.txt\n    dest: \/tmp\/\n    flat: yes<\/pre>\n<p>&nbsp;<\/p>\n<p>Now we need a directory on the PROD servers where the file will be copied to :<\/p>\n<pre class=\"brush: actionscript3; gutter: true; first-line: 1; highlight: [7,8]\">- name: Create directory to store the file on PROD servers\n  become: true\n  ansible.builtin.file:\n    path: \/etc\/dbi\n    state: directory\n    mode: '0777'\n  delegate_to: \"{{ item }}\"\n  loop: \"{{ groups['prod'] }}\"<\/pre>\n<p>I used &#8220;delegate_to&#8221; to make this task executed by the PROD servers.<\/p>\n<p>And finally, we copy the file to the PROD servers :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: [6,7]\">- name: Copy the file from the Ansible Control node to the PROD servers\n  ansible.builtin.copy: \n    src: \/tmp\/packages-set.txt\n    dest: \/etc\/dbi\n    force: true\n  delegate_to: \"{{ item }}\"\n  loop: \"{{ groups['prod'] }}\"\n...<\/pre>\n<p>Everything is done on the TEST servers : patching, reboot, file containing packages version transferred to the PROD servers.<br \/>\nLet&#8217;s see how to patch the PROD servers&#8230;<\/p>\n<h3>Task &#8211; prod.yml<\/h3>\n<p>First of all we need to ensure that the file &#8220;packages-set.txt&#8221; is still present on the servers :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">---\n- name: Check if the packages list file (\/etc\/dbi\/packages-set.txt) is present on PROD servers\n  ansible.builtin.stat:\n    path: \/etc\/dbi\/packages-set.txt\n  register: reg_file_status<\/pre>\n<p>&nbsp;<\/p>\n<p>As the goal is to use it for the patching, we want the playbook to fail if it is not there :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">- name: Fail when file \/etc\/dbi\/packages-set.txt does not exist\n  ansible.builtin.fail: \n    msg: \/etc\/dbi\/packages-set.txt does not exist\n  when: reg_file_status.stat.exists == false<\/pre>\n<p>&nbsp;<\/p>\n<p>And now&#8230; the (easy) trick :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: [3]\">- name: Upgrade PROD servers to the same packages version as TEST servers\n  become: true\n  shell: \"cat \/etc\/dbi\/packages-set.txt | xargs yum update-to -y\"<\/pre>\n<pre class=\"brush: actionscript3; gutter: true; first-line: 1\">- name: Check if a reboot is required\n  become: true\n  command: needs-restarting -r\n  register: reg_reboot_required\n  ignore_errors: true\n  failed_when: false\n  changed_when: reg_reboot_required.rc != 0\n  notify:\n    - Reboot server<\/pre>\n<p>&nbsp;<\/p>\n<h3>Run the playbook on the TEST servers<\/h3>\n<p>To patch the TEST servers, we only need to execute the playbook with the extra-var &#8220;ev_env_to_patch=test&#8221; :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">$ ansible-playbook playbooks\/os_update.yml -e \"ev_env_to_patch=test\"\n\nPLAY [Patch TEST or PROD servers] *******************************************************************************************************************************************************************************\n\nTASK [Gathering Facts] ***********************************************************************************************************************************************************************************************************************\nok: [35.156.114.202]\nok: [3.67.8.87]\nok: [54.93.245.3]\nok: [54.93.103.197]\n\nTASK [os_update : include_tasks] *************************************************************************************************************************************************************************************************************\nskipping: [54.93.245.3]\nskipping: [3.67.8.87]\nincluded: \/ansible\/roles\/os_update\/tasks\/test.yml for 35.156.114.202, 54.93.103.197\n\nTASK [os_update : Get packages that can be upgraded on TEST servers] *************************************************************************************************************************************************************************\nok: [54.93.103.197]\nok: [35.156.114.202]\n\nTASK [os_update : List packages that will be upgraded on TEST servers] ***********************************************************************************************************************************************************************\nok: [54.93.103.197] =&gt; {\n    \"changed\": false,\n    \"msg\": [\n        \"bash\",\n        \"bind-export-libs\",\n        \"bind-libs-lite\",\n        \"bind-license\",\n        \"binutils\",\n        \"btrfs-progs\",\n        \"ca-certificates\",\n        \"cloud-init\",\n        \"cronie-anacron\",\n        \"cronie\",\n        \"curl\",\n        \"cyrus-sasl-lib\",\n        \"device-mapper-libs\",\n        \"device-mapper\",\n        \"dhclient\",\n        \"dhcp-common\",\n        \"dhcp-libs\",\n        \"dmidecode\",\n        \"firewalld-filesystem\",\n        \"gettext-libs\",\n        \"gettext\",\n        \"glib2\",\n        \"glibc-common\",\n        \"glibc\",\n        \"grub2-common\",\n        \"grub2-pc-modules\",\n        \"grub2-pc\",\n        \"grub2-tools-extra\",\n        \"grub2-tools-minimal\",\n        \"grub2-tools\",\n        \"grub2\",\n        \"iproute\",\n        \"kbd-legacy\",\n        \"kbd-misc\",\n        \"kbd\",\n        \"kernel-tools-libs\",\n        \"kernel-tools\",\n        \"kpartx\",\n        \"krb5-libs\",\n        \"libblkid\",\n        \"libcurl\",\n        \"libgudev1\",\n        \"libmount\",\n        \"libsmartcols\",\n        \"libuuid\",\n        \"libwebp\",\n        \"libxml2-python\",\n        \"libxml2\",\n        \"linux-firmware\",\n        \"nspr\",\n        \"nss-softokn-freebl\",\n        \"nss-softokn\",\n        \"nss-sysinit\",\n        \"nss-tools\",\n        \"nss-util\",\n        \"nss\",\n        \"openldap\",\n        \"openssh-clients\",\n        \"openssh-server\",\n        \"openssh\",\n        \"openssl-libs\",\n        \"openssl\",\n        \"oraclelinux-release-el7\",\n        \"pciutils-libs\",\n        \"pciutils\",\n        \"python-firewall\",\n        \"python-perf\",\n        \"rpm-build-libs\",\n        \"rpm-libs\",\n        \"rpm-python\",\n        \"rpm\",\n        \"rsyslog\",\n        \"selinux-policy-targeted\",\n        \"selinux-policy\",\n        \"sudo\",\n        \"systemd-libs\",\n        \"systemd-sysv\",\n        \"systemd\",\n        \"tzdata\",\n        \"util-linux\",\n        \"virt-what\"\n    ]\n}\nok: [35.156.114.202] =&gt; {\n    \"changed\": false,\n    \"msg\": [\n        \"bash\",\n        \"bind-export-libs\",\n        \"bind-libs-lite\",\n        \"bind-license\",\n        \"binutils\",\n        \"btrfs-progs\",\n        \"ca-certificates\",\n        \"cloud-init\",\n        \"cronie-anacron\",\n        \"cronie\",\n        \"curl\",\n        \"cyrus-sasl-lib\",\n        \"device-mapper-libs\",\n        \"device-mapper\",\n        \"dhclient\",\n        \"dhcp-common\",\n        \"dhcp-libs\",\n        \"dmidecode\",\n        \"firewalld-filesystem\",\n        \"gettext-libs\",\n        \"gettext\",\n        \"glib2\",\n        \"glibc-common\",\n        \"glibc\",\n        \"grub2-common\",\n        \"grub2-pc-modules\",\n        \"grub2-pc\",\n        \"grub2-tools-extra\",\n        \"grub2-tools-minimal\",\n        \"grub2-tools\",\n        \"grub2\",\n        \"iproute\",\n        \"kbd-legacy\",\n        \"kbd-misc\",\n        \"kbd\",\n        \"kernel-tools-libs\",\n        \"kernel-tools\",\n        \"kpartx\",\n        \"krb5-libs\",\n        \"libblkid\",\n        \"libcurl\",\n        \"libgudev1\",\n        \"libmount\",\n        \"libsmartcols\",\n        \"libuuid\",\n        \"libwebp\",\n        \"libxml2-python\",\n        \"libxml2\",\n        \"linux-firmware\",\n        \"nspr\",\n        \"nss-softokn-freebl\",\n        \"nss-softokn\",\n        \"nss-sysinit\",\n        \"nss-tools\",\n        \"nss-util\",\n        \"nss\",\n        \"openldap\",\n        \"openssh-clients\",\n        \"openssh-server\",\n        \"openssh\",\n        \"openssl-libs\",\n        \"openssl\",\n        \"oraclelinux-release-el7\",\n        \"pciutils-libs\",\n        \"pciutils\",\n        \"python-firewall\",\n        \"python-perf\",\n        \"rpm-build-libs\",\n        \"rpm-libs\",\n        \"rpm-python\",\n        \"rpm\",\n        \"rsyslog\",\n        \"selinux-policy-targeted\",\n        \"selinux-policy\",\n        \"sudo\",\n        \"systemd-libs\",\n        \"systemd-sysv\",\n        \"systemd\",\n        \"tzdata\",\n        \"util-linux\",\n        \"virt-what\"\n    ]\n}\n\nTASK [os_update : Request user confirmation] *************************************************************************************************************************************************************************************************\n[os_update : Request user confirmation]\n\nThe packages listed above will be upgraded. Do you want to continue ?\n-&gt; Press RETURN to continue.\n-&gt; Press Ctrl+c and then \"a\" to abort.\n:\nok: [54.93.103.197]\n\nTASK [os_update : Upgrade packages on TEST servers] ******************************************************************************************************************************************************************************************\nchanged: [35.156.114.202]\nchanged: [54.93.103.197]\n\nTASK [os_update : Print errors if upgrade failed] ********************************************************************************************************************************************************************************************\nskipping: [54.93.103.197]\nskipping: [35.156.114.202]\n\nTASK [os_update : Install yum-utils on TEST servers] *****************************************************************************************************************************************************************************************\nok: [54.93.103.197]\nok: [35.156.114.202]\n\nTASK [os_update : Check if a reboot is required] *********************************************************************************************************************************************************************************************\nchanged: [54.93.103.197]\nchanged: [35.156.114.202]\n\nTASK [os_update : Genereate the list of the upgraded packages on TEST servers] ***************************************************************************************************************************************************************\nchanged: [35.156.114.202]\nchanged: [54.93.103.197]\n\nTASK [os_update : Delete the fake RPM packages gpg-pubkey from the list] *********************************************************************************************************************************************************************\nchanged: [35.156.114.202]\nchanged: [54.93.103.197]\n\nTASK [os_update : Fetch the file from the Ansible Control Node] **********************************************************************************************************************************************************\nchanged: [35.156.114.202]\nchanged: [54.93.103.197]\n\nTASK [os_update : Create directory to store the file on PROD servers] ************************************************************************************************************************************************************************\nok: [54.93.103.197 -&gt; 54.93.245.3] =&gt; (item=54.93.245.3)\nok: [35.156.114.202 -&gt; 54.93.245.3] =&gt; (item=54.93.245.3)\nok: [35.156.114.202 -&gt; 3.67.8.87] =&gt; (item=3.67.8.87)\nok: [54.93.103.197 -&gt; 3.67.8.87] =&gt; (item=3.67.8.87)\n\nTASK [os_update : Copy the file from the Ansible Control node to the PROD servers] ***********************************************************************************************************************************************************\nok: [54.93.103.197 -&gt; 54.93.245.3] =&gt; (item=54.93.245.3)\nchanged: [35.156.114.202 -&gt; 54.93.245.3] =&gt; (item=54.93.245.3)\nok: [35.156.114.202 -&gt; 3.67.8.87] =&gt; (item=3.67.8.87)\nchanged: [54.93.103.197 -&gt; 3.67.8.87] =&gt; (item=3.67.8.87)\n\nTASK [os_update : include_tasks] *************************************************************************************************************************************************************************************************************\nskipping: [54.93.103.197]\nskipping: [35.156.114.202]\nskipping: [54.93.245.3]\nskipping: [3.67.8.87]\n\nRUNNING HANDLER [os_update : Reboot server] **************************************************************************************************************************************************************************************************\nchanged: [54.93.103.197]\nchanged: [35.156.114.202]\n\nPLAY RECAP ***********************************************************************************************************************************************************************************************************************************\n3.67.8.87                  : ok=1    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n35.156.114.202             : ok=13   changed=7    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n54.93.103.197              : ok=14   changed=7    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n54.93.245.3                : ok=1    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n\n$<\/pre>\n<p>&nbsp;<\/p>\n<h3>Run the playbook on the PROD servers<\/h3>\n<p>To patch the PROD servers, we only need to execute the playbook with the extra-var &#8220;ev_env_to_patch=<strong>prod<\/strong>&#8221; :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">$ ansible-playbook playbooks\/os_update.yml -e \"ev_env_to_patch=prod\"\n\nPLAY [Patch TEST or PROD servers] *******************************************************************************************************************************************************************************\n\nTASK [Gathering Facts] ***********************************************************************************************************************************************************************************************************************\nok: [54.93.245.3]\nok: [3.67.8.87]\nok: [35.156.114.202]\nok: [54.93.103.197]\n\nTASK [os_update : include_tasks] *************************************************************************************************************************************************************************************************************\nskipping: [35.156.114.202]\nskipping: [54.93.103.197]\nskipping: [54.93.245.3]\nskipping: [3.67.8.87]\n\nTASK [os_update : include_tasks] *************************************************************************************************************************************************************************************************************\nskipping: [54.93.103.197]\nskipping: [35.156.114.202]\nincluded: \/ansible\/roles\/os_update\/tasks\/prod.yml for 54.93.245.3, 3.67.8.87\n\nTASK [os_update : Check if the packages list file (\/etc\/dbi\/packages-set.txt) is present on PROD servers] ************************************************************************************************************************************\nok: [3.67.8.87]\nok: [54.93.245.3]\n\nTASK [os_update : Fail when file \/etc\/dbi\/packages-set.txt does not exist] *******************************************************************************************************************************************************************\nskipping: [54.93.245.3]\nskipping: [3.67.8.87]\n\nTASK [os_update : Upgrade PROD servers to the same packages version as TEST servers] *********************************************************************************************************************************************************\nchanged: [54.93.245.3]\nchanged: [3.67.8.87]\n\nTASK [os_update : Check if a reboot is required] *********************************************************************************************************************************************************************************************\nchanged: [54.93.245.3]\nchanged: [3.67.8.87]\n\nRUNNING HANDLER [os_update : Reboot server] **************************************************************************************************************************************************************************************************\nchanged: [3.67.8.87]\nchanged: [54.93.245.3]\n\nPLAY RECAP ***********************************************************************************************************************************************************************************************************************************\n3.67.8.87                  : ok=6    changed=3    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n35.156.114.202             : ok=1    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n54.93.103.197              : ok=1    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n54.93.245.3                : ok=6    changed=3    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   \n\n$<\/pre>\n<p>&nbsp;<\/p>\n<p>Easy, isn&#8217;t ? Happy patching !<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Your may want to patch your Linux servers on a regular basis (e.g using &#8220;yum\/dnf update&#8221;). As always, it&#8217;s obviously recommended to : 1) Patch the TEST systems 2) Check if there is no side effects 3) Wait few days or weeks 4) Patch the PROD systems The problem here is that between step 1 [&hellip;]<\/p>\n","protected":false},"author":30,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1321,1320,42,149],"tags":[150,151,152,73,153,154,155,156,157,158],"type_dbi":[],"class_list":["post-581","post","type-post","status-publish","format-standard","hentry","category-ansible","category-devops","category-operating-systems","category-security","tag-ansible","tag-devops","tag-dnf","tag-linux","tag-operating-system","tag-os","tag-patching","tag-prod","tag-test","tag-yum"],"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>Automating Linux patching with Ansible - How to keep all your environments consistent ? - 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\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Automating Linux patching with Ansible - How to keep all your environments consistent ?\" \/>\n<meta property=\"og:description\" content=\"Your may want to patch your Linux servers on a regular basis (e.g using &#8220;yum\/dnf update&#8221;). As always, it&#8217;s obviously recommended to : 1) Patch the TEST systems 2) Check if there is no side effects 3) Wait few days or weeks 4) Patch the PROD systems The problem here is that between step 1 [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-01-14T10:24:04+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-04-06T06:26:38+00:00\" \/>\n<meta name=\"author\" content=\"Jo\u00ebl Cattin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jo\u00ebl Cattin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 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\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/\"},\"author\":{\"name\":\"Jo\u00ebl Cattin\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/2c774f00321ee734515f0c2f6a96b780\"},\"headline\":\"Automating Linux patching with Ansible &#8211; How to keep all your environments consistent ?\",\"datePublished\":\"2022-01-14T10:24:04+00:00\",\"dateModified\":\"2022-04-06T06:26:38+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/\"},\"wordCount\":642,\"commentCount\":1,\"keywords\":[\"Ansible\",\"DevOps\",\"dnf\",\"Linux\",\"operating system\",\"os\",\"Patching\",\"prod\",\"test\",\"yum\"],\"articleSection\":[\"Ansible\",\"DevOps\",\"Operating systems\",\"Security\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/\",\"name\":\"Automating Linux patching with Ansible - How to keep all your environments consistent ? - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\"},\"datePublished\":\"2022-01-14T10:24:04+00:00\",\"dateModified\":\"2022-04-06T06:26:38+00:00\",\"author\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/2c774f00321ee734515f0c2f6a96b780\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.dbi-services.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Automating Linux patching with Ansible &#8211; How to keep all your environments consistent ?\"}]},{\"@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\/2c774f00321ee734515f0c2f6a96b780\",\"name\":\"Jo\u00ebl Cattin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/a4271811924694263d4de5a469f8bd4a90b14d3d90e6ad819b9e2e5ac035a2dc?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a4271811924694263d4de5a469f8bd4a90b14d3d90e6ad819b9e2e5ac035a2dc?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a4271811924694263d4de5a469f8bd4a90b14d3d90e6ad819b9e2e5ac035a2dc?s=96&d=mm&r=g\",\"caption\":\"Jo\u00ebl Cattin\"},\"description\":\"Jo\u00ebl Cattin has more than three years of experience in databases management. He is specialized in Oracle solutions such as Data Guard and RMAN and has a good background knowledge of Oracle Database Appliance (ODA), Real Application Cluster (RAC) and applications development on APEX. Jo\u00ebl Cattin\u2019s experience includes other RDBMS, such as PostgreSQL and MySQL. He is Oracle Database 12c Administrator Certified Professional, EDB Postgres Advanced Server 9.5 Certified Professional, RedHat Certified System Administrator and ITILv3 Foundation for Service Management Certified. Jo\u00ebl Cattin holds a degree from the \u00c9cole Sup\u00e9rieure d\u2019Informatique de Gestion (ESIG) in Del\u00e9mont and a Federal Certificate of Proficiency in Computer Science (Certificat f\u00e9d\u00e9ral de Capacit\u00e9 \u2013 CFC).\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/author\/joel-cattin\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Automating Linux patching with Ansible - How to keep all your environments consistent ? - 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\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/","og_locale":"en_US","og_type":"article","og_title":"Automating Linux patching with Ansible - How to keep all your environments consistent ?","og_description":"Your may want to patch your Linux servers on a regular basis (e.g using &#8220;yum\/dnf update&#8221;). As always, it&#8217;s obviously recommended to : 1) Patch the TEST systems 2) Check if there is no side effects 3) Wait few days or weeks 4) Patch the PROD systems The problem here is that between step 1 [&hellip;]","og_url":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/","og_site_name":"dbi Blog","article_published_time":"2022-01-14T10:24:04+00:00","article_modified_time":"2022-04-06T06:26:38+00:00","author":"Jo\u00ebl Cattin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jo\u00ebl Cattin","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/"},"author":{"name":"Jo\u00ebl Cattin","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/2c774f00321ee734515f0c2f6a96b780"},"headline":"Automating Linux patching with Ansible &#8211; How to keep all your environments consistent ?","datePublished":"2022-01-14T10:24:04+00:00","dateModified":"2022-04-06T06:26:38+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/"},"wordCount":642,"commentCount":1,"keywords":["Ansible","DevOps","dnf","Linux","operating system","os","Patching","prod","test","yum"],"articleSection":["Ansible","DevOps","Operating systems","Security"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/","url":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/","name":"Automating Linux patching with Ansible - How to keep all your environments consistent ? - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2022-01-14T10:24:04+00:00","dateModified":"2022-04-06T06:26:38+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/2c774f00321ee734515f0c2f6a96b780"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/automating-linux-patching-with-ansible-how-to-keep-all-your-environments-consistent\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Automating Linux patching with Ansible &#8211; How to keep all your environments consistent ?"}]},{"@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\/2c774f00321ee734515f0c2f6a96b780","name":"Jo\u00ebl Cattin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/a4271811924694263d4de5a469f8bd4a90b14d3d90e6ad819b9e2e5ac035a2dc?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/a4271811924694263d4de5a469f8bd4a90b14d3d90e6ad819b9e2e5ac035a2dc?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a4271811924694263d4de5a469f8bd4a90b14d3d90e6ad819b9e2e5ac035a2dc?s=96&d=mm&r=g","caption":"Jo\u00ebl Cattin"},"description":"Jo\u00ebl Cattin has more than three years of experience in databases management. He is specialized in Oracle solutions such as Data Guard and RMAN and has a good background knowledge of Oracle Database Appliance (ODA), Real Application Cluster (RAC) and applications development on APEX. Jo\u00ebl Cattin\u2019s experience includes other RDBMS, such as PostgreSQL and MySQL. He is Oracle Database 12c Administrator Certified Professional, EDB Postgres Advanced Server 9.5 Certified Professional, RedHat Certified System Administrator and ITILv3 Foundation for Service Management Certified. Jo\u00ebl Cattin holds a degree from the \u00c9cole Sup\u00e9rieure d\u2019Informatique de Gestion (ESIG) in Del\u00e9mont and a Federal Certificate of Proficiency in Computer Science (Certificat f\u00e9d\u00e9ral de Capacit\u00e9 \u2013 CFC).","url":"https:\/\/www.dbi-services.com\/blog\/author\/joel-cattin\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/581","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\/30"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=581"}],"version-history":[{"count":4,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/581\/revisions"}],"predecessor-version":[{"id":710,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/581\/revisions\/710"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=581"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}