hasResource($run, 'nc_admin')) { return StepResult::advance(); } $instance = $this->instance($run); $pve = $this->pve->forHost($instance->host); $node = (string) $run->context('node'); $vmid = (int) $run->context('vmid'); $username = 'admin'; $password = Str::random(20); // Retry-safe: if a prior crashed attempt already created the user, reset // its password instead of re-running user:add (which Nextcloud rejects). $exists = (int) ($pve->guestExec($node, $vmid, NextcloudOcc::command('user:info '.escapeshellarg($username)))['exitcode'] ?? 1) === 0; $action = $exists ? 'user:resetpassword --password-from-env '.escapeshellarg($username) : 'user:add --password-from-env --group=admin '.escapeshellarg($username); // The password travels as OC_PASS rather than as an argument, so it never // shows up in the guest's process list — see NextcloudOcc::command(). $this->guest($pve, $run, NextcloudOcc::command($action, ['OC_PASS' => $password])); // Persist only the username (encrypted ref); hand the password to step 15 // encrypted-in-context for delivery, then it is scrubbed. Never plaintext. $instance->update(['nc_admin_ref' => $username]); $run->mergeContext(['admin_password' => Crypt::encryptString($password)]); $this->recordResource($run, $instance->host, 'nc_admin', $username); return StepResult::advance(); } }