Give a Teammate Access

AnsibleBeginner
Practice Now

Introduction

Small Frame Studio needs Ava and Noah to work on two prepared hosts while Maya keeps her existing access and notes. You will create scoped staff accounts with Ansible, use their real SSH identities and turn one person’s declaration into a reusable staff list.

You need basic terminal navigation and prior practice reading a small Ansible inventory and playbook. This lab explains the required Linux account, SSH key, permission, lookup and loop concepts before use. All training keys, trusted host identities and the separate collection are prepared for you.

Give Ava a Working Login

In this step, you will create one course account on two studio hosts, authorize its training key and use that identity to save a real work file.

Small Frame Studio stores different editorial notes on web1 and web2. Maya already works on both hosts; Ava needs access without losing Maya's work. The prepared targets are two SSH containers sharing the training VM kernel, not independent VMs. Ansible runs on the controller and connects as the prepared automation account. Staff accounts live only inside these course targets.

Open Controller. It starts in the project directory. This command also returns you there if you enter the wrong directory:

cd /home/labex/project

Read the inventory and the approved staff data with cat, which displays files without changing them:

cat inventory.ini
cat approved-staff.json

The final staff list contains studio_ava and studio_noah for both hosts. We will start with Ava, then extend the same work to a list. Keep these approved inputs, the inventory and prepared SSH settings unchanged.

SSH authenticates a user on a remote host. A public key may be installed on the target; its matching private key stays with the client and proves possession. Here, training-keys/ contains fresh disposable training keys, never real personal credentials. Files ending in .pub are public; the matching filename without that suffix is private. Do not copy private keys to the targets or print their contents.

Read the prepared client configuration:

cat clients.conf

Each alias, such as web1-maya, selects a target, course username and private key. -F clients.conf tells ssh to use this project configuration. It pins trusted host keys, disables password prompts and connection reuse, and offers only the selected identity. The target host key identifies the server; the user's public key authorizes the person. They solve different problems. Do not disable either trust check.

First observe a working staff identity. Text in single quotes is a command executed on the remote host. The semicolon separates two commands there: identify the user, then read the local work file.

ssh -F clients.conf web1-maya 'id -un; cat work/briefing.txt'

It prints studio_maya and the Riverside studio notes. Read the other host with a new connection:

ssh -F clients.conf web2-maya 'id -un; cat work/briefing.txt'

This prints the same identity but different Hill studio notes. Preserve both files. Ava's connection is expected to fail now because her account and grant do not exist:

ssh -F clients.conf web1-ava 'id -un'

Expect Permission denied (publickey) and exit status 255, not a password prompt. A connection timeout or host-key warning is a different problem: return to this project directory and check the unchanged inventory/configuration, rather than weakening SSH trust.

A Linux user has a home, login shell and primary group. The user module creates the account and, on these prepared targets, its same-named primary group. create_home: true creates its home. become: true uses the prepared automation account's authorized privilege escalation on the targets; it does not change the LabEx login.

Write a complete playbook. In this here-document, > replaces the named controller file and the quoted YAML delimiter prevents shell expansion. Put its closing delimiter alone at the start of a line. If you mistype a block, repeat the whole block to replace that file. If the shell is still waiting for a missing closing delimiter, press Ctrl+C to cancel the unfinished input, then rerun the complete block from the normal prompt. YAML uses spaces for nesting and - for list entries.

cat > access.yml <<'YAML'
- name: Give Ava access to the studio hosts
  hosts: all
  become: true
  gather_facts: false
  tasks:
    - name: Create Ava's course account
      ansible.builtin.user:
        name: studio_ava
        shell: /bin/bash
        create_home: true
    - name: Keep Ava's home private
      ansible.builtin.file:
        path: /home/studio_ava
        state: directory
        owner: studio_ava
        group: studio_ava
        mode: '0750'
    - name: Create Ava's work directory
      ansible.builtin.file:
        path: /home/studio_ava/work
        state: directory
        owner: studio_ava
        group: studio_ava
        mode: '0750'
    - name: Authorize Ava's public key
      ansible.posix.authorized_key:
        user: studio_ava
        key: "{{ lookup('file', 'training-keys/ava.pub') }}"
        state: present
YAML

The file module manages directory ownership and permissions, not the contents of existing work files. Mode 0750 gives the owner full access, the primary group read/traverse access, and other users no access; quoted digits avoid YAML number interpretation. The lookup reads a public key from the controller. Double braces evaluate that expression. state: present adds that public key while retaining other keys; it does not replace the account's whole key file.

Modules beginning with ansible.builtin ship with Core. ansible.posix.authorized_key comes from a separate collection, installed offline for this project. It creates the user's .ssh directory with mode 0700 and authorized_keys with mode 0600, owned by that user and its primary group. You do not need to install a collection or edit SSH server settings.

Check YAML/playbook syntax first:

ansible-playbook access.yml --syntax-check

