Autologon and Domain Join Issue with Sysprep Image
During the preparation of an image using sysprep for deployment, an unattend answer file is utilized that includes the following steps:
- Rename the machine and restart. (Efforts to rename and join the domain in a single step have not been successful.)
- Join the domain and remove the local user.
The issue arises when the system restarts to complete the operation; Windows sometimes logs in automatically as the local user or remains on a login screen for a user that has been removed. Various entries in the Windows registry related to the local user have been deleted in attempts to resolve this.
However, if the domain is joined manually without the commands in the unattend file, the undesired behavior does not occur.
What might be missing in this process?
Unattend.xml
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AutoLogon>
<Enabled>true</Enabled>
<LogonCount>2</LogonCount>
<Username>TI</Username>
<Password>
<PlainText>true</PlainText>
<Value></Value>
</Password>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell.exe Set-ExecutionPolicy Bypass -Scope Process -Force</CommandLine>
<Order>1</Order>
<RequiresUserInput>false</RequiresUserInput>
<Description>ativa scripts</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File C:\Windows\System32\SetComputerName.ps1</CommandLine>
<Order>2</Order>
<Description>Set Computer Name based on MAC Address</Description>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
<!-- Comando para excluir o script após a execução -->
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c del C:\Windows\System32\SetComputerName.ps1</CommandLine>
<Order>3</Order>
<Description>Excluir o script após execução</Description>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c "rmdir C:\Windows.old</CommandLine>
<Order>4</Order>
<Description>Excluir o script após execução</Description>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>shutdown /r /t 0</CommandLine>
<Order>5</Order>
<Description>Reiniciar o computador</Description>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell.exe Set-ExecutionPolicy Bypass -Scope Process -Force</CommandLine>
<Order>6</Order>
<RequiresUserInput>false</RequiresUserInput>
<Description>ativa scripts</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell.exe -ExecutionPolicy Bypass -File C:\Windows\System32\JoinDomainRemoTI.ps1</CommandLine>
<Order>7</Order>
<Description>Executa o script após execução</Description>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c del C:\Windows\System32\JoinDomainRemoTI.ps1</CommandLine>
<Order>8</Order>
<Description>Excluir o script após execução</Description>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>shutdown /r /t 0</CommandLine>
<Order>9</Order>
<Description>Reiniciar o computador</Description>
<RequiresUserInput>false</RequiresUserInput>
<!-- Reiniciar o computador após renomear e excluir o script -->
</FirstLogonCommands>
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
<SkipMachineOOBE>true</SkipMachineOOBE>
<SkipUserOOBE>true</SkipUserOOBE>
<UnattendEnableRetailDemo>false</UnattendEnableRetailDemo>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
</OOBE>
<UserAccounts>
<LocalAccounts>
<LocalAccount>
<DisplayName>TI</DisplayName>
<Group>Administrators</Group>
<Name>TI</Name>
<Password>
<PlainText>true</PlainText>
<Value></Value>
</Password>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
</component>
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputLocale>0416:00010416</InputLocale>
<UILanguage>pt-BR</UILanguage>
<UILanguageFallback>pt-BR</UILanguageFallback>
<UserLocale>pt-BR</UserLocale>
<SystemLocale>pt-BR</SystemLocale>
</component>
</settings>
<cpi:offlineImage cpi:source="wim:c:/users/ti/desktop/nova%20pasta/sources/install.wim#Windows 11 Enterprise" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
JoinDomainRemoTI.ps1
#Validação do canal seguro com o domínio
try {
if (-not (Test-ComputerSecureChannel -Verbose)) {
Write-Log "Conexão segura com o domínio falhou. Abortando script para evitar perda de acesso."
exit
} else {
Write-Log "Conexão segura com o domínio validada."
}
} catch {
Write-Log "Erro ao validar o canal seguro com o domínio: $_"
}
$USERNAME = "TI"
# Remover o perfil do usuário do registro
try {
Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$USERNAME" -Force -ErrorAction SilentlyContinue
Write-Log "Perfil do usuário $USERNAME removido do registro."
} catch {
Write-Log "Erro ao remover o perfil do usuário $USERNAME do registro: $_"
}
# Remover o usuário de todos os grupos
Get-LocalGroup | ForEach-Object {
$groupName = $_.Name
try {
Remove-LocalGroupMember -Group $groupName -Member $USERNAME -ErrorAction SilentlyContinue
Write-Log "Usuário $USERNAME removido do grupo $groupName."
} catch {
Write-Log "Erro ao remover $USERNAME do grupo $groupName: $_"
}
}
# Passo 1: Configurar para não exibir o último nome de usuário
try {
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "DontDisplayLastUserName" -Value 1
Write-Log "Configurado para não exibir o último nome de usuário."
} catch {
Write-Log "Erro ao configurar DontDisplayLastUserName: $_"
}
# Passo 2: Desativar login automático após reinicialização
try {
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "DisableAutomaticRestartSignOn" -Value 1
Write-Log "Configurado para desativar login automático após reinicialização."
} catch {
Write-Log "Erro ao configurar DisableAutomaticRestartSignOn: $_"
}
# Passo 3: Retardar processamento de GPOs do domínio
try {
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System" -Name "GroupPolicyRefreshTime" -Value 90
Write-Log "Configuração ajustada para retardar o processamento de GPOs do domínio."
} catch {
Write-Log "Erro ao configurar GroupPolicyRefreshTime: $_"
}
# Remover o usuário
try {
Remove-LocalUser -Name $USERNAME
Write-Log "Usuário $USERNAME removido com sucesso."
} catch {
Write-Log "Erro ao remover o usuário $USERNAME: $_"
}
# Remove o usuário de todos os arquivos e diretórios
Get-ChildItem -Path "C:\" -Recurse -Force | ForEach-Object {
if (Test-Path $_) {
$acl = Get-Acl $_
$acl.Access | Where-Object { $_.IdentityReference -eq "$env:COMPUTERNAME\$USERNAME" } | ForEach-Object {
$acl.RemoveAccessRule($_)
}
Set-Acl -Path $_ -AclObject $acl
}
}
function Remove-UserPolicyEntries {
param([string]$BasePath)
# Verifica se a chave existe
if (Test-Path $BasePath) {
try {
Write-Log "Removendo a chave do registro: $BasePath"
Remove-Item -Path $BasePath -Recurse -Force -ErrorAction SilentlyContinue
Write-Log "Chave $BasePath removida com sucesso."
} catch {
Write-Log "Erro ao remover a chave $BasePath: $_"
}
} else {
Write-Log "A chave $BasePath não existe. Nenhuma ação necessária."
}
# Recriar a chave vazia
try {
Write-Log "Recriando a chave do registro: $BasePath"
New-Item -Path $BasePath -Force | Out-Null
Write-Log "Chave $BasePath recriada com sucesso."
} catch {
Write-Log "Erro ao recriar a chave $BasePath: $_"
}
}
# Executa limpeza em caminhos padrão de políticas de grupo
Remove-UserPolicyEntries -BasePath "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\DataStore"
Remove-UserPolicyEntries -BasePath "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Group Policy\DataStore"
$registryPaths = @(
# Entradas de login e autenticação
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\UserTiles\$Username",
"HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\UserList\$Username",
# Entradas de preferências de usuário
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\*\Count\$Username",
# Entradas relacionadas a últimos usuários logados
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData\*",
# Entradas de credenciais
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Roaming\*\$Username"
)
# Resetar valores diretamente na chave LogonUI
$logonUIKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
if (Test-Path $logonUIKey) {
try {
$values = Get-ItemProperty -Path $logonUIKey
foreach ($value in $values.PSObject.Properties.Name) {
if ($value -ne "(default)") {
Write-Log "Removendo valor: $value"
Remove-ItemProperty -Path $logonUIKey -Name $value -ErrorAction Stop
}
}
Write-Log "Valores resetados na chave $logonUIKey."
} catch {
Write-Log "Erro ao resetar valores da chave $logonUIKey: $_"
}
} else {
Write-Log "A chave $logonUIKey não existe. Nenhuma ação necessária."
}
# Remover entradas de caminhos específicos
foreach ($path in $registryPaths) {
try {
if (Test-Path $path) {
Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue
Write-Log "Removida entrada do registro: $path"
}
} catch {
Write-Log "Erro ao remover entrada do registro $path: $_"
}
}
# Limpar entradas de autologon
try {
$USERNAME = "TI"
$winlogonKey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
# Multiple user removal methods
wmic useraccount where name='$USERNAME' delete
net user $USERNAME /delete
Remove-LocalUser -Name $USERNAME -ErrorAction SilentlyContinue
# Remove entradas padrão já existentes
Remove-ItemProperty -Path $winlogonKey -Name "DefaultUserName" -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $winlogonKey -Name "LastUsedUsername" -ErrorAction SilentlyContinue
# Entradas adicionais de autologon
$autologonEntries = @(
"AutoAdminLogon",
"DefaultPassword",
"LastPassword",
"AutoLogonCount",
"LoginDomain",
"UserName"
)
foreach ($entry in $autologonEntries) {
try {
Remove-ItemProperty -Path $winlogonKey -Name $entry -ErrorAction SilentlyContinue
Write-Log "Removida entrada de autologon: $entry"
} catch {
Write-Log "Erro ao remover entrada de autologon $entry: $_"
}
}
Write-Log "Limpeza de entradas de autologon concluída."
} catch {
Write-Log "Erro ao limpar entradas de Winlogon: $_"
}
try {
$unattendPaths = @(
"C:\Windows\Panther\Unattend.xml",
"C:\Windows\System32\Sysprep\Unattend.xml"
"C:\Windows\System32\JoinDomainRemoTI.ps1"
)
foreach ($path in $unattendPaths) {
if (Test-Path $path) {
Remove-Item $path -Force -ErrorAction SilentlyContinue
}
}
Write-Log "Usuário $USERNAME foi completamente removido e suas configurações foram limpas."
} catch {
Write-Log "Erro ao remover arquivos Unattend.xml"
}