How should I initialize a new local account for a Windows service?

I'm using Terraform with Google Cloud to create Windows Server Core 2019 hosts for use with Jenkins. I want to install some software like Git, OpenJDK, and others using Scoop, and start the Jenkins Agent JAR as a system service running under a local jenkins user.

I'm setting them up using PowerShell scripts provided via metadata keys like sysprep-specialize-script-ps1 and windows-startup-script-ps1:

https://cloud.google.com/compute/docs/startupscript#providing_a_startup_script_for_windows_instances

My problem is that I'm creating local users using Powershell like so:

New-LocalUser -Name $Username `
    -Description $Description `
    -Password $Password `
    -PasswordNeverExpires `
    -AccountNeverExpires

And I create a Jenkins Agent service using Windows Service Wrapper: https://github.com/winsw/winsw

But when I try to run it without logging into the Jenkins user first I get back:

2020-08-26 14:49:57,760 INFO  - Starting the service with id 'JenkinsAgent'
2020-08-26 14:49:57,823 FATAL - WMI Operation failure: ServiceLogonFailure
WMI.WmiException: ServiceLogonFailure
   at WMI.WmiRoot.BaseHandler.CheckError(ManagementBaseObject result)
   at WMI.WmiRoot.InstanceHandler.Invoke(Object proxy, MethodInfo method, Object[] arguments)
   at WinSW.Program.<Run>g__Start|2_2(<>c__DisplayClass2_0& )
   at WinSW.Program.Run(String[] argsArray, IWinSWConfiguration descriptor)
   at WinSW.Program.Main(String[] args)

This issue does not happen if I first log into the jenkins user using RDP, which creates the user profile on first login. I assume that's what allows the service to start correctly.

I'm trying to figure out how I can create a user and their profile without having to manually log into that user so the service can start correctly. The setup and startup PS scripts are running as SYSTEM user, which might be the reason why using something like Start-Process, Invoke-Command, or Start-Job with -Credential flag to run a command as jenkins user to let its profile be created fails.

I've had issues trying to run a command or just exit as jenkins user like so:

[localhost] The background process reported an error with the following message: .
    + CategoryInfo          : OpenError: (localhost:String) [], PSRemotingTransportException
    + FullyQualifiedErrorId : 2100,PSSessionStateBroken

What am I doing wrong?

Is there some better way of achieving what I'm trying to do?

How can I create a local user with its profile so that the service I create starts?


Based on replies from a linked question there are three ways (that look most promising):

  1. Use CreateProfile API

  2. Use Powershell As described in this answer - you will find example on GitHub - in this case you will be able to define home directory path. Part of code which defines profile path:

    $methodName = 'UserEnvCP'
    $script:nativeMethods = @();

    Register-NativeMethod "userenv.dll" "int CreateProfile([MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,`
      [MarshalAs(UnmanagedType.LPWStr)] string pszUserName,`
      [Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath, uint cchProfilePath)";

    Add-NativeMethods -typeName $MethodName;

    $localUser = New-Object System.Security.Principal.NTAccount("$UserName");
    $userSID = $localUser.Translate([System.Security.Principal.SecurityIdentifier]);
    $sb = new-object System.Text.StringBuilder(260);
    $pathLen = $sb.Capacity;
 
    Write-Verbose "Creating user profile for $Username";
    try
    {
        [UserEnvCP]::CreateProfile($userSID.Value, $Username, $sb, $pathLen) | Out-Null;
    }
    catch
    {
        Write-Error $_.Exception.Message;
        break;
    }
  1. Use psexec utility This looks like a straightforwad method but I'm not sure If it will Ansible. psexec.exe -u foobar -p Abcd123! cmd.exe /c exit Here's more info about psexec tool.