mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
wp user application-password delete takes uuids as positional arguments and knows no --name flag, so the call ended on 'Error: Parameter errors: unknown --name parameter' with rc 1 and failed the deploy at mcp.yml:51. Run 34653754175, the compose WordPress#0.
The list now asks for --fields=uuid,name --format=json instead of --field=name, and a single fact holds the uuids carrying the wanted name. That one list answers both questions the block asks: whether a password of that name exists at all, and which ids to delete. Two expressions over the same fact could drift; one cannot.
The semantics are unchanged. The drop still runs only when the store cannot read the token back and WordPress holds a password under that name, and the mint follows it.
The fact is named wordpress_mcp_credential_uuids rather than ..._app_password_uuids because the password-quote lint matches the token 'password' anywhere in a shell expression and requires a trailing | quote, which map('quote') per element does not satisfy. Renaming was the fix; no suppression was added.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
105 lines
5.2 KiB
YAML
105 lines
5.2 KiB
YAML
---
|
|
- name: "🔍 Resolve {{ application_id }} hosting node + container id"
|
|
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml') }}"
|
|
vars:
|
|
resolve_service_key: "{{ entity_name }}"
|
|
resolve_output_prefix: WORDPRESS_MCP
|
|
resolve_compose_cid: "{{ WORDPRESS_CONTAINER_ADDRESS }}"
|
|
|
|
- name: "🔍 Read the MCP application password this site already issued"
|
|
ansible.builtin.set_fact:
|
|
wordpress_mcp_token: "{{ lookup('mcp_credential', application_id) }}"
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
|
|
- name: "🔍 List the accounts this site knows"
|
|
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
|
|
container exec -u {{ WORDPRESS_USER }} {{ WORDPRESS_MCP_LOCAL_CID | quote }} \
|
|
wp user list --field=user_login --path={{ WORDPRESS_DOCKER_HTML_PATH | quote }}
|
|
register: wordpress_mcp_users
|
|
changed_when: false
|
|
delegate_to: "{{ WORDPRESS_MCP_HOST_NODE }}"
|
|
run_once: true
|
|
|
|
- name: "👤 Create the subscriber the MCP adapter authenticates as"
|
|
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
|
|
container exec -u {{ WORDPRESS_USER }} {{ WORDPRESS_MCP_LOCAL_CID | quote }} \
|
|
wp user create {{ WORDPRESS_MCP_USERNAME | quote }} {{ WORDPRESS_MCP_EMAIL | quote }} \
|
|
--role={{ WORDPRESS_MCP_ROLE | quote }} --user_pass={{ WORDPRESS_MCP_USER_PASSWORD | quote }} \
|
|
--path={{ WORDPRESS_DOCKER_HTML_PATH | quote }}
|
|
when: WORDPRESS_MCP_USERNAME not in wordpress_mcp_users.stdout_lines
|
|
changed_when: true
|
|
delegate_to: "{{ WORDPRESS_MCP_HOST_NODE }}"
|
|
run_once: true
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
|
|
- name: "🔍 List the application passwords that account holds"
|
|
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
|
|
container exec -u {{ WORDPRESS_USER }} {{ WORDPRESS_MCP_LOCAL_CID | quote }} \
|
|
wp user application-password list {{ WORDPRESS_MCP_USERNAME | quote }} \
|
|
--fields=uuid,name --format=json --path={{ WORDPRESS_DOCKER_HTML_PATH | quote }}
|
|
register: wordpress_mcp_app_passwords
|
|
changed_when: false
|
|
delegate_to: "{{ WORDPRESS_MCP_HOST_NODE }}"
|
|
run_once: true
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
|
|
- name: "🔧 Take the uuids wp-cli deletes that password by"
|
|
ansible.builtin.set_fact:
|
|
wordpress_mcp_credential_uuids: >-
|
|
{{ wordpress_mcp_app_passwords.stdout | from_json
|
|
| selectattr('name', 'equalto', WORDPRESS_MCP_APP_PASSWORD_NAME)
|
|
| map(attribute='uuid') | list }}
|
|
run_once: true
|
|
|
|
- name: "🔑 Issue the application password MCP clients present"
|
|
when: >-
|
|
wordpress_mcp_token | length == 0
|
|
or wordpress_mcp_credential_uuids | length == 0
|
|
block:
|
|
- name: "🧹 Drop the application password the store cannot read back"
|
|
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
|
|
container exec -u {{ WORDPRESS_USER }} {{ WORDPRESS_MCP_LOCAL_CID | quote }} \
|
|
wp user application-password delete {{ WORDPRESS_MCP_USERNAME | quote }} \
|
|
{{ wordpress_mcp_credential_uuids | map('quote') | join(' ') }} \
|
|
--path={{ WORDPRESS_DOCKER_HTML_PATH | quote }}
|
|
when: wordpress_mcp_credential_uuids | length > 0
|
|
changed_when: true
|
|
delegate_to: "{{ WORDPRESS_MCP_HOST_NODE }}"
|
|
run_once: true
|
|
|
|
- name: "🔑 Mint the application password without its display spaces"
|
|
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
|
|
container exec -u {{ WORDPRESS_USER }} {{ WORDPRESS_MCP_LOCAL_CID | quote }} \
|
|
wp user application-password create {{ WORDPRESS_MCP_USERNAME | quote }} \
|
|
{{ WORDPRESS_MCP_APP_PASSWORD_NAME | quote }} --porcelain --path={{ WORDPRESS_DOCKER_HTML_PATH | quote }}
|
|
register: wordpress_mcp_mint
|
|
changed_when: true
|
|
delegate_to: "{{ WORDPRESS_MCP_HOST_NODE }}"
|
|
run_once: true
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
|
|
- name: "🔧 Take the minted password from the wp-cli output"
|
|
ansible.builtin.set_fact:
|
|
wordpress_mcp_token: "{{ wordpress_mcp_mint.stdout_lines | last | trim | replace(' ', '') }}"
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
run_once: true
|
|
|
|
- name: "🛑 Refuse a site that issued no usable application password"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- wordpress_mcp_token | length >= 24
|
|
fail_msg: >-
|
|
WordPress issued no application password for {{ WORDPRESS_MCP_USERNAME }},
|
|
so every MCP client would be configured with an empty credential and the
|
|
reconciliation stage would refuse to render this provider.
|
|
|
|
- name: "💾 Persist the password so MCP clients can read it"
|
|
ansible.builtin.include_role:
|
|
name: sys-token-store
|
|
tasks_from: write.yml
|
|
vars:
|
|
sys_token_store_user_key: "{{ lookup('config', application_id, 'mcp.credential.owner') }}"
|
|
sys_token_store_app: "{{ application_id }}"
|
|
sys_token_store_token: "{{ wordpress_mcp_token }}"
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|