Restrict Windows 10 and Windows 11 logon to the current user or user who enrolled the device during Autopilot

A while back I investigate if there was any possibility to lock down a Windows 10 or 11 device that gets provisioned with Autopilot and enrolled in to Azure AD and Intune to only allow the user who enrolled the device to be able to logon to that specific machine. Meaning that no other user except for Admins and the specific user the computer belonged to should have the capability to logon to the device.

You should see this as a proof-of-concept or an article to get some inspiration from instead of a how-to guide. The reason for that are many but there are a few issues that needs to be ironed and scenarios you need to be aware of when this might not work as intended. I have dedicated a section below “Known Issues and scenarios to be aware of” to just that.

To understand why we ended up were we did I just want to talk about some of the capabilities that exist today in the form of different policies that can be configured from Intune and CSP directly but which did not cover the specific scenario we are trying to solve here.

Allow local log on – Settings catalog

this solution was not applicable for this scenario as described below.

https://docs.microsoft.com/en-us/windows/client-management/mdm/policy-csp-UserRights?WT.mc_id=Portal-fx#userrights-allowlocallogon

The Issue for our scenario with this setting is that we need to pre-define which group or user that should be added before hand, thus we cant use this since we dont necessary know which user enrolls a specific device and even if we did we had to make a new policy in Intune for each device and user.

Another thing I was investigating was if it was possbile to remove Authenticated users and add a specific user to the group but I never got that to work as exspected or able to block any other sign-in with that.

The script and solution

In its simplest form this is the script that edits the local security policy but you probably want to look further down in this article under the PSADT section where I have added a few more steps for constiancy when running the script from my own testing with newly Autopilot provisioned computers.

Don’t forget to change the domain in the script.


  $User = Get-WmiObject Win32_Process -Filter "Name='explorer.exe'" | ForEach-Object { $_.GetOwner() } | Select-Object -Unique -Expand User
   
  $Username = "VIAMONSTRA\$($User)" #Change domain 
 
  $Temp = "C:\Windows\Temp"
  $Import = Join-Path -Path $Temp -ChildPath "import.inf"
  if(Test-Path $Import) { Remove-Item -Path $Import -Force }
   
  $Export = Join-Path -Path $Temp -ChildPath "export.inf"
  if(Test-Path $Export) { Remove-Item -Path $Export -Force }
   
  $Secedt = Join-Path -Path $Temp -ChildPath "secedt.sdb"
  if(Test-Path $Secedt) { Remove-Item -Path $Secedt -Force }
   try {
    
    $sid = ((New-Object System.Security.Principal.NTAccount($Username)).Translate([System.Security.Principal.SecurityIdentifier])).Value
    secedit /export /cfg $Export
    $sids = (Select-String $Export -Pattern "SeInteractiveLogonRight").Line
    $sids = $sids -replace ([regex]::Escape(',*S-1-5-32-545')),'' # SID S-1-5-32-545 = Built in Users group https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab
     
 
      
foreach ($row in @("[Unicode]", "Unicode=yes", "[System Access]", "[Event Audit]", "[Registry Values]", "[Version]", "signature=`"`$CHICAGO$`"", "Revision=1", "[Profile Description]", "Description=GrantLogOnAsAService security template", "[Privilege Rights]", "$sids,*$sid")){
      Add-Content $Import $row
    }
    secedit /import /db $Secedt /cfg $Import
    secedit /configure /db $Secedt
    gpupdate /force
  } catch {
 
  }

PSADT Script section

This is the actuall script I’ve been running in my envirorment. I create a Win32 app which is just wrapped with PSADT and then deployed from Intune to a AAD device group. If you want to know more about how to use PSADT wtih Intune you can have a look at an article i wrote a couple of years ago thats still is relevant today

https://timmyit.com/2019/09/02/using-psadt-with-win32-apps-in-intune/