For a syntax error, read the reported file/line, compare spaces and quotes with the complete block, rewrite it and repeat. A VM reset is unnecessary. A check-mode preview is not reliable for this initial creation: a later key task cannot use an account that an earlier task only predicted creating. Apply the declared creation, then use check mode after the accounts exist:

ansible-playbook access.yml

Both hosts must finish with no unreachable or failed tasks. Now log in as Ava and save her actual identity. Inside the remote quotes, umask 027 restricts newly created files to at most 0640; > writes id's output to the arrival file; cat reads it back. The setting applies to this remote command's shell only.

ssh -F clients.conf web1-ava 'umask 027; id -un > work/arrival.txt; cat work/arrival.txt'
ssh -F clients.conf web2-ava 'umask 027; id -un > work/arrival.txt; cat work/arrival.txt'

Both print studio_ava. You used a real staff login and file write, not merely a successful Ansible recap. Maya's separate files and login must still work.

Reuse the Declaration for a Staff List

In this step, you will replace repeated account details with structured data and a loop, add Noah and confirm convergence without changing existing work.

Stay in Controller at /home/labex/project. Each staff entry needs a course username and the controller path of its public key. The prepared group_vars directory is empty. A file named group_vars/all.yml supplies variables to every inventory host. Write the approved two-person list:

cat > group_vars/all.yml <<'YAML'
staff:
  - name: studio_ava
    key_file: training-keys/ava.pub
  - name: studio_noah
    key_file: training-keys/noah.pub
YAML

A matching host_vars/web1.yml can instead supply a list for web1; host values override shared group defaults. This unit needs the same list on both hosts, while the challenge supplies different approved host lists. The reusable tasks do not need to change.

A loop repeats a task for each entry in staff. item.name reads the current entry's name, and item.key_file reads its public-key path. The same list drives account creation, directories and grants. Replace the single-person playbook with the reusable version:

cat > access.yml <<'YAML'
- name: Manage the approved studio staff
  hosts: all
  become: true
  gather_facts: false
  tasks:
    - name: Create each course account
      ansible.builtin.user:
        name: "{{ item.name }}"
        shell: /bin/bash
        create_home: true
      loop: "{{ staff }}"
    - name: Keep each home private
      ansible.builtin.file:
        path: "/home/{{ item.name }}"
        state: directory
        owner: "{{ item.name }}"
        group: "{{ item.name }}"
        mode: '0750'
      loop: "{{ staff }}"
    - name: Create each work directory
      ansible.builtin.file:
        path: "/home/{{ item.name }}/work"
        state: directory
        owner: "{{ item.name }}"
        group: "{{ item.name }}"
        mode: '0750'
      loop: "{{ staff }}"
    - name: Add each approved public key
      ansible.posix.authorized_key:
        user: "{{ item.name }}"
        key: "{{ lookup('file', item.key_file) }}"
        state: present
      loop: "{{ staff }}"
YAML

Keep the default nonexclusive key behavior. An exclusive key operation in a loop can remove another legitimate key from a previous iteration. This project adds specified keys and later removes a specified old key; it does not replace everyone's complete key set.

Check syntax, then apply. Noah does not exist yet, so the initial-creation check-mode limitation still applies.

ansible-playbook access.yml --syntax-check
ansible-playbook access.yml

Ava should need no new changes, while Noah is created on both targets. Read the per-item results and confirm both recaps have unreachable=0 and failed=0. Use Noah's actual identity to create his own work file:

ssh -F clients.conf web1-noah 'umask 027; id -un > work/arrival.txt; cat work/arrival.txt'
ssh -F clients.conf web2-noah 'umask 027; id -un > work/arrival.txt; cat work/arrival.txt'

Both print studio_noah. Ava's arrival files remain her own; Maya's notes remain unchanged. Read them through their original identities:

ssh -F clients.conf web1-ava 'cat work/arrival.txt'
ssh -F clients.conf web2-ava 'cat work/arrival.txt'
ssh -F clients.conf web1-maya 'cat work/briefing.txt'
ssh -F clients.conf web2-maya 'cat work/briefing.txt'

Now all required users exist. --check predicts supported changes without applying them:

ansible-playbook access.yml --check

Both recaps should report changed=0, unreachable=0 and failed=0. Repeat the real declaration:

ansible-playbook access.yml

It should also report no changes. This is idempotency for the declared accounts, directories and key grants; ordinary SSH access may still write system login logs. The same loop can consume another approved staff list without rewriting its implementation.

If a login fails, read which alias and identity you selected, then compare your public-key path and username with the approved data. Correct the controller declaration and apply it again. Do not fix a missing grant by copying a private key to a target, making a home world-writable or disabling server trust.

Summary

You created course accounts, private work directories and public-key grants; used real SSH identities to save work; replaced one-person repetition with a structured staff loop; and confirmed convergence while retaining existing staff access and notes.