Introduction
Ava replaced an old studio laptop. Both prepared hosts still accept its key, alongside her current and backup keys. Remove only the old device authorization while preserving her account and original notes.
This independent unit assumes the user, public-key, lookup and loop practice from Give a Teammate Access. Its own training keys and existing accounts are prepared; no files or connections are inherited from the earlier VM.
Remove One Old Device Key
In this step, you will revoke Ava's old device key on both studio hosts, prove that fresh connections are denied and retain her current key, backup key and original work.
This unit has its own fresh environment. Ava already has accounts and work on both targets. Each account currently accepts three training keys: current, backup and old device. You will remove only the old grant. Removing a key blocks new authentication with it; it does not terminate an already authenticated connection. This lab tests new connections and makes no session-termination claim.
Open Controller and enter the project directory. This command also recovers from an accidental directory change:
cd /home/labex/project
Read the prepared inputs and client aliases:
cat approved-staff.json
cat clients.conf
The approved course account is studio_ava. The key to revoke is training-keys/ava-old.pub; training-keys/ava.pub and training-keys/ava-backup.pub remain authorized. Keys ending in .pub are public; corresponding private keys stay on this training client. The aliases select the host, user and exact key. -F clients.conf selects this pinned project configuration; every ssh call makes a fresh connection. Leave the inventory, client settings, host trust and training keys unchanged.
Observe the normal behavior before changing access. In the single quotes, the remote shell runs id and then cat, separated by a semicolon:
ssh -F clients.conf web1-ava 'id -un; cat work/briefing.txt'
ssh -F clients.conf web2-ava 'id -un; cat work/briefing.txt'
The identity is studio_ava on both, but the Riverside and Hill notes differ. Verify the backup key as well:
ssh -F clients.conf web1-ava-backup 'id -un'
ssh -F clients.conf web2-ava-backup 'id -un'
The old device is still accepted at this point:
ssh -F clients.conf web1-ava-old 'id -un'
ssh -F clients.conf web2-ava-old 'id -un'
Simply deleting a key from a list of keys to add does not request its removal. See that difference safely with an empty addition list. The here-document writes a whole controller file: > replaces that file, and quoted YAML prevents shell expansion. End the delimiter alone at the start of a line. 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. A loop over [], an empty YAML list, has no items to act on.
cat > additions.yml <<'YAML'
- name: Add listed keys without removing other grants
hosts: all
become: true
gather_facts: false
vars:
keys_to_add: []
tasks:
- name: Add a listed key
ansible.posix.authorized_key:
user: studio_ava
key: "{{ lookup('file', item) }}"
state: present
loop: "{{ keys_to_add }}"
YAML
ansible-playbook additions.yml
The key task is skipped on both hosts and neither changes. It did not inspect every possible authorization and remove unlisted keys. The old device can still connect:
ssh -F clients.conf web1-ava-old 'id -un'
Now express the actual revocation. group_vars/all.yml supplies this list to both inventory hosts; it does not change the prepared approval file. Each object names a course user and a controller public-key path. Repeating a malformed file block replaces that controller file; it does not require resetting the VM.
cat > group_vars/all.yml <<'YAML'
revoked_keys:
- name: studio_ava
key_file: training-keys/ava-old.pub
YAML
Use the taught key module with state: absent, which removes this key while preserving the account and its other grants. A loop processes each listed removal. Double braces resolve the current entry; lookup reads the public key on the controller. The collection is installed offline for this project. Do not use exclusive replacement, delete home directories, lock passwords or modify the platform SSH service.
cat > revoke.yml <<'YAML'
- name: Revoke only the approved old device keys
hosts: all
become: true
gather_facts: false
tasks:
- name: Remove each listed old public key
ansible.posix.authorized_key:
user: "{{ item.name }}"
key: "{{ lookup('file', item.key_file) }}"
state: absent
loop: "{{ revoked_keys }}"
YAML
Check syntax. For a YAML error, read the named line, compare indentation/quotes with the full block and rewrite only that controller file.
ansible-playbook revoke.yml --syntax-check
These accounts already exist, so supported check mode can predict the key removal. --diff would expose public-key lines in output, which are not needed for this preview. Use --check:
ansible-playbook revoke.yml --check
Both hosts should predict a change. Check mode has not revoked the real grant yet. Apply it:
ansible-playbook revoke.yml
Both hosts must finish without unreachable or failed tasks. The next two connections are expected to fail with Permission denied (publickey) and exit status 255:
ssh -F clients.conf web1-ava-old 'id -un'
ssh -F clients.conf web2-ava-old 'id -un'
A timeout, connection refusal or host-key warning does not prove revocation. The server must remain available and the allowed keys must still work. Make fresh current and backup connections to read both original files:
ssh -F clients.conf web1-ava 'id -un; cat work/briefing.txt'
ssh -F clients.conf web2-ava 'id -un; cat work/briefing.txt'
ssh -F clients.conf web1-ava-backup 'id -un; cat work/briefing.txt'
ssh -F clients.conf web2-ava-backup 'id -un; cat work/briefing.txt'
All four must succeed as studio_ava and show the corresponding original notes. Keep their bytes, owner and mode 0640; keep the account's home/work directories at 0750, .ssh at 0700 and authorized_keys at 0600. The files belong to studio_ava and its primary group. The existing key module preserves these prepared secure permissions.
Repeat the real declaration, then its prediction:
ansible-playbook revoke.yml
ansible-playbook revoke.yml --check
Both recaps should show changed=0, unreachable=0 and failed=0. Correct revocation is a selective change to actual authorization, not just a green recap or an empty controller list. If you selected the wrong key path, fix the controller data; restore a wrongly removed allowed key with the previously taught state: present, then reapply the correct targeted removal. Do not recover by clearing the whole key file or making it world-writable.
Summary
You distinguished an omitted addition from an explicit revocation, removed one public-key grant with state absent, verified actual new-connection denial and retained the current/backup logins and both original work files. An unchanged declaration converged without unnecessary changes.