# First run

  $User = Get-WmiObject Win32_Process -Filter "Name='explorer.exe'" | ForEach-Object { $_.GetOwner() } | Select-Object -Unique -Expand User
   
  $Username = "VIAMONSTRA\$($User)" #Change domain 
 
  $Temp = "C:\Windows\Temp"
  $Import = Join-Path -Path $Temp -ChildPath "import.inf"
  if(Test-Path $Import) { Remove-Item -Path $Import -Force }
   
  $Export = Join-Path -Path $Temp -ChildPath "export.inf"
  if(Test-Path $Export) { Remove-Item -Path $Export -Force }
   
  $Secedt = Join-Path -Path $Temp -ChildPath "secedt.sdb"
  if(Test-Path $Secedt) { Remove-Item -Path $Secedt -Force }
   try {
    
    $sid = ((New-Object System.Security.Principal.NTAccount($Username)).Translate([System.Security.Principal.SecurityIdentifier])).Value
    secedit /export /cfg $Export
    $sids = (Select-String $Export -Pattern "SeInteractiveLogonRight").Line
    $sids = $sids -replace ([regex]::Escape(',*S-1-5-32-545')),'' # SID S-1-5-32-545 = Built in Users group https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab
     
 
      
foreach ($row in @("[Unicode]", "Unicode=yes", "[System Access]", "[Event Audit]", "[Registry Values]", "[Version]", "signature=`"`$CHICAGO$`"", "Revision=1", "[Profile Description]", "Description=GrantLogOnAsAService security template", "[Privilege Rights]", "$sids,*$sid")){
      Add-Content $Import $row
    }
    secedit /import /db $Secedt /cfg $Import
    secedit /configure /db $Secedt
    gpupdate /force
  } catch {
 
  }

Start-Sleep -seconds 30

#2nd Run - This part runs again because of certain scenarios where newly provisioned Autopilot devices not always removes Users from the policy on the first try. 

   try {
    
    $sid = ((New-Object System.Security.Principal.NTAccount($Username)).Translate([System.Security.Principal.SecurityIdentifier])).Value
    secedit /export /cfg $Export
    $sids = (Select-String $Export -Pattern "SeInteractiveLogonRight").Line
    $sids = $sids -replace ([regex]::Escape(',*S-1-5-32-545')),'' # SID S-1-5-32-545 = Built in Users group https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab
     
 
      
foreach ($row in @("[Unicode]", "Unicode=yes", "[System Access]", "[Event Audit]", "[Registry Values]", "[Version]", "signature=`"`$CHICAGO$`"", "Revision=1", "[Profile Description]", "Description=GrantLogOnAsAService security template", "[Privilege Rights]", "$sids,*$sid")){
      Add-Content $Import $row
    }
    secedit /import /db $Secedt /cfg $Import
    secedit /configure /db $Secedt
    gpupdate /force
  } catch {
 
  }
   
   
$TodaysdateTime = get-date -Format "yyyy-MM-dd HH:mm"  
Set-RegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\PSADT\AllowLocalLogonOn' -Name 'InstalledForUser' -Type String -Value "VIAMONSTRA\$($User)"
Set-RegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\PSADT\AllowLocalLogonOn' -Name 'InstallDateTime' -Type String -Value "$($TodaysdateTime)"

You can just copy the script and paste it in under the Installation section in Deploy-Application.ps1

As detection method I’ve just been using the registry keys I create during the installation, there’s probably a much better way of doing this.

Windows 10 experience

Windows 11 experience

Troubleshooting and logs

The secedit part of the script exports an .ini file which contains all the configuration as it is when the script first runs. Then we create a new file Import.ini that we then import. If you need to check the actuall information that gets retreived in any of those instances you can have look in those files.

Secedit Logs

The secedit logs for the actual import is located here:

%windir%\security\logs

the scesrv.txt contains information on what happend during the import and will tell you if something went wrong during import and what has hanged.

Known Issues and scenarios to be aware of

Device ESP – Defaultuser0
If you are using Enrollment Status Page and wait for applications to install during the Device configuration phase I have seen issues with that the script runs and configures the user as DefualtUser0 (this is the user that ESP and the intial setup of Windows runs under) Im working on trying to get some logic in to the script to avoid this in a future article.

Inconsistency that Users group not gets removed

There has been a few occations where the script runs fine but the built in User group never gets removed from the Local Security Policy for devices that gets the script during Autopilot and the first logon there seems to be a window on the machine where all the standard groups are not yet populated and when this happened the script could not remove the User group because of the regex string it was looking for did not match.

the solution seems to be to run the this section of the script twice, as mentioned in the PSADT section I run the lat bit of the script 2 times and that have been a workaround that worked for me in my testing.

Admin logon

If an Administrator logs on to the device (which they are allowed to do unless you removed administrators as well from the local security policy) and the script runs again the user that enrolled the device will get removed from the policy and won’t be able to login again.

Thats it for this article,
Timmy

2 comments

Leave a Reply