Run Tasks As Another User

on this page

Use this guide when you need tasks to execute under a specific user identity — for example, configuring a kiosk account’s profile, writing to a user’s APPDATA, or running a command that checks user-scoped registry values.

Prerequisites

  • A working playbook and target connection
  • The account used for your transport (WinRM or SSH) must have permission to create local users and to execute commands as other users
  • For Windows become: the target user must have a password

Overview

The typical pattern has two phases:

  1. Create the user account using the user module, running as your transport account.
  2. Add become to tasks that must run in that user’s context.

1. Store The Password As A Secret

Avoid hardcoding passwords. Use a named secret reference instead:

bash
preflight secret encrypt exhibit-password

See Manage secrets if you have not set up age secrets yet.

2. Provision The User Account

Run a task as your normal transport account to create the user:

yaml
tasks:
  - name: Create exhibit user
    user:
      name: exhibit
      password: secret:exhibit-password
      groups:
        - Users
      ensure: present

This task runs as the account your WinRM or SSH transport authenticates with — it does not need become.

3. Configure Autologin (Optional)

If this is a kiosk that should boot directly into the exhibit account:

yaml
  - name: Configure autologin
    uses: preflight/autologin
    with:
      username: exhibit
      password: secret:exhibit-password

4. Run Tasks As The New User

Add a become block to any task that must execute under the exhibit account’s identity. On Windows, become uses runas by default:

yaml
  - name: Set user environment variable
    become:
      user: exhibit
      password: secret:exhibit-password
    environment:
      name: EXHIBIT_MODE
      value: "kiosk"
      scope: user
      ensure: present

  - name: Write user-scoped registry value
    become:
      user: exhibit
      password: secret:exhibit-password
    registry:
      path: 'HKCU:\Software\ExhibitApp'
      values:
        StartFullscreen: "1"
      ensure: present

become is task execution metadata — it does not change the module’s params, only the identity the task runs under.

This is also the recommended way to apply current-user stdlib actions to a kiosk or exhibit account when the action does not expose a user input, or when you need every task in the action to run under that Windows identity.

For preflight/windows-shell, preflight/windows-input, preflight/windows-power, and preflight/debloat, you can set with.user when you only need the action’s supported user-scoped registry settings and do not want to switch the task process identity. The target user’s profile hive must already be loaded, such as while that user is signed in or by running with become.load_profile.

yaml
  - name: Configure supported shell defaults for exhibit user
    uses: preflight/windows-shell
    with:
      user: exhibit
      theme_mode: dark
      taskbar_auto_hide: true

Some shell-facing changes persist immediately in the user’s profile but only become visible after sign-out, Explorer restart, or reboot.

5. Use Playbook Defaults To Avoid Repetition

When most tasks in a playbook should run as the exhibit user, set defaults.become at the playbook level:

yaml
name: configure-exhibit-user

defaults:
  become:
    user: exhibit
    password: secret:exhibit-password

tasks:
  - name: Set environment variable
    environment:
      name: EXHIBIT_MODE
      value: "kiosk"
      scope: user
      ensure: present

  - name: Write registry value
    registry:
      path: 'HKCU:\Software\ExhibitApp'
      values:
        StartFullscreen: "1"
      ensure: present

  - name: Create exhibit user
    become:
      enabled: false
    user:
      name: exhibit
      password: secret:exhibit-password
      ensure: present

The last task uses enabled: false to run as the transport account instead of inheriting the default become.

6. Load The User Profile

By default, Windows become does not load the user’s full profile (environment variables, APPDATA, USERPROFILE, etc.). Set load_profile: true when the task needs those values:

yaml
  - name: Configure user shell settings
    become:
      user: exhibit
      password: secret:exhibit-password
      load_profile: true
    powershell:
      script: |
        $appData = $env:APPDATA
        New-Item -ItemType Directory -Path "$appData\ExhibitApp" -Force | Out-Null

Troubleshooting

become: password is required for Windows user

Windows runas requires a password. Make sure password is set and the secret is configured in the project:

bash
preflight secret list

If the secret exists but holds the wrong value, update it with preflight secret edit exhibit-password.

become is not taking effect

Check that the task does not have enabled: false and that it is not overriding an inherited default with an empty block. A become: key without user: is an error.

POSIX: Become To Root From An Unprivileged SSH User

On POSIX targets the recommended posture is the reverse of Windows: connect over SSH as an unprivileged user and escalate per task with become. Allowing root SSH login is a stated working alternative (it passes the requires_root check with no become), but an unprivileged session plus become is the default recommendation.

A bare become: {enabled: true} means root on POSIX — you do not need to name user: root.

Password-first: the host-var sudo password pattern

The documented primary path supplies become.password from a secret: reference. Put the become default in the playbook and the per-host sudo password in ordinary host/group vars — Preflight resolves secret: refs in become.password right before execution:

yaml
# playbooks/manage-hosts.yml
name: manage-hosts

defaults:
  become:
    enabled: true            # bare become means root on POSIX
    password: "{{ sudo_password }}"

tasks:
  - name: ensure a package is present
    system_package:
      packages:
        - name: htop
          ensure: present
yaml
# preflight.yml — the sudo password is a per-host secret
inventory:
  hosts:
    - name: web-01
      address: [IP_ADDRESS]
      transport: ssh
      username: deploy              # unprivileged SSH user
      password: secret:deploy-password
      vars:
        sudo_password: secret:deploy-sudo-password

The same shape works at the group level — put sudo_password in the group’s vars: and every host in the group inherits it.

NOPASSWD fallback

If a host’s sudoers is configured with NOPASSWD for the SSH user, omit become.password entirely. Preflight runs sudo -n, which fails fast with a sudo-password-required reason code if a password turns out to be required, so a misconfigured NOPASSWD never hangs the run.

requires_root modules

service, user, system_package, and reboot require an effective root user. Preflight probes id -u once per target and fails such a task before Check() with a requires-root-violation reason code when the effective user is not root. Setting become: {enabled: true} (root) or connecting as root satisfies the check; become-to-a-non-root-user is caught by the same check.