50-install-prepare.yml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. ---
  2. # Download all the tools, set up registry credentials, ssh keys, and ensure the health of a DHCP and a DNS server on utility.
  3. #
  4. # XXX: If someone has gone beyond this point in the course and runs this playbook, it will revert the changes back to the original.
  5. - name: Warn that this playbook might cause subsequent exercises to be reverted.
  6. hosts: workstation.lab.example.com
  7. gather_facts: no
  8. become: no
  9. tasks:
  10. - ansible.builtin.pause:
  11. prompt: |
  12. WARNING: This playbook resets the configuration of DNS and DHCP services to what they
  13. are originally when the lab is provisioned.
  14. This means that if you have performed any exercises past the preparation for
  15. installation, your changes will be reverted.
  16. Continuing in 10 seconds unless you interrupt execution.
  17. seconds: 10
  18. - name: Download all the tools we need on workstation, set up registry credentials, and a SSH keypair.
  19. hosts: workstation.lab.example.com
  20. gather_subset: min
  21. become: no
  22. vars:
  23. downloads:
  24. - baseurl: https://mirror.openshift.com/pub/openshift-v4/clients/butane/latest
  25. filename: butane-amd64
  26. dest_dir: /usr/local/bin
  27. deploy: yes
  28. extract: no
  29. extract_files: []
  30. target_filename: butane
  31. completion: no
  32. - baseurl: https://mirror.openshift.com/pub/openshift-v4/clients/coreos-installer/latest
  33. filename: coreos-installer_amd64
  34. dest_dir: /usr/local/bin
  35. deploy: yes
  36. extract: no
  37. extract_files: []
  38. target_filename: coreos-installer
  39. completion: no
  40. - baseurl: https://mirror.openshift.com/pub/openshift-v4/clients/ocp/4.18.6
  41. filename: openshift-install-rhel9-amd64.tar.gz
  42. dest_dir: /usr/local/bin
  43. deploy: yes
  44. extract: yes
  45. extract_files:
  46. - openshift-install-fips
  47. target_filename: None
  48. completion: yes
  49. - baseurl: https://mirror.openshift.com/pub/openshift-v4/clients/ocp/stable-4.18
  50. filename: openshift-client-linux-amd64-rhel9.tar.gz
  51. dest_dir: /usr/local/bin
  52. deploy: yes
  53. extract: yes
  54. extract_files:
  55. - oc
  56. - kubectl
  57. target_filename: None
  58. completion: yes
  59. - baseurl: https://rhcos.mirror.openshift.com/art/storage/prod/streams/4.18-9.4/builds/418.94.202501221327-0/x86_64
  60. filename: rhcos-418.94.202501221327-0-live.x86_64.iso
  61. deploy: no
  62. tasks:
  63. - name: Download tools, if necessary.
  64. ansible.builtin.get_url:
  65. url: "{{ item.baseurl }}/{{ item.filename }}"
  66. dest: "{{ ansible_facts['user_dir'] }}/Downloads/{{ item.filename }}"
  67. mode: 0644
  68. owner: student
  69. group: student
  70. loop: "{{ downloads }}"
  71. loop_control:
  72. label: "{{ item.filename }}"
  73. - name: Extract tools, if necessary.
  74. become: yes
  75. ansible.builtin.unarchive:
  76. src: "{{ ansible_facts['user_dir'] }}/Downloads/{{ item.filename }}"
  77. remote_src: yes
  78. dest: "{{ item.dest_dir }}"
  79. include: "{{ item.extract_files }}"
  80. creates: "{{ item.extract_files[0] }}"
  81. mode: 0755
  82. owner: root
  83. group: root
  84. loop: "{{ downloads }}"
  85. loop_control:
  86. label: "{{ item.filename }}"
  87. when:
  88. - item.deploy
  89. - item.extract
  90. - name: Install tools, if necessary.
  91. become: yes
  92. ansible.builtin.copy:
  93. src: "{{ ansible_facts['user_dir'] }}/Downloads/{{ item.filename }}"
  94. remote_src: yes
  95. dest: "{{ item.dest_dir }}/{{ item.target_filename }}"
  96. mode: 0755
  97. owner: root
  98. group: root
  99. loop: "{{ downloads }}"
  100. loop_control:
  101. label: "{{ item.filename }}"
  102. when:
  103. - item.deploy
  104. - not item.extract
  105. - item.target_filename is defined
  106. - item.target_filename != None
  107. - name: Create command completion files, if necessary.
  108. become: yes
  109. ansible.builtin.shell:
  110. cmd: "{{ item.extract_files[0] }} completion bash > /etc/bash_completion.d/{{ item.extract_files[0] }}.completion"
  111. creates: "/etc/bash_completion.d/{{ item.extract_files[0] }}.completion"
  112. loop: "{{ downloads }}"
  113. loop_control:
  114. label: "{{ item.filename }}"
  115. when:
  116. - item.completion is defined
  117. - item.completion
  118. - name: Ensure .docker directory exists.
  119. ansible.builtin.file:
  120. path: "{{ ansible_facts['user_dir'] }}/.docker"
  121. state: directory
  122. mode: 0750
  123. owner: student
  124. group: student
  125. - name: Ensure mirror registry auth file is on standby.
  126. ansible.builtin.copy:
  127. dest: "{{ ansible_facts['user_dir'] }}/.docker/config.json-mirror"
  128. mode: 0640
  129. owner: student
  130. group: student
  131. content: '{"auths":{"registry.ocp4.example.com":{"auth":"YWRtaW46cmVkaGF0MTIz"},"cloud.openshift.com":{"auth":"b3BlbnNoaWZ0LXJlbGVhc2UtZGV2K29jbV9hY2Nlc3NfNGM4ZmZhZTI5ZjUzNGJiZjgyZDhlNWE0ZmZiYjRiY2Y6N0NER083UjM4TE1KTEJTN1EzVkVKQVkyTVVCUkpXQjNWNlUxR1BLVzRaN0tGTVpaSENVVTMwMVdaTzMxTDBPNQ==","email":"glsbugs-devops@redhat.com"},"quay.io":{"auth":"b3BlbnNoaWZ0LXJlbGVhc2UtZGV2K29jbV9hY2Nlc3NfNGM4ZmZhZTI5ZjUzNGJiZjgyZDhlNWE0ZmZiYjRiY2Y6N0NER083UjM4TE1KTEJTN1EzVkVKQVkyTVVCUkpXQjNWNlUxR1BLVzRaN0tGTVpaSENVVTMwMVdaTzMxTDBPNQ==","email":"glsbugs-devops@redhat.com"},"registry.connect.redhat.com":{"auth":"fHVoYy1wb29sLTlmMDA1Mzc2LTM2YTItNDJhMS1hNTQwLTA0NzNkYzg3MzYzMzpleUpoYkdjaU9pSlNVelV4TWlKOS5leUp6ZFdJaU9pSTVPRGc1WVdFeFl6Qm1PV0kwWmpVM1lqazNObUk1WldFeU16SXdaalUwTUNKOS5zWmQ5VE1RbzBXREc2NUc5Qk1ObmtuYlBjRkIzNmhyRFhkMThfdTNLeHFaczdlOG1hQ19QeEFReGpwdVk0YVM2VERIbkxDNWpGYjRRNXFYVEpWbjJCOGE4cDFuY08tM24ySG5QdDg3NmktVUFDU3lldWtpb3k4aHI0V3d1ZkhReFVYMmxxWFhYdjN6blE3am1URUNBc25rWkNRSFU1dFNpRnNUZHhFZGZkeU42Z20xN3VqY2thZG5NbFBZcTZfU1I2bUtLaUpUdFQ3SFlDWXJBVk5zZ0tfNGFkZ2MtRXBlbEtHbGNERWkzNGhYbzFqbEIzRERyUWkxSUxCV0UwZkdXb1czZy1ZUzFGMFlEXzc0bm1XSU5mUE1jM25UOERaQWl0OEw0VlFPTnZnUE51YnVfTVVGUGhqX29VUjF3VUR0a1BRNktJdm82UWYyRkdwMndLM1B6YnRBRFFzRVZTZDlITzQ3a0RKdGFobk95YTFmRmdqZVk1bFNxLW1vT2RqUldCZ3U2XzNIX25lZExJR1lQRHRBZnp5cGJ1eHZ1cEd1M2hYWnVzeWN0aURtR203SkR5RW5KdjF1RFZmYVduU2EzSV9NcFRSVVcyZWU1RF9CanJleTdlU2I0bEpGcmp1eC1nY2JVaHFsWGJZc2l6azdXWHpvRmtrVFlMdXFDQ1FvS1J0OFdSN1UzTmh3c3Q2ckV3eEFOaWJFTlNzUVB3MGg4X0NDRm5qTHFSTl82cWpTc0tpeWRGT2tHVFliT0taTktaSVVhYkZFTjRhYVRVYmlYTVdPS2Eyak1xLUhwazBMNEowUmtOM2JkQVVqWmtERHE0ZFY1ZVFjdXNIeV9LY29nd1VKSjZ4MDNObnM4b0xBdjRJZ3RKeXlxcmE1YUJHSkxReHNjRXVSNzQwWQ==","email":"glsbugs-devops@redhat.com"},"registry.redhat.io":{"auth":"fHVoYy1wb29sLTlmMDA1Mzc2LTM2YTItNDJhMS1hNTQwLTA0NzNkYzg3MzYzMzpleUpoYkdjaU9pSlNVelV4TWlKOS5leUp6ZFdJaU9pSTVPRGc1WVdFeFl6Qm1PV0kwWmpVM1lqazNObUk1WldFeU16SXdaalUwTUNKOS5zWmQ5VE1RbzBXREc2NUc5Qk1ObmtuYlBjRkIzNmhyRFhkMThfdTNLeHFaczdlOG1hQ19QeEFReGpwdVk0YVM2VERIbkxDNWpGYjRRNXFYVEpWbjJCOGE4cDFuY08tM24ySG5QdDg3NmktVUFDU3lldWtpb3k4aHI0V3d1ZkhReFVYMmxxWFhYdjN6blE3am1URUNBc25rWkNRSFU1dFNpRnNUZHhFZGZkeU42Z20xN3VqY2thZG5NbFBZcTZfU1I2bUtLaUpUdFQ3SFlDWXJBVk5zZ0tfNGFkZ2MtRXBlbEtHbGNERWkzNGhYbzFqbEIzRERyUWkxSUxCV0UwZkdXb1czZy1ZUzFGMFlEXzc0bm1XSU5mUE1jM25UOERaQWl0OEw0VlFPTnZnUE51YnVfTVVGUGhqX29VUjF3VUR0a1BRNktJdm82UWYyRkdwMndLM1B6YnRBRFFzRVZTZDlITzQ3a0RKdGFobk95YTFmRmdqZVk1bFNxLW1vT2RqUldCZ3U2XzNIX25lZExJR1lQRHRBZnp5cGJ1eHZ1cEd1M2hYWnVzeWN0aURtR203SkR5RW5KdjF1RFZmYVduU2EzSV9NcFRSVVcyZWU1RF9CanJleTdlU2I0bEpGcmp1eC1nY2JVaHFsWGJZc2l6azdXWHpvRmtrVFlMdXFDQ1FvS1J0OFdSN1UzTmh3c3Q2ckV3eEFOaWJFTlNzUVB3MGg4X0NDRm5qTHFSTl82cWpTc0tpeWRGT2tHVFliT0taTktaSVVhYkZFTjRhYVRVYmlYTVdPS2Eyak1xLUhwazBMNEowUmtOM2JkQVVqWmtERHE0ZFY1ZVFjdXNIeV9LY29nd1VKSjZ4MDNObnM4b0xBdjRJZ3RKeXlxcmE1YUJHSkxReHNjRXVSNzQwWQ==","email":"glsbugs-devops@redhat.com"}}}'
  132. - name: Ensure installation registry auth file is active.
  133. ansible.builtin.copy:
  134. dest: "{{ item }}"
  135. mode: 0640
  136. owner: student
  137. group: student
  138. content: '{"auths":{"registry.ocp4.example.com":{"auth":"YWRtaW46cmVkaGF0MTIz"}}}'
  139. loop:
  140. - "{{ ansible_facts['user_dir'] }}/.docker/config.json"
  141. - "{{ ansible_facts['user_dir'] }}/install-pull-secret"
  142. - name: Create a SSH keypair, if necessary.
  143. community.crypto.openssh_keypair:
  144. path: "{{ ansible_facts['user_dir'] }}/.ssh/openshift"
  145. type: rsa
  146. size: 2048
  147. state: present
  148. - name: Ensure DHCP and DNS servers on utility are configured.
  149. hosts: utility.lab.example.com
  150. gather_subset: min
  151. become: yes
  152. tasks:
  153. - name: Ensure there is a DHCP server installed.
  154. ansible.builtin.yum:
  155. name: dhcp-server
  156. state: present
  157. - name: Fix the environment of the DHCP server a bit by ensuring the unit file specifies it.
  158. ansible.builtin.copy:
  159. dest: /etc/systemd/system/dhcpd.service
  160. mode: 0644
  161. owner: root
  162. group: root
  163. content: |
  164. [Unit]
  165. Description=DHCPv4 Server Daemon
  166. Documentation=man:dhcpd(8) man:dhcpd.conf(5)
  167. Wants=network-online.target
  168. After=network-online.target
  169. After=time-sync.target
  170. [Service]
  171. Type=notify
  172. Environment=DHCPDARGS=eth1
  173. EnvironmentFile=-/etc/sysconfig/dhcpd
  174. ExecStart=/usr/sbin/dhcpd -f -cf /etc/dhcp/dhcpd.conf -user dhcpd -group dhcpd --no-pid $DHCPDARGS
  175. StandardError=null
  176. [Install]
  177. WantedBy=multi-user.target
  178. notify: reload systemd
  179. - name: Ensure systemd is reloaded if it needs to be at this point.
  180. ansible.builtin.meta: flush_handlers
  181. - name: Ensure the DHCP config file has the correct content.
  182. ansible.builtin.copy:
  183. dest: /etc/dhcp/dhcpd.conf
  184. mode: 0640
  185. owner: root
  186. group: root
  187. content: |
  188. ddns-update-style interim;
  189. ignore client-updates;
  190. authoritative;
  191. allow booting;
  192. allow bootp;
  193. allow unknown-clients;
  194. # Set default and max IP lease time to infinite with -1 value
  195. default-lease-time -1;
  196. max-lease-time -1;
  197. # BEGIN ANSIBLE MANAGED DHCP CONFIG for ocp4.example.com
  198. subnet 192.168.50.0 netmask 255.255.255.0 {
  199. range 192.168.50.100 192.168.50.149;
  200. option routers 192.168.50.254;
  201. option ntp-servers 103.16.182.23,103.16.182.214;
  202. option domain-search "ocp4.example.com";
  203. filename "pxelinux.0";
  204. option domain-name-servers 192.168.50.254;
  205. next-server 192.168.50.254;
  206. host master01.ocp4.example.com { hardware ethernet 52:54:00:00:32:0A; fixed-address 192.168.50.10; option host-name "master01"; }
  207. host master02.ocp4.example.com { hardware ethernet 52:54:00:00:32:0B; fixed-address 192.168.50.11; option host-name "master02"; }
  208. host master03.ocp4.example.com { hardware ethernet 52:54:00:00:32:0C; fixed-address 192.168.50.12; option host-name "master03"; }
  209. host worker01.ocp4.example.com { hardware ethernet 52:54:00:00:32:0D; fixed-address 192.168.50.13; option host-name "worker01"; }
  210. host worker02.ocp4.example.com { hardware ethernet 52:54:00:00:32:0E; fixed-address 192.168.50.14; option host-name "worker02"; }
  211. }
  212. # END ANSIBLE MANAGED DHCP CONFIG for ocp4.example.com
  213. notify: restart dhcpd
  214. - name: Ensure DHCP server is enabled and running.
  215. ansible.builtin.systemd_service:
  216. name: dhcpd
  217. enabled: yes
  218. state: started
  219. - name: Ensure the Bind config file has the correct content.
  220. ansible.builtin.copy:
  221. dest: /etc/named.conf
  222. mode: 0640
  223. owner: root
  224. group: named
  225. content: |
  226. //
  227. // named.conf
  228. //
  229. // Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
  230. // server as a caching only nameserver (as a localhost DNS resolver only).
  231. //
  232. // See /usr/share/doc/bind*/sample/ for example named configuration files.
  233. //
  234. options {
  235. #listen-on port 53 { 127.0.0.1; };
  236. #listen-on-v6 port 53 { ::1; };
  237. directory "/var/named";
  238. dump-file "/var/named/data/cache_dump.db";
  239. statistics-file "/var/named/data/named_stats.txt";
  240. memstatistics-file "/var/named/data/named_mem_stats.txt";
  241. secroots-file "/var/named/data/named.secroots";
  242. recursing-file "/var/named/data/named.recursing";
  243. allow-query { localhost; 192.168.50.0/24; 172.25.250.254; };
  244. /*
  245. - If you are building an AUTHORITATIVE DNS server, do NOT enable recursion.
  246. - If you are building a RECURSIVE (caching) DNS server, you need to enable
  247. recursion.
  248. - If your recursive DNS server has a public IP address, you MUST enable access
  249. control to limit queries to your legitimate users. Failing to do so will
  250. cause your server to become part of large scale DNS amplification
  251. attacks. Implementing BCP38 within your network would greatly
  252. reduce such attack surface
  253. */
  254. recursion yes;
  255. dnssec-enable no;
  256. dnssec-validation no;
  257. managed-keys-directory "/var/named/dynamic";
  258. geoip-directory "/usr/share/GeoIP";
  259. pid-file "/run/named/named.pid";
  260. session-keyfile "/run/named/session.key";
  261. # BEGIN ANSIBLE MANAGED FORWARDERS
  262. forwarders {
  263. 172.25.250.254;
  264. };
  265. # END ANSIBLE MANAGED FORWARDERS
  266. /* https://fedoraproject.org/wiki/Changes/CryptoPolicy */
  267. include "/etc/crypto-policies/back-ends/bind.config";
  268. };
  269. logging {
  270. channel default_debug {
  271. file "data/named.run";
  272. severity dynamic;
  273. };
  274. };
  275. zone "." IN {
  276. type hint;
  277. file "named.ca";
  278. };
  279. include "/etc/named.rfc1912.zones";
  280. include "/etc/named.root.key";
  281. # BEGIN ANSIBLE MANAGED DNS ZONE for ocp4.example.com
  282. zone "ocp4.example.com" {
  283. type master;
  284. file "ocp4.example.com.db";
  285. allow-update { none; };
  286. };
  287. # END ANSIBLE MANAGED DNS ZONE for ocp4.example.com
  288. # BEGIN ANSIBLE MANAGED DNS REVERSE ZONE for ocp4.example.com
  289. zone "50.168.192.in-addr.arpa" IN {
  290. type master;
  291. file "ocp4.example.com.reverse.db";
  292. allow-update { none; };
  293. };
  294. notify: restart named
  295. - name: Ensure the forward lookup zone file has the correct content.
  296. ansible.builtin.copy:
  297. dest: /var/named/ocp4.example.com.db
  298. mode: 0644
  299. owner: root
  300. group: root
  301. content: |
  302. $TTL 1D
  303. @ IN SOA dns.ocp4.example.com. root.example.com. (
  304. 2019022400 ; serial
  305. 3h ; refresh
  306. 15 ; retry
  307. 1w ; expire
  308. 3h ; minimum
  309. )
  310. IN NS dns.ocp4.example.com.
  311. dns IN A 192.168.50.254
  312. api IN A 192.168.50.254
  313. api-int IN A 192.168.50.254
  314. registry IN A 192.168.50.50
  315. git IN A 192.168.50.50
  316. *.apps IN A 192.168.50.254
  317. master01 IN A 192.168.50.10
  318. master02 IN A 192.168.50.11
  319. master03 IN A 192.168.50.12
  320. worker01 IN A 192.168.50.13
  321. worker02 IN A 192.168.50.14
  322. idm IN A 192.168.50.40
  323. notify: restart named
  324. - name: Ensure the reverse lookup zone file has the correct content.
  325. ansible.builtin.copy:
  326. dest: /var/named/ocp4.example.com.reverse.db
  327. mode: 0644
  328. owner: root
  329. group: root
  330. content: |
  331. $TTL 1D
  332. @ IN SOA dns.ocp4.example.com. root.example.com. (
  333. 2019022400 ; serial
  334. 3h ; refresh
  335. 15 ; retry
  336. 1w ; expire
  337. 3h ; minimum
  338. )
  339. IN NS dns.ocp4.example.com.
  340. 254 IN PTR api.ocp4.example.com.
  341. 254 IN PTR api-int.ocp4.example.com.
  342. 50 IN PTR registry.ocp4.example.com.
  343. 50 IN PTR git.ocp4.example.com.
  344. 10 IN PTR master01.ocp4.example.com.
  345. 11 IN PTR master02.ocp4.example.com.
  346. 12 IN PTR master03.ocp4.example.com.
  347. 13 IN PTR worker01.ocp4.example.com.
  348. 14 IN PTR worker02.ocp4.example.com.
  349. 40 IN PTR idm.ocp4.example.com.
  350. notify: restart named
  351. - name: Ensure Bind server is enabled and running.
  352. ansible.builtin.systemd_service:
  353. name: named
  354. enabled: yes
  355. state: started
  356. handlers:
  357. - name: restart dhcpd
  358. ansible.builtin.systemd_service:
  359. name: dhcpd
  360. state: restarted
  361. - name: restart named
  362. ansible.builtin.systemd_service:
  363. name: named
  364. state: restarted
  365. - name: reload systemd
  366. ansible.builtin.systemd_service:
  367. daemon_reload: true
  368. - name: Have utility serve time.
  369. hosts: utility.lab.example.com
  370. become: no
  371. gather_subset: min
  372. tasks:
  373. - name: Ensure we have the correct chrony.conf
  374. become: yes
  375. ansible.builtin.copy:
  376. dest: /etc/chrony.conf
  377. mode: 0644
  378. content: |
  379. # Use public servers from the pool.ntp.org project.
  380. # Please consider joining the pool (http://www.pool.ntp.org/join.html).
  381. server 172.25.254.254 iburst
  382. # Record the rate at which the system clock gains/losses time.
  383. driftfile /var/lib/chrony/drift
  384. # Allow the system clock to be stepped in the first three updates
  385. # if its offset is larger than 1 second.
  386. makestep 1.0 3
  387. # Enable kernel synchronization of the real-time clock (RTC).
  388. rtcsync
  389. # Enable hardware timestamping on all interfaces that support it.
  390. #hwtimestamp *
  391. # Increase the minimum number of selectable sources required to adjust
  392. # the system clock.
  393. #minsources 2
  394. # Allow NTP client access from local network.
  395. #allow 192.168.0.0/16
  396. allow all
  397. bindcmdaddress 0.0.0.0
  398. cmdallow all
  399. # Serve time even if not synchronized to a time source.
  400. #local stratum 10
  401. # Specify file containing keys for NTP authentication.
  402. keyfile /etc/chrony.keys
  403. # Get TAI-UTC offset and leap seconds from the system tz database.
  404. leapsectz right/UTC
  405. # Specify directory for log files.
  406. logdir /var/log/chrony
  407. # Select which information is logged.
  408. #log measurements statistics tracking
  409. notify:
  410. - restart chronyd
  411. - name: Ensure firewall allows NTP.
  412. become: yes
  413. ansible.posix.firewalld:
  414. immediate: yes
  415. permanent: yes
  416. zone: "{{ item }}"
  417. service: ntp
  418. state: enabled
  419. loop:
  420. - external
  421. - public
  422. - name: Ensure firewall allows cmdport.
  423. become: yes
  424. ansible.posix.firewalld:
  425. immediate: yes
  426. permanent: yes
  427. zone: "{{ item }}"
  428. port: 323/udp
  429. state: enabled
  430. loop:
  431. - external
  432. - public
  433. handlers:
  434. - name: restart chronyd
  435. become: yes
  436. ansible.builtin.service:
  437. name: chronyd
  438. state: restarted
  439. ...