wiki/guidelines/awx_scm_guidelines/index.html

1128 lines
36 KiB
HTML

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Infrastructure Wiki and Documentation">
<link rel="canonical" href="https://infra.rocky.page/guidelines/awx_scm_guidelines/">
<link rel="prev" href="../">
<link rel="next" href="../../sop/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.17">
<title>AWX / Ansible SCM Guidelines - Infrastructure Wiki</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.bcfcd587.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/palette.06af60db.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="teal" data-md-color-accent="teal">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#contact-information" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="Infrastructure Wiki" class="md-header__button md-logo" aria-label="Infrastructure Wiki" data-md-component="logo">
<img src="../../assets/icon-white.svg" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Infrastructure Wiki
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
AWX / Ansible SCM Guidelines
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="teal" data-md-color-accent="teal" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="teal" data-md-color-accent="teal" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
</label>
</form>
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://git.resf.org/infrastructure/wiki" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
infrastructure/wiki
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--integrated" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="Infrastructure Wiki" class="md-nav__button md-logo" aria-label="Infrastructure Wiki" data-md-component="logo">
<img src="../../assets/icon-white.svg" alt="logo">
</a>
Infrastructure Wiki
</label>
<div class="md-nav__source">
<a href="https://git.resf.org/infrastructure/wiki" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
infrastructure/wiki
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Infrastructure Wiki
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../members/" class="md-nav__link">
<span class="md-ellipsis">
Members
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../what_we_do/" class="md-nav__link">
<span class="md-ellipsis">
What We Do
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4" >
<div class="md-nav__link md-nav__container">
<a href="../../documentation/" class="md-nav__link ">
<span class="md-ellipsis">
Documentation
</span>
</a>
<label class="md-nav__link " for="__nav_4" id="__nav_4_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_2" >
<div class="md-nav__link md-nav__container">
<a href="../../documentation/idm/" class="md-nav__link ">
<span class="md-ellipsis">
Identity Management
</span>
</a>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_2">
<span class="md-nav__icon md-icon"></span>
Identity Management
</label>
<ul class="md-nav__list" data-md-scrollfix>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../../documentation/dns/" class="md-nav__link">
<span class="md-ellipsis">
DNS Zone Standards
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" checked>
<div class="md-nav__link md-nav__container">
<a href="../" class="md-nav__link ">
<span class="md-ellipsis">
Guidelines
</span>
</a>
<label class="md-nav__link " for="__nav_5" id="__nav_5_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Guidelines
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
AWX / Ansible SCM Guidelines
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
AWX / Ansible SCM Guidelines
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#contact-information" class="md-nav__link">
<span class="md-ellipsis">
Contact Information
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#guidelines" class="md-nav__link">
<span class="md-ellipsis">
Guidelines
</span>
</a>
<nav class="md-nav" aria-label="Guidelines">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#root-structure" class="md-nav__link">
<span class="md-ellipsis">
Root Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#designing-playbooks" class="md-nav__link">
<span class="md-ellipsis">
Designing Playbooks
</span>
</a>
<nav class="md-nav" aria-label="Designing Playbooks">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#pre-flight-and-post-flight-tasks" class="md-nav__link">
<span class="md-ellipsis">
Pre-flight and Post-flight Tasks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#tasks-general-information" class="md-nav__link">
<span class="md-ellipsis">
Tasks General Information
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#comments" class="md-nav__link">
<span class="md-ellipsis">
Comments
</span>
</a>
<nav class="md-nav" aria-label="Comments">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#tags" class="md-nav__link">
<span class="md-ellipsis">
Tags
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#playbook-naming-system" class="md-nav__link">
<span class="md-ellipsis">
Playbook Naming System
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#defining-hosts" class="md-nav__link">
<span class="md-ellipsis">
Defining Hosts
</span>
</a>
<nav class="md-nav" aria-label="Defining Hosts">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#local-inventory-files" class="md-nav__link">
<span class="md-ellipsis">
Local Inventory Files
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#local-ansiblecfg-files" class="md-nav__link">
<span class="md-ellipsis">
Local ansible.cfg files
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#collections-and-roles" class="md-nav__link">
<span class="md-ellipsis">
Collections and Roles
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#pre-commits-linting" class="md-nav__link">
<span class="md-ellipsis">
Pre-commits / linting
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#tests" class="md-nav__link">
<span class="md-ellipsis">
Tests
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_6" >
<div class="md-nav__link md-nav__container">
<a href="../../sop/" class="md-nav__link ">
<span class="md-ellipsis">
SOP
</span>
</a>
<label class="md-nav__link " for="__nav_6" id="__nav_6_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
SOP
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../sop/idm_sop_gdpr/" class="md-nav__link">
<span class="md-ellipsis">
SOP: Personal Data Request - Deletion
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../sop/idm_sop_mm_ras/" class="md-nav__link">
<span class="md-ellipsis">
SOP: Mattermost and RAS Group Sync
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<a href="https://git.resf.org/infrastructure/wiki/_edit/main/docs/guidelines/awx_scm_guidelines.md" title="Edit this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v-2m10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1 2.1 2.1Z"/></svg>
</a>
<h1>AWX / Ansible SCM Guidelines</h1>
<p>This document covers the guidelines as set out by the Infrastructure/Core group for designing modular repositories that may be used in the Rocky AWX instance or local execution based on team needs. This is meant to supersede the guidelines in the ansible-awx-template repository.</p>
<p>This does not cover detailed examples, but is meant to get teams and their contributors started in designing or improving upon all ansible related activities for their group.</p>
<h2 id="contact-information">Contact Information<a class="headerlink" href="#contact-information" title="Permanent link">&para;</a></h2>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Owner</strong></td>
<td>Infrastructure Team</td>
</tr>
<tr>
<td><strong>Email Contact</strong></td>
<td>infrastructure@rockylinux.org</td>
</tr>
<tr>
<td><strong>Mattermost Contacts</strong></td>
<td><code>@label</code></td>
</tr>
<tr>
<td><strong>Mattermost Contacts</strong></td>
<td><code>@neil</code></td>
</tr>
<tr>
<td><strong>Mattermost Contacts</strong></td>
<td><code>@tgo</code></td>
</tr>
<tr>
<td><strong>Mattermost Channels</strong></td>
<td><code>~Infrastructure</code></td>
</tr>
</tbody>
</table>
<h2 id="guidelines">Guidelines<a class="headerlink" href="#guidelines" title="Permanent link">&para;</a></h2>
<p>This section covers the basics for your AWX project. It is absolutely important that you start with these as an absolute bare minimum. While you will be forking/cloning off of <code>infrastructure/ansible-awx-template</code> and using that as the starting point, the next few sections will explain the basic structure and basic design principals.</p>
<p>You should begin by starting from the <a href="https://git.resf.org/infrastructure/ansible-awx-template">Infrastructure Ansible AWX Template</a>.</p>
<h3 id="root-structure">Root Structure<a class="headerlink" href="#root-structure" title="Permanent link">&para;</a></h3>
<p>The general structure will always start from this:</p>
<div class="highlight"><pre><span></span><code>.
├── somePlaybook.yml
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
└── main.yml
</code></pre></div>
<p>This structure follows the basic expected structure for ansible (this means ignoring AWX/Tower). If you are familiar with ansible already, you may already know how these files and directories work at an operational level. The gist of it is:</p>
<ul>
<li>All playbooks should be in the root and import tasks from <code>./tasks</code> if needed</li>
<li>Vars should be clearly defined where needed in vars, defaults, and/or playbooks</li>
<li>Files and templates should be created with a purpose</li>
<li>Handlers should be clearly defined and used</li>
<li>There should be wiggle room to add callback_plugins, filter_plugins, libraries</li>
</ul>
<p>With these basic ideas in mind, we can move into playbook design.</p>
<h3 id="designing-playbooks">Designing Playbooks<a class="headerlink" href="#designing-playbooks" title="Permanent link">&para;</a></h3>
<p>Generally, your playbooks should be doing the following:</p>
<ol>
<li>Checking if ansible can be ran on a specific host</li>
<li>Asserting if variables are filled and are correctly formed, if applicable</li>
<li>Importing tasks from the <code>./tasks</code> directory</li>
<li>Importing roles, if necessary</li>
<li>Post tasks, if necessary</li>
</ol>
<p><strong>At no point should you be using</strong> <code>./tasks/main.yml</code> <strong>under any circumstance.</strong></p>
<h4 id="pre-flight-and-post-flight-tasks">Pre-flight and Post-flight Tasks<a class="headerlink" href="#pre-flight-and-post-flight-tasks" title="Permanent link">&para;</a></h4>
<p>In majority of cases, you will need to have pre-flight and post-flight tasks. These aren't needed in <em>all</em> cases, but they should be used as a starting point.</p>
<div class="highlight"><pre><span></span><code> pre_tasks:
- name: Check if ansible cannot be run here
ansible.builtin.stat:
path: /etc/no-ansible
register: no_ansible
- name: Verify if we can run ansible
ansible.builtin.assert:
that:
- &quot;not no_ansible.stat.exists&quot;
success_msg: &quot;We are able to run on this node&quot;
fail_msg: &quot;/etc/no-ansible exists - skipping run on this node&quot;
# Assertions and other checks here
# Import roles/tasks here
post_tasks:
- name: Touching run file that ansible has ran here
ansible.builtin.file:
path: /var/log/ansible.run
state: touch
mode: &#39;0644&#39;
owner: root
group: root
</code></pre></div>
<h4 id="tasks-general-information">Tasks General Information<a class="headerlink" href="#tasks-general-information" title="Permanent link">&para;</a></h4>
<p>Ensure that your tasks are using FQCN. This means, even for the simple modules such as <code>file</code>, you should be using <code>ansible.builtin.file</code> to be compliant with ansible-lint 6+ and ansible 2.12+.</p>
<h3 id="comments">Comments<a class="headerlink" href="#comments" title="Permanent link">&para;</a></h3>
<p>Each playbook should have comments or a name descriptor that explains what the playbook does or how it is used. If not available, <code>README-...</code> can be used in place, especially in the case of adhoc playbooks that take or require input. Documentation for each playbook/role does not have to be on a wiki. Comments or README's should be perfectly sufficient.</p>
<h4 id="tags">Tags<a class="headerlink" href="#tags" title="Permanent link">&para;</a></h4>
<p>Ensure that you are using relevant tags where necessary for your tasks. This will allow you or the deployers to have deeper control of what is ran or called in AWX.</p>
<h4 id="playbook-naming-system">Playbook Naming System<a class="headerlink" href="#playbook-naming-system" title="Permanent link">&para;</a></h4>
<p>When making playbooks, there is a set of predefined prefixes you will need to set. It is highly discouraged to step outside of these prefixes.</p>
<div class="highlight"><pre><span></span><code>init-* -&gt; Starting playbooks that run solo or import other playbooks that start
with import-. Can also be used to run updates or repetive tasks that
adhoc may not suffice and running a role playbook is too much overhead.
adhoc -&gt; These playbooks are one-off playbooks that can be used on the CLI or
in AWX. These are typically for basic tasks.
import -&gt; Playbooks that should be imported from the top level playbooks or
used to &quot;import&quot; or &quot;add&quot; data somewhere (e.g., a database or LDAP)
role-* -&gt; These playbooks call roles for potential tasks or even roles in general.
</code></pre></div>
<div class="admonition note">
<p class="admonition-title">Using the role prefix without an ansible role</p>
<p>It is perfectly fine to use <code>role-</code> as a way to say "this system will do or be X" without calling out to an ansible role. You may use <code>role</code> for this case or you can use <code>init</code>. This is <strong>not</strong> a strict requirement and you should go with what feels right for your project.</p>
</div>
<h4 id="defining-hosts">Defining Hosts<a class="headerlink" href="#defining-hosts" title="Permanent link">&para;</a></h4>
<p>There will likely be multiple dynamic inventory sources used for hosts managed by AWX, and as a result, there will be a lot of groups defined with one or more hosts at a time. As this is the case, here are some things to keep in mind:</p>
<ul>
<li>Use group names where necessary</li>
<li>Use localhost if you aren't actually doing anything to a system (e.g., you're calling an API) <em>and</em> you don't have to connect to a system to use said API</li>
<li>
<p>When filling in the <code>hosts</code> directive, follow these general guidelines:</p>
<ul>
<li>If it applies to all hosts in an inventory, use <code>all</code></li>
<li>If you want the host or a group of hosts to be selectable (via dropdown or manual input), set <code>host: {{ host }}</code> and the host var can be defined as an extra var</li>
<li>If the above two are not applicable and you must set a hostname, you may do so. Note that this will require you to be more vigilant in keeping your repository up to date.</li>
</ul>
</li>
</ul>
<h5 id="local-inventory-files">Local Inventory Files<a class="headerlink" href="#local-inventory-files" title="Permanent link">&para;</a></h5>
<p>Generally local inventory files are not recommended. Some general rules to follow is this:</p>
<ul>
<li>If your project can be ran in AWX <em>and</em> locally outside of AWX, the inventory file should not be committed as <code>inventory</code> (the current <code>.gitignore</code> prevents this from being committed by default)</li>
<li>If your project is local only (meaning it will not be used in AWX, but your team will be using it locally), you may modify <code>.gitignore</code> to include the file</li>
</ul>
<p>We want to prevent AWX from picking up a random inventory that isn't defined within it.</p>
<h5 id="local-ansiblecfg-files">Local ansible.cfg files<a class="headerlink" href="#local-ansiblecfg-files" title="Permanent link">&para;</a></h5>
<p>General ansible.cfg files are not recommended as they would be picked up during normal operation. These should be provided only for special cases. Optionally, you may provide it under another name that a user can reference for local execution outside of AWX.</p>
<h4 id="collections-and-roles">Collections and Roles<a class="headerlink" href="#collections-and-roles" title="Permanent link">&para;</a></h4>
<p>Collections and roles should be defined in a <code>requirements.yml</code> in their respective directories. AWX will pick them up. Optionally, you can provide a playbook or script to install roles and collections locally. Example commands that could be in a script or playbook:</p>
<div class="highlight"><pre><span></span><code>ansible-galaxy collection install -r collections/requirements.yml
ansible-galaxy role install -r roles/requirements.yml
</code></pre></div>
<p>Tools like <code>ansible-navigator</code> and <code>ansible-builder</code> can also help in this area as well.</p>
<h4 id="pre-commits-linting">Pre-commits / linting<a class="headerlink" href="#pre-commits-linting" title="Permanent link">&para;</a></h4>
<p>When committing, pre-commit must run to verify your changes. They must be passing to be pushed up. This is an absolute requirement, even for roles. There are very rare exceptions to this rule and they may be granted depending on what it is.</p>
<p>When the linter passes, a push can be performed. After that, if a PR is necessary, open one. Otherwise, it should be free to use locally or in AWX.</p>
<h4 id="tests">Tests<a class="headerlink" href="#tests" title="Permanent link">&para;</a></h4>
<p>A template generally comes with a <code>tests</code> directory. While not strictly required, it is recommended to create a suite of tests to ensure most, if not all of your playbooks are in working order. This is similar to providing tests to ansible collections, in that they should test at least basic functionality.</p>
<p>Complex situations can be tested for as well and is encouraged.</p>
<aside class="md-source-file">
<span class="md-source-file__fact">
<span class="md-icon" title="Last update">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1-2.1-2M12.5 7v5.2l4 2.4-1 1L11 13V7h1.5M11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2v1.8Z"/></svg>
</span>
<span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">April 13, 2023</span>
</span>
</aside>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12Z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2023 Rocky Enterprise Software Foundation
</div>
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "../..", "features": ["navigation.expand", "navigation.indexes", "navigation.instant", "navigation.sections", "navigation.top", "navigation.tracking", "navigation.path", "search.highlight", "search.suggest", "toc.integrate", "content.action.edit"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
<script src="../../assets/javascripts/bundle.1e8ae164.min.js"></script>
</body>
</html>