This blog post uses the Accounts configuration service provider (CSP), to create a local user account on Windows 10 devices. This area was added in Windows 10, version 1803, which is currently available as Insider Preview build.
This week is all about creating local user accounts via Windows 10 MDM. That can for example make life a bit easier with troubleshooting an offline device. A fallback account. In this post I’ll show how this can be achieved by using the Accounts CSP. I’ll show the available nodes and I’ll show how to configure them. I’ll end this post by showing the end-user experience. Also, spoiler alert, it’s good to note that this is not a pretty administrator experience at this moment, but I’m pretty sure that will be fixed when it’s a built-in configuration in Microsoft Intune.
Overview
Let’s start by having a look at the tree of the Accounts CSP.
Available nodes
The Accounts CSP contains nodes for renaming a computer account and for the creation of a user account. To get a better understanding of the different nodes, it’s good to walk through the available nodes. Specifically those related to user accounts, as those are the subject of this post. Let’s go through those related nodes.
- .Device/Vendor/MSFT/Account – Defines the root node for the Accounts CSP;
- Users – Defines the interior node for the user account information;
- [UserName] – Defines the username of the new local user account;
- Password – Defines the password for the new local user account;
- LocalUserGroup – Defines the local user group for the new local user account.
Configurable nodes
There are basically two configurable nodes related to the creation of a local user account. The Password node and the LocalUserGroup node. The [UserName] node should contain the username and can be anything. The table below provides an overview of the configurable nodes.
Node | Value | Description |
Password |
String |
This required setting allows the administrator to set the password for the new local administrator account. |
LocalUserGroup | Integer 1 – (Default) Users 2 – Administrators |
This optional setting allows the administrator to control the local user group of the new local administrator account. |
Note: The password value can be any valid string and is visible as plaintext in the Azure portal.
Configure
Now let’s continue by having a look at the required and optional configuration to create a local user account on the device. In other words, create a device configuration profile with the previously mentioned custom OMA-URI settings. The following three steps walk through the creation of that device configuration profile. After that simply assign the created profile to a user or device group.
1 | Open the Azure portal and navigate to Intune > Device configuration > Profiles; |
2 | On the Devices configuration – Profiles blade, click Create profile to open the Create profile blade; |
3a |
On the Create profile blade, provide the following information and click Create;
|
3b |
On the Custom OMA-URI Settings blade, provide the following information and click Add to open the Add row blade. On the Add row blade, provide the following information and click OK;
|
3c |
On the Custom OMA-URI Settings blade, provide the following information and click Add to open the Add row blade. On the Add row blade, provide the following information and click OK (and click OK in the Custom OMA-URI blade);
|
Note: At some point in time this configuration will probably become available in the Azure portal without the requirement of creating a custom OMA-URI.
End-user experience
Let’s end this post by having a quick look at the end-user experience. There’s actually not that much to be shown. Only the created account. Below on the left is a screenshot of the default configuration of the created user account, including the full name, and below on the right is a screenshot of the group memberships of the created user account.
Note: The reporting in the Azure portal still provides me with a remediation failed error message, while the actual account creation was a success.
More information
For more information about the Accounts CSP, refer to this article named Accounts CSP.
So far from the documentation the only supported operating is Add. How to delete the local admin user after some time?
Hi Aaron,
Removing the user can not be achieved with this CSP. Probably the easiest would be a small script.
Regards, Peter
I use this little gem of a PoSH script. I find a lot of people forget the ADSI command still works locally.
This script will create a user called ‘Local User’, assign it to the local Administrator group, set the password
that you define and to never expire. I simply update the password once a month and push it out via Intune using he native PoSHh scipt option.
This script will look for any built-in accounts (e.g. service accounts, SQL) and automaticly excluded them. Bulit-in accouonts end in ‘-500’, but you can excluded your own as long as you know the name. I left a few ‘ElseIf’ statements to referance those. You can also exlcuded users in certain local groups (see bottom $remove). By default the script will disable and randomize the password of any account not exlcuded. If you want to delete accounts that are not exlcuded, remove the ” from line 152 and 156. Enjoy!
param(
[string]$ComputerName = “$env:computername”
)
##: Feedback
Write-Output “$ComputerName ——————————————————————–`n”
# http://msdn.microsoft.com/en-us/library/aa772300(VS.85).aspx
$ADS_UF_SCRIPT = 1 # 0x1
$ADS_UF_ACCOUNTDISABLE = 2 # 0x2
$ADS_UF_HOMEDIR_REQUIRED = 8 # 0x8
$ADS_UF_LOCKOUT = 16 # 0x10
$ADS_UF_PASSWD_NOTREQD = 32 # 0x20
$ADS_UF_PASSWD_CANT_CHANGE = 64 # 0x40
$ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128 # 0x80
$ADS_UF_TEMP_DUPLICATE_ACCOUNT = 256 # 0x100
$ADS_UF_NORMAL_ACCOUNT = 512 # 0x200
$ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 2048 # 0x800
$ADS_UF_WORKSTATION_TRUST_ACCOUNT = 4096 # 0x1000
$ADS_UF_SERVER_TRUST_ACCOUNT = 8192 # 0x2000
$ADS_UF_DONT_EXPIRE_PASSWD = 65536 # 0x10000
$ADS_UF_MNS_LOGON_ACCOUNT = 131072 # 0x20000
$ADS_UF_SMARTCARD_REQUIRED = 262144 # 0x40000
$ADS_UF_TRUSTED_FOR_DELEGATION = 524288 # 0x80000
$ADS_UF_NOT_DELEGATED = 1048576 # 0x100000
$ADS_UF_USE_DES_KEY_ONLY = 2097152 # 0x200000
$ADS_UF_DONT_REQUIRE_PREAUTH = 4194304 # 0x400000
$ADS_UF_PASSWORD_EXPIRED = 8388608 # 0x800000
$ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216 # 0x1000000
Function AddUserFlag()
{
$userName = $args[0]
$flag = $args[1]
$u = [adsi]”WinNT://$ComputerName/$userName,user”
$u.invokeSet(“userFlags”, ($u.userFlags[0] -BOR $flag))
$u.commitChanges()
}
Function RemoveUserFlag()
{
$userName = $args[0]
$flag = $args[1]
$u = [adsi]”WinNT://$ComputerName/$userName,user”
if ($u.UserFlags[0] -BAND $flag)
{
$u.invokeSet(“userFlags”, ($u.userFlags[0] -BXOR $flag))
$u.commitChanges()
}
}
Function GetRandomString()
{
[int]$Length = $args[0]
$set = ‘~!@#$%^&*-+=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789’.ToCharArray()
$result = “”
for ($x = 0; $x -lt $Length; $x++) {
$result += $set | Get-Random
}
return $result
}
##: Get local user list
$computer = [ADSI]”WinNT://$ComputerName,computer”
$Users = $computer.psbase.Children | Where-Object {$_.psbase.schemaclassname -eq ‘user’}
##: Iterate
foreach ($user in $Users.psbase.syncroot)
{
If ($user.name -eq “Administrator”)
{
$randpass = GetRandomString(20)
Write-Output(” “+$user.name+”: Disable and set strong password –> ‘”+$randpass+”‘”)
AddUserFlag $user.name $ADS_UF_DONT_EXPIRE_PASSWD
AddUserFlag $user.name $ADS_UF_ACCOUNTDISABLE
$user.setpassword($randpass)
$user.description = “Local Administrator account”
$user.setinfo()
}
ElseIf ($user.name -eq “Guest”)
{
$randpass = GetRandomString(20)
Write-Output(” “+$user.name+”: Disable and set strong password –> ‘”+$randpass+”‘”)
AddUserFlag $user.name $ADS_UF_DONT_EXPIRE_PASSWD
AddUserFlag $user.name $ADS_UF_ACCOUNTDISABLE
$user.setpassword($randpass)
$user.description = “Local Guest account”
$user.setinfo()
}
ElseIf ($user.name -eq “DefaultAccount”)
{
$randpass = GetRandomString(20)
Write-Output(” “+$user.name+”: Disable and set strong password –> ‘”+$randpass+”‘”)
AddUserFlag $user.name $ADS_UF_DONT_EXPIRE_PASSWD
AddUserFlag $user.name $ADS_UF_ACCOUNTDISABLE
$user.setpassword($randpass)
$user.description = “Local Default account”
$user.setinfo()
}
ElseIf ($user.name -eq “ASPNET”)
{
Write-Output(” “+$user.name+”: Ignore service account”)
}
ElseIf ($user.name -eq “Local User”)
{
Write-Output(” “+$user.name+”: Ignore service account”)
}
ElseIf ($user.name -eq “kioskUser0″)
{
Write-Output(” “+$user.name+”: Ignore service account”)
}
Else
{
$randpass = GetRandomString(20)
Write-Output(” “+$user.name+”: Disable and set strong password –> ‘”+$randpass+”‘”)
AddUserFlag $user.name $ADS_UF_DONT_EXPIRE_PASSWD
AddUserFlag $user.name $ADS_UF_ACCOUNTDISABLE
$user.setpassword($randpass)
$user.description = “Local manually created account”
$user.setinfo()
}
}
Write-Output “`n——————————————————————————-`n”
Get-CimInstance -ClassName win32_group -Filter “name = ‘administrators'” |
Get-CimAssociatedInstance -Association win32_groupuser |
Where-Object { $_.SID -notlike “*-500” } |
Where-Object {
# Filter out accounts that are used for local services
$_.Name -notin {
# An array of the names for the local computer (domainpart always “.”)
Get-WmiObject -Class Win32_Service |
Select-Object -ExpandProperty startname -Unique |
Where-Object { $_ -like “.\*” } |
ForEach-Object { Split-Path -Path $_ -Leaf }
}
} |
$Computer = [ADSI]”WinNT://$Env:COMPUTERNAME,Computer”
$User = $Computer.Create(“User”, “Local User”)
$User.SetPassword(“”)
$User.SetInfo()
$User.FullName = “Local User”
$User.SetInfo()
$User.UserFlags = 64 + 65536 # ADS_UF_PASSWD_CANT_CHANGE + ADS_UF_DONT_EXPIRE_PASSWD
$User.SetInfo()
$group = [ADSI]”WinNT://./Administrators,group”
$group.Add(“WinNT://Local User,user”)
$remove = net localgroup administrators | select -skip 6 | ? {$_ -and $_ -notmatch ‘successfully|^Administrator|Local User’}; foreach ($user in $remove) {net localgroup administrators “`”$user`”” /delete};
$remove = net localgroup “Power Users” | select -skip 6 | ? {$_ -and $_ -notmatch ‘successfully|^Administrator|Local User’}; foreach ($user in $remove) {net localgroup “Power Users” “`”$user`”” /delete};
$remove = net localgroup “Backup Operators” | select -skip 6 | ? {$_ -and $_ -notmatch ‘successfully|^Administrator|Local User’}; foreach ($user in $remove) {net localgroup “Backup Operators” “`”$user`”” /delete};
$remove = net localgroup “Device Owners” | select -skip 6 | ? {$_ -and $_ -notmatch ‘successfully|^Administrator|Local User’}; foreach ($user in $remove) {net localgroup “Device Owners” “`”$user`”” /delete};
Thank you for sharing, NVader2000!
Regards, Peter
Great write up Peter. I can’t seem to get the created user to not have the “User must change password at next logon” unchecked. I am trying to create a local admin user but if I haven’t manually updated the password on first logon, i can’t use it to elevate.
Cheers,
Hi Quardi,
That’s often policy related. From the perspective of this configuration, that can only be adjusted by custom scripting and not via the CSP.
Regards, Peter
Hi, What policy could trigger that local admin behaviour? I’m having the same issue.
I tried to run the following script in powershell in order to find a workaround for the issue, but I got: “Logon Failure: EAS policy requires that the user change their password before this operation can be performed.”
$user=[ADSI]’WinNT://localhost/LCAdmin’;
$user.passwordExpired = 0;
$user.setinfo();
I tried also by removing the EAS policies reg entries , but to no avail
Hi Maximilliano,
That’s default behavior when creating an account this way. You could look at scripting the whole thing.
Regards, Peter
Januari 2021: still getting the remediation failed error message…. (while it is functioning ofcourse)
Hi,
I get the same error message ->
-2016281112 (Remediation failed)
Anybody solved this allready?
Hi Car,
Sadly not. It’s now even mentioned in the docs here.
Regards, Peter
Hi Peter,
Great stuff.
Any OMA/URI for unchecking User Must Change the Password at the next logon.
I have tried with Powershell script(net user “Ratan” /logonpasswordchg:no)
but for some reason, it is getting failed.
Any clue why it is happening?
Hi Sunny,
No. That configuring would still require some custom scripting (with indeed something like PowerShell).
Regards, Peter
Hi,
is it possible to add the user to the “PowerUser” Group?
Best regards
Robert
Hi Robert,
Yes, but not via this configuration. That would require custom scripting or the use of restricted groups.
Regards, Peter
Hello Peter , Looks like the password doesn’t seem to work but however , user created seems to show in built in admin group
What do you mean, Dheeraj?
Regards, Peter
Actually , i was saying the password configured through the Accounts csp was not working when the user tried to login with local admin account.
However , now the issue seems to be self resolved
That is good to hear Dheeraj.
Regards, Peter