100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Infrastructure as Code — Terraform & Ansible
25 minintermediate

Core Modules — copy, template, service, package, user and file

Ansible's built-in modules are the vocabulary of configuration management — they are the named, idempotent, documented operations that playbooks compose to achieve desired system state. Where shell scripts string together raw commands that may or may not be idempotent, modules encode idempotency into their implementation: the 'ansible.builtin.package' module checks whether a package is already installed before attempting installation, the 'ansible.builtin.user' module checks whether a user already exists before creating them, and the 'ansible.builtin.service' module checks current service state before starting or stopping. This idempotency is not magic — it is a design contract that every module author must implement. Understanding the six most-used modules (file, copy, template, package, service, user) covers the vast majority of day-to-day configuration management tasks. Mastering their parameters and idempotency guarantees enables writing reliable playbooks that can be re-run safely in production.

Analogy🏏Cricket
🏏 Think of it like cricket: Terraform Cloud is the ICC's centralised match management platform — instead of each national board (team) maintaining its own scoring system, umpire assignment software and results database (self-managed CI/CD + S3 backend), the ICC platform handles all of this centrally. When a board member proposes a rule change (pull request), the platform automatically simulates the match under the new rules (speculative plan on PR), shows the referees the impact (plan output in PR comment), and requires the match committee to approve (policy gates) before the rule takes effect. The audit log records every change, every approval, and who made each decision — providing the governance and traceability that serious tournament operations require.

The Six Core Modules

'ansible.builtin.file' manages files, directories and symbolic links: sets path, state (file/directory/link/absent/touch), owner, group, mode, and recurse. It is the primary module for creating directory structures and setting permissions. 'ansible.builtin.copy' copies files from the Ansible control node to managed hosts, or from one location to another on the managed host. Key parameters: src (source file or directory), dest (destination path), content (inline string content, instead of src), owner, group, mode, backup (preserve previous version with timestamp). 'ansible.builtin.template' processes a Jinja2 template from the control node and writes the rendered result to the managed host — variables from inventory, playbook vars, and facts are available in the template. It is the primary module for configuration files that vary per host or environment. 'ansible.builtin.package' is a distribution-agnostic package manager that uses yum, dnf, apt, zypper or the appropriate manager for the target OS, detected from facts. 'ansible.builtin.service' manages system services via systemd, init, upstart or the detected init system. 'ansible.builtin.user' manages UNIX user accounts and group memberships. Together these six modules handle the majority of OS-level configuration tasks in any automation environment.

Analogy🏏Cricket
🏏 Think of it like cricket: The six core modules divide the work of standing up a server exactly the way six specialist staff divide the work of preparing a venue. 'file' is the groundskeeper — makes sure the physical structures exist with the right ownership and access (directories created, permissions set, symlinks laid like marked pitch boundaries). 'copy' is the equipment manager delivering standard-issue kit unchanged from the store room (static files shipped byte-for-byte from the control node). 'template' is the sign-writer who takes the standard scoreboard layout and fills in today's teams and venue before hanging it (a .j2 file rendered with host-specific variables, then deployed). 'package' is the procurement officer who works with whatever supplier the region uses — same requisition form whether the local supplier is apt, yum or dnf (distribution-agnostic package installation). 'service' is the operations manager who ensures the floodlights are on now AND wired to come on automatically for every future fixture (state: started for now, enabled: true for boot). And 'user' is the accreditation office issuing identities and group memberships (accounts, groups, SSH keys). The reason to learn the specialists rather than doing everything with 'shell' commands is the same reason venues employ specialists rather than one person with a toolbox: each specialist knows how to check whether their piece of the work is ALREADY done and skip it — which is the idempotency that makes re-running the whole preparation safe.
yaml
# Core modules reference — Cricket Analytics Platform configuration
---
- name: Core modules demonstration
  hosts: api_servers
  become: true
  vars:
    app_user: cricket-app
    app_dir: /opt/cricket-analytics
    nginx_version: '1.24.*'

  tasks:
    # ── ansible.builtin.file — manage files, dirs, symlinks ───────────────────
    - name: Create application directory structure
      ansible.builtin.file:
        path: '{{ item }}'
        state: directory
        owner: '{{ app_user }}'
        group: '{{ app_user }}'
        mode: '0750'
      loop:
        - '{{ app_dir }}'
        - '{{ app_dir }}/logs'
        - '{{ app_dir }}/config'
        - '{{ app_dir }}/data'
      # Idempotent: directories already existing are left unchanged

    - name: Create symlink for application logs
      ansible.builtin.file:
        src: '{{ app_dir }}/logs'
        dest: /var/log/cricket-analytics
        state: link
        force: true

    - name: Remove obsolete temporary directory
      ansible.builtin.file:
        path: /tmp/cricket-old-deploy
        state: absent  # Deletes if exists, no-op if not present

    # ── ansible.builtin.copy — copy files from control node ───────────────────
    - name: Copy static application configuration
      ansible.builtin.copy:
        src: files/cricket-api-config.json  # Relative to playbook directory
        dest: '{{ app_dir }}/config/config.json'
        owner: '{{ app_user }}'
        group: '{{ app_user }}'
        mode: '0640'
        backup: true  # Keep previous version as .json.YYYYMMDD-HHMMSS

    - name: Write inline configuration file
      ansible.builtin.copy:
        content: |
          # Cricket Analytics API — generated by Ansible
          # Host: {{ inventory_hostname }}
          # Environment: {{ environment }}
          [api]
          workers = {{ ansible_processor_vcpus * 2 }}
          bind = 0.0.0.0:8080
          timeout = 60
        dest: '{{ app_dir }}/config/gunicorn.conf'
        owner: '{{ app_user }}'
        mode: '0640'
      notify: Restart Cricket API  # Handler triggered if content changes

    # ── ansible.builtin.template — Jinja2-rendered configuration ─────────────
    - name: Deploy Nginx virtual host configuration
      ansible.builtin.template:
        src: templates/nginx-vhost.conf.j2
        dest: '/etc/nginx/conf.d/cricket-api.conf'
        owner: root
        group: root
        mode: '0644'
        validate: 'nginx -t -c /etc/nginx/nginx.conf'
      notify: Reload Nginx

    # ── ansible.builtin.package — distribution-agnostic package management ────
    - name: Install required packages
      ansible.builtin.package:
        name:
          - nginx
          - python3
          - python3-pip
          - htop
          - vim
        state: present
      # package module detects apt vs yum vs dnf automatically from facts

    - name: Ensure specific Nginx version
      ansible.builtin.package:
        name: 'nginx={{ nginx_version }}'
        state: present
      when: ansible_os_family == 'Debian'  # Debian-style version pinning

    # ── ansible.builtin.service — service state management ───────────────────
    - name: Enable and start Nginx
      ansible.builtin.service:
        name: nginx
        enabled: true   # Enable at boot
        state: started  # Ensure running now
      # Idempotent: already-running services are left running

    - name: Ensure cricket-api service is running
      ansible.builtin.service:
        name: cricket-api
        enabled: true
        state: started
      failed_when: false  # Do not fail if service is not installed yet

    # ── ansible.builtin.user — user account management ────────────────────────
    - name: Create application service user
      ansible.builtin.user:
        name: '{{ app_user }}'
        comment: 'Cricket Analytics Application User'
        system: true          # System user (lower UID range)
        shell: /sbin/nologin  # No interactive login
        home: '{{ app_dir }}'
        create_home: false    # Home already created by file module above
        state: present

    - name: Add deploy user to cricket-app group
      ansible.builtin.user:
        name: cricket-deploy
        groups: '{{ app_user }}'
        append: true  # Add to group without removing from existing groups

    - name: Set authorised SSH keys for deploy user
      ansible.posix.authorized_key:
        user: cricket-deploy
        key: '{{ lookup("file", "files/deploy_key.pub") }}'
        state: present
        exclusive: false  # Do not remove other existing keys
Lesson 17 of 33
0% complete