UPDATE 19. August: Windows-Update blockiert SiteKiosk-Autostart

Company

UPDATE 19. August: Windows-Update blockiert SiteKiosk-Autostart

12. August 2026

Ein von Microsoft verteiltes Windows-Update verhindert den Autostart von SiteKiosk Online und SiteKiosk Classic mit dem lokalen SiteKiosk-Benutzer.

Durch ein Windows-Update, das von Microsoft in der Nacht auf den 12. August 2026 verteilt wurde, funktioniert der Autostart-Modus mit dem SiteKiosk-Benutzer nicht mehr korrekt.

Ursache

Ursache ist die von SiteKiosk vorgenommene Rechteeinschränkung am SiteKiosk-Benutzer. Diese verhindert, dass das Windows-Update alle erforderlichen Schritte auf dem betroffenen Benutzerkonto vollständig und ordnungsgemäß ausführen kann. In der Folge kann die NTUSER.DAT nicht geladen werden und der Autostart wird blockiert.

Update vom 19. August 2026:
Mit der neuen Version 9.10.10010 des SiteKiosk Classic Clients ist der Fix nun standardmäßig enthalten.

Update vom 17. August 2026:
Mit der neuen Version 1.10.341.0 vom SiteKiosk Online Client steht dieser Fix nun standardmäßig zur Verfügung.

Aktualisierter Workaround

Update vom 17. August 2026:
Wir haben den bisherigen Workaround überarbeitet und stellen ein neues PowerShell-Skript zur Verfügung.

Wichtig:
Bevorzugen Sie nach Möglichkeit die Installation über den aktuellen SiteKiosk-Installer, da der Fix darüber bereits standardmäßig enthalten ist. Sollte der Einsatz des Installers im Einzelfall nicht möglich sein, kann alternativ der nachfolgende aktualisierte Workaround angewendet werden.
Das aktualisierte Skript muss nach jeder Neuinstallation von SiteKiosk erneut ausgeführt werden.

Als kurzfristigen Workaround empfehlen wir, auf einem betroffenen Rechner das nachfolgende PowerShell-Skript in einer PowerShell-Sitzung mit administrativen Berechtigungen auszuführen.

Das neue Skript ersetzt den bisher veröffentlichten Hotfix und berücksichtigt zusätzliche Szenarien bei der Reparatur des SiteKiosk-Benutzerprofils. Unser Entwicklungsteam arbeitet weiterhin an einer finalen Lösung. Über weitere Änderungen informieren wir, sobald neue Informationen verfügbar sind.

Stellen Sie bitte sicher, dass der SiteKiosk-Benutzer vollständig abgemeldet ist, bevor Sie das Skript ausführen.

Vorgehen bei SiteKiosk Online

Update vom 17. August 2026:
Mit der neuen Version 1.10.341.0 vom SiteKiosk Online Client steht dieser Fix nun standardmäßig zur Verfügung.

12. August 2026
Zur Transparenz: Am 12. August 2026 haben wir automatisiert ein vereinfachtes Skript an alle zu diesem Zeitpunkt verbundenen Clients ausgespielt.

Alternativ kann der Workaround aus der Ferne für jeden Client einzeln ausgeführt werden, zum Beispiel über die Recovery Shell eines betroffenen Clients:
Monitoring > [Client] > Administration > Recovery Shell

Vorgehen bei SiteKiosk Classic Windows

Update vom 19. August 2026:
Mit der neuen Version 9.10.10010 des SiteKiosk Classic Clients ist der Fix nun standardmäßig enthalten.

Wichtig:
Wo immer möglich, empfehlen wir die Installation über den aktuellen SiteKiosk-Installer, da der Fix dort bereits standardmäßig enthalten ist. Sollte die Verwendung des Installers in einem konkreten Fall nicht möglich sein, kann alternativ der unten aufgeführte aktualisierte Workaround vom 17. August 2026 angewendet werden.

Update vom 17. August 2026:

Bei SiteKiosk Classic Windows muss das Skript lokal mit einem Administratorkonto ausgeführt werden:

1. Entsperren Sie zuerst die Tastatursperre. Anleitung: Tastatursperre entsperren
2. Melden Sie sich anschließend mit einem Administratorkonto am System an.
3. Starten Sie PowerShell ISE mit administrativen Rechten und führen Sie das Skript aus.

PowerShell-Skript



[CmdletBinding(DefaultParameterSetName = 'Automatic', SupportsShouldProcess)]
param(
    [Parameter(Mandatory, ParameterSetName = 'Explicit', Position = 0)]
    [Alias('Account')]
    [ValidateNotNullOrEmpty()]
    [string] $UserName,

    [Parameter(ParameterSetName = 'Explicit', Position = 1)]
    [AllowEmptyString()]
    [string] $Domain,

    [Parameter()]
    [Alias('LogFile')]
    [ValidateNotNullOrEmpty()]
    [string] $LogPath,

    [Parameter()]
    [switch] $Restart
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$siteKioskSettingsPath = 'SOFTWARE\PROVISIO\SiteKiosk'
$installerSettingsPath = 'SOFTWARE\PROVISIO\{A1625B78-854F-4AB7-B99D-A52EF6E94C8A}'
$classicSecurityWizardPath = 'SOFTWARE\PROVISIO\SecurityWizard'
$requiredFileRights = [System.Security.AccessControl.FileSystemRights] (
    [System.Security.AccessControl.FileSystemRights]::ReadAttributes -bor
    [System.Security.AccessControl.FileSystemRights]::ReadData -bor
    [System.Security.AccessControl.FileSystemRights]::ReadExtendedAttributes -bor
    [System.Security.AccessControl.FileSystemRights]::ReadPermissions -bor
    [System.Security.AccessControl.FileSystemRights]::WriteData -bor
    [System.Security.AccessControl.FileSystemRights]::AppendData -bor
    [System.Security.AccessControl.FileSystemRights]::WriteAttributes -bor
    [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes -bor
    [System.Security.AccessControl.FileSystemRights]::Delete -bor
    [System.Security.AccessControl.FileSystemRights]::Synchronize
)
$requiredDirectoryRights = (
    [System.Security.AccessControl.FileSystemRights]::CreateFiles
)
$repairLogPath = $null
$repairLogEncoding = [System.Text.UTF8Encoding]::new($false)
$repairErrorWasLogged = $false


function Initialize-RepairLog {
    if ([string]::IsNullOrWhiteSpace($LogPath)) {
        return
    }

    try {
        $candidatePath = if ([System.IO.Path]::IsPathRooted($LogPath)) {
            $LogPath
        }
        else {
            Join-Path -Path (Get-Location).ProviderPath -ChildPath $LogPath
        }

        $script:repairLogPath = [System.IO.Path]::GetFullPath($candidatePath)
        $parentPath = [System.IO.Path]::GetDirectoryName($script:repairLogPath)

        if (
            -not [string]::IsNullOrWhiteSpace($parentPath) -and
            -not [System.IO.Directory]::Exists($parentPath)
        ) {
            [void] [System.IO.Directory]::CreateDirectory($parentPath)
        }

        [System.IO.File]::AppendAllText(
            $script:repairLogPath,
            "`r`n$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff zzz') " +
                "[INFO] Repair started by '$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)'.`r`n",
            $script:repairLogEncoding
        )
    }
    catch {
        throw "Unable to initialize the log file '$LogPath': $($_.Exception.Message)"
    }
}


function Write-RepairLog {
    param(
        [Parameter(Mandatory)]
        [ValidateSet('INFO', 'WARN', 'ERROR')]
        [string] $Level,

        [AllowNull()]
        [AllowEmptyString()]
        [object] $Message
    )

    if ([string]::IsNullOrWhiteSpace($script:repairLogPath)) {
        return
    }

    $text = if ($null -eq $Message) { '' } else { [string] $Message }
    $lines = [System.Text.RegularExpressions.Regex]::Split($text, '\r?\n')
    $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff zzz'
    $logText = ($lines | ForEach-Object {
        "$timestamp [$Level] $_"
    }) -join "`r`n"

    [System.IO.File]::AppendAllText(
        $script:repairLogPath,
        $logText + "`r`n",
        $script:repairLogEncoding
    )
}


function Write-RepairHost {
    param(
        [AllowNull()]
        [AllowEmptyString()]
        [object] $Message
    )

    Write-RepairLog -Level INFO -Message $Message
    Write-Host $Message
}


function Write-RepairWarning {
    param(
        [AllowNull()]
        [AllowEmptyString()]
        [object] $Message
    )

    Write-RepairLog -Level WARN -Message $Message
    Write-Warning $Message
}


function Write-RepairError {
    param(
        [AllowNull()]
        [AllowEmptyString()]
        [object] $Message
    )

    $script:repairErrorWasLogged = $true
    Write-RepairLog -Level ERROR -Message $Message
    Write-Error $Message -ErrorAction Continue
}


function Get-RegistryViews {
    if ([Environment]::Is64BitOperatingSystem) {
        [Microsoft.Win32.RegistryView]::Registry64
    }

    [Microsoft.Win32.RegistryView]::Registry32
}


function Test-EnabledRegistryValue {
    param(
        [AllowNull()]
        [object] $Value
    )

    $text = [Convert]::ToString($Value)

    return (
        [string]::Equals(
            $text,
            'true',
            [StringComparison]::InvariantCultureIgnoreCase
        ) -or
        $text -eq '1'
    )
}


function Get-RegistryUser {
    param(
        [Parameter(Mandatory)]
        [Microsoft.Win32.RegistryView] $RegistryView,

        [Parameter(Mandatory)]
        [string] $RegistryPath,

        [Parameter(Mandatory)]
        [string] $UserNameValue,

        [Parameter(Mandatory)]
        [string] $DomainValue,

        [Parameter(Mandatory)]
        [string] $Source,

        [switch] $RequireInstallerCreatedUser
    )

    $baseKey = $null
    $registryKey = $null

    try {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey(
            [Microsoft.Win32.RegistryHive]::LocalMachine,
            $RegistryView
        )

        $registryKey = $baseKey.OpenSubKey($RegistryPath, $false)

        if ($null -eq $registryKey) {
            return
        }

        if (
            $RequireInstallerCreatedUser -and
            -not (Test-EnabledRegistryValue `
                -Value $registryKey.GetValue('CREATESITEKIOSKUSER'))
        ) {
            return
        }

        $userName = $registryKey.GetValue($UserNameValue) -as [string]
        $domain = $registryKey.GetValue($DomainValue) -as [string]

        if ([string]::IsNullOrWhiteSpace($userName)) {
            return
        }

        [PSCustomObject]@{
            UserName = $userName
            Domain   = $domain
            Source   = $Source
        }
    }
    finally {
        if ($null -ne $registryKey) {
            $registryKey.Dispose()
        }

        if ($null -ne $baseKey) {
            $baseKey.Dispose()
        }
    }
}


function ConvertTo-ResolvedUser {
    param(
        [Parameter(Mandatory)]
        [PSCustomObject] $Candidate,

        [switch] $IgnoreResolutionFailure
    )

    $candidateSid = $Candidate.PSObject.Properties['Sid']

    if (
        $null -ne $candidateSid -and
        -not [string]::IsNullOrWhiteSpace($candidateSid.Value)
    ) {
        try {
            $securityIdentifier = [System.Security.Principal.SecurityIdentifier]::new(
                $candidateSid.Value
            )
            $resolvedAccount = $securityIdentifier.Translate(
                [System.Security.Principal.NTAccount]
            ).Value
        }
        catch {
            $message = (
                "The SID '$($candidateSid.Value)' from " +
                "'$($Candidate.Source)' could not be resolved: " +
                $_.Exception.Message
            )

            if ($IgnoreResolutionFailure) {
                Write-RepairWarning $message
                return
            }

            throw $message
        }

        $separator = $resolvedAccount.IndexOf('\')
        $resolvedDomain = if ($separator -ge 0) {
            $resolvedAccount.Substring(0, $separator)
        }
        else {
            ''
        }
        $resolvedUserName = if ($separator -ge 0) {
            $resolvedAccount.Substring($separator + 1)
        }
        else {
            $resolvedAccount
        }

        return [PSCustomObject]@{
            UserName = $resolvedUserName
            Domain   = $resolvedDomain
            Sid      = $securityIdentifier.Value
            Sources  = @($Candidate.Source)
        }
    }

    $userName = $Candidate.UserName.Trim()
    $domain = $Candidate.Domain
    $qualifiedSeparator = $userName.IndexOf('\')

    if ($qualifiedSeparator -ge 0) {
        if (-not [string]::IsNullOrWhiteSpace($domain)) {
            throw (
                "The account '$userName' is already qualified. " +
                'Do not specify Domain as well.'
            )
        }

        $domain = $userName.Substring(0, $qualifiedSeparator)
        $userName = $userName.Substring($qualifiedSeparator + 1)

        if ([string]::IsNullOrWhiteSpace($userName)) {
            throw "The qualified account name does not contain a user name."
        }
    }

    if (
        [string]::IsNullOrWhiteSpace($domain) -or
        $domain -eq '.'
    ) {
        $domain = $env:COMPUTERNAME
    }

    $ntAccount = if ($userName.Contains('@')) {
        [System.Security.Principal.NTAccount]::new($userName)
    }
    else {
        [System.Security.Principal.NTAccount]::new($domain, $userName)
    }

    try {
        $securityIdentifier = $ntAccount.Translate(
            [System.Security.Principal.SecurityIdentifier]
        )
        $resolvedAccount = $securityIdentifier.Translate(
            [System.Security.Principal.NTAccount]
        ).Value
    }
    catch {
        $message = (
            "The account '$domain\$userName' from " +
            "'$($Candidate.Source)' could not be resolved: " +
            $_.Exception.Message
        )

        if ($IgnoreResolutionFailure) {
            Write-RepairWarning $message
            return
        }

        throw $message
    }

    $separator = $resolvedAccount.IndexOf('\')

    return [PSCustomObject]@{
        UserName = if ($separator -ge 0) {
            $resolvedAccount.Substring($separator + 1)
        }
        else {
            $resolvedAccount
        }
        Domain   = if ($separator -ge 0) {
            $resolvedAccount.Substring(0, $separator)
        }
        else {
            ''
        }
        Sid      = $securityIdentifier.Value
        Sources  = @($Candidate.Source)
    }
}


function Test-RunningElevated {
    $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = [System.Security.Principal.WindowsPrincipal]::new($identity)

    return $principal.IsInRole(
        [System.Security.Principal.WindowsBuiltInRole]::Administrator
    )
}


function Get-RegistrySidUser {
    param(
        [Parameter(Mandatory)]
        [Microsoft.Win32.RegistryView] $RegistryView,

        [Parameter(Mandatory)]
        [string] $RegistryPath,

        [Parameter(Mandatory)]
        [string] $SidValue,

        [Parameter(Mandatory)]
        [string] $Source
    )

    $baseKey = $null
    $registryKey = $null

    try {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey(
            [Microsoft.Win32.RegistryHive]::LocalMachine,
            $RegistryView
        )
        $registryKey = $baseKey.OpenSubKey($RegistryPath, $false)

        if ($null -eq $registryKey) {
            return
        }

        $sid = $registryKey.GetValue($SidValue) -as [string]

        if ([string]::IsNullOrWhiteSpace($sid)) {
            return
        }

        [PSCustomObject]@{
            Sid      = $sid
            Source   = $Source
        }
    }
    finally {
        if ($null -ne $registryKey) {
            $registryKey.Dispose()
        }

        if ($null -ne $baseKey) {
            $baseKey.Dispose()
        }
    }
}


function Get-SiteKioskUsers {
    $candidates = @(
        foreach ($registryView in Get-RegistryViews) {
            Get-RegistryUser `
                -RegistryView $registryView `
                -RegistryPath $siteKioskSettingsPath `
                -UserNameValue 'UserName' `
                -DomainValue 'Domain' `
                -Source 'Currently configured SiteKiosk Online user'

            Get-RegistryUser `
                -RegistryView $registryView `
                -RegistryPath $installerSettingsPath `
                -UserNameValue 'SITEKIOSKUSERNAME' `
                -DomainValue 'SITEKIOSKUSERDOMAIN' `
                -Source 'SiteKiosk Online user created by the installer' `
                -RequireInstallerCreatedUser
        }

        # SiteKiosk Classic is a 32-bit application and stores the SID of its
        # restricted user directly in this registry view.
        Get-RegistrySidUser `
            -RegistryView ([Microsoft.Win32.RegistryView]::Registry32) `
            -RegistryPath $classicSecurityWizardPath `
            -SidValue 'RestrictedUser' `
            -Source 'Restricted SiteKiosk Classic user'

        # The Classic installer always creates this local account. Keep it as a
        # separate candidate because the administrator may later configure a
        # different restricted user while the installer-created profile remains.
        [PSCustomObject]@{
            UserName = 'SiteKiosk'
            Domain   = '.'
            Source   = 'SiteKiosk Classic user created by the installer'
        }
    )

    $usersBySid = @{}

    foreach ($candidate in $candidates) {
        $user = ConvertTo-ResolvedUser `
            -Candidate $candidate `
            -IgnoreResolutionFailure

        if ($null -eq $user) {
            continue
        }

        if (-not $usersBySid.ContainsKey($user.Sid)) {
            $usersBySid[$user.Sid] = $user
        }
        else {
            $existing = $usersBySid[$user.Sid]

            if ($existing.Sources -notcontains $candidate.Source) {
                $existing.Sources += $candidate.Source
            }
        }
    }

    return @($usersBySid.Values)
}


function Get-OrRepairProfilePath {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [string] $Sid,

        [Parameter(Mandatory)]
        [string] $AccountName
    )

    # ProfileList is operating-system state. On a 64-bit system the native
    # 64-bit view is authoritative, regardless of which SiteKiosk edition
    # supplied the user account information.
    $registryView = if ([Environment]::Is64BitOperatingSystem) {
        [Microsoft.Win32.RegistryView]::Registry64
    }
    else {
        [Microsoft.Win32.RegistryView]::Registry32
    }
    $profileListPath = (
        'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
    )
    $baseKey = $null
    $profileListKey = $null
    $profileKey = $null
    $backupKey = $null

    try {
        $baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey(
            [Microsoft.Win32.RegistryHive]::LocalMachine,
            $registryView
        )
        $profileListKey = $baseKey.OpenSubKey($profileListPath, $true)

        if ($null -eq $profileListKey) {
            throw 'The Windows ProfileList registry key could not be opened.'
        }

        $profileKey = $profileListKey.OpenSubKey($Sid, $false)
        $backupKey = $profileListKey.OpenSubKey("$Sid.bak", $false)

        if ($null -ne $profileKey -and $null -ne $backupKey) {
            throw (
                "Both ProfileList entries '$Sid' and '$Sid.bak' exist. " +
                'This state is ambiguous and was not modified.'
            )
        }

        $sourceKey = if ($null -ne $profileKey) {
            $profileKey
        }
        else {
            $backupKey
        }

        if ($null -eq $sourceKey) {
            # The account may never have had a profile.
            return $null
        }

        $rawPath = $sourceKey.GetValue(
            'ProfileImagePath',
            $null,
            [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
        ) -as [string]

        if ([string]::IsNullOrWhiteSpace($rawPath)) {
            throw (
                "The ProfileList entry for $Sid does not contain " +
                'a ProfileImagePath value.'
            )
        }

        $profilePath = [Environment]::ExpandEnvironmentVariables($rawPath)

        if (-not [System.IO.Path]::IsPathRooted($profilePath)) {
            throw (
                "The ProfileList entry for $Sid contains a non-absolute " +
                "ProfileImagePath: $profilePath"
            )
        }

        if ($null -ne $profileKey) {
            return $profilePath
        }

        if (-not (Test-Path -LiteralPath $profilePath -PathType Container)) {
            throw (
                "The .bak entry for $Sid refers to a missing profile " +
                "directory: $profilePath. This script cannot repair this " +
                'problem. Wait for a newer SiteKiosk installer that can ' +
                'create the required user profile.'
            )
        }

        if (-not (Test-Path -LiteralPath (Join-Path $profilePath 'NTUSER.DAT') -PathType Leaf)) {
            throw (
                "The .bak entry for $Sid refers to '$profilePath', but " +
                'NTUSER.DAT is missing. The registration was not restored.'
            )
        }

        if ($backupKey.SubKeyCount -ne 0) {
            throw (
                "The ProfileList .bak entry for $Sid contains unexpected " +
                'subkeys. The registration was not restored automatically.'
            )
        }

        $normalizedProfilePath = (
            [System.IO.Path]::GetFullPath($profilePath)
        ).TrimEnd('\')

        foreach ($otherKeyName in $profileListKey.GetSubKeyNames()) {
            if (
                $otherKeyName -ieq $Sid -or
                $otherKeyName -ieq "$Sid.bak"
            ) {
                continue
            }

            $otherKey = $null

            try {
                $otherKey = $profileListKey.OpenSubKey($otherKeyName, $false)

                if ($null -eq $otherKey) {
                    continue
                }

                $otherRawPath = $otherKey.GetValue(
                    'ProfileImagePath',
                    $null,
                    [Microsoft.Win32.RegistryValueOptions]::
                        DoNotExpandEnvironmentNames
                ) -as [string]

                if ([string]::IsNullOrWhiteSpace($otherRawPath)) {
                    continue
                }

                $otherProfilePath = [System.IO.Path]::GetFullPath(
                    [Environment]::ExpandEnvironmentVariables($otherRawPath)
                ).TrimEnd('\')

                if (
                    [string]::Equals(
                        $normalizedProfilePath,
                        $otherProfilePath,
                        [StringComparison]::OrdinalIgnoreCase
                    )
                ) {
                    throw (
                        "ProfileList entry '$otherKeyName' also refers to " +
                        "'$profilePath'. The .bak entry was not restored."
                    )
                }
            }
            finally {
                if ($null -ne $otherKey) {
                    $otherKey.Dispose()
                }
            }
        }

        $profileIsLoaded = Test-Path -LiteralPath "Registry::HKEY_USERS\$Sid"

        if ($profileIsLoaded -and -not $WhatIfPreference) {
            throw (
                "The profile of '$AccountName' is currently loaded. " +
                'Sign out the user before restoring its ProfileList entry.'
            )
        }

        if ($profileIsLoaded) {
            Write-RepairWarning (
                "The profile of '$AccountName' is currently loaded. " +
                'WhatIf will only preview the ProfileList repair.'
            )
        }

        $registryTarget = (
            "HKLM:\$profileListPath\$Sid.bak"
        )

        if (
            $PSCmdlet.ShouldProcess(
                $registryTarget,
                "Restore the regular ProfileList entry for $AccountName"
            )
        ) {
            if (Test-Path -LiteralPath "Registry::HKEY_USERS\$Sid") {
                throw (
                    "The profile of '$AccountName' was loaded while the " +
                    'repair was being prepared. Sign out the user and try again.'
                )
            }

            $lateProfileKey = $null

            try {
                $lateProfileKey = $profileListKey.OpenSubKey($Sid, $false)

                if ($null -ne $lateProfileKey) {
                    throw (
                        "A regular ProfileList entry for $Sid appeared while " +
                        'the repair was being prepared. Nothing was modified.'
                    )
                }
            }
            finally {
                if ($null -ne $lateProfileKey) {
                    $lateProfileKey.Dispose()
                }
            }

            Write-RepairHost (
                "Restoring ProfileList entry: $Sid.bak -> $Sid"
            )

            $newProfileKey = $null
            $destinationCreated = $false

            try {
                $newProfileKey = $profileListKey.CreateSubKey($Sid)

                if ($null -eq $newProfileKey) {
                    throw "Unable to create the ProfileList entry for $Sid."
                }

                $destinationCreated = $true

                foreach ($valueName in $backupKey.GetValueNames()) {
                    $value = $backupKey.GetValue(
                        $valueName,
                        $null,
                        [Microsoft.Win32.RegistryValueOptions]::
                            DoNotExpandEnvironmentNames
                    )
                    $valueKind = $backupKey.GetValueKind($valueName)
                    $newProfileKey.SetValue($valueName, $value, $valueKind)
                }

                $newProfileKey.SetValue(
                    'State',
                    0,
                    [Microsoft.Win32.RegistryValueKind]::DWord
                )
                $newProfileKey.SetValue(
                    'RefCount',
                    0,
                    [Microsoft.Win32.RegistryValueKind]::DWord
                )
                $newProfileKey.Flush()

                $restoredRawPath = $newProfileKey.GetValue(
                    'ProfileImagePath',
                    $null,
                    [Microsoft.Win32.RegistryValueOptions]::
                        DoNotExpandEnvironmentNames
                ) -as [string]

                if (-not [string]::Equals(
                    $rawPath,
                    $restoredRawPath,
                    [StringComparison]::OrdinalIgnoreCase
                )) {
                    throw 'The restored ProfileImagePath could not be verified.'
                }

                $newProfileKey.Dispose()
                $newProfileKey = $null
                $backupKey.Dispose()
                $backupKey = $null
                $profileListKey.DeleteSubKeyTree("$Sid.bak", $false)
            }
            catch {
                if ($null -ne $newProfileKey) {
                    $newProfileKey.Dispose()
                    $newProfileKey = $null
                }

                if ($destinationCreated) {
                    try {
                        $profileListKey.DeleteSubKeyTree($Sid, $false)
                    }
                    catch {
                        Write-RepairWarning (
                            "Rollback of the new ProfileList entry '$Sid' " +
                            'also failed. Inspect ProfileList manually.'
                        )
                    }
                }

                throw
            }
        }

        return $profilePath
    }
    finally {
        if ($null -ne $backupKey) {
            $backupKey.Dispose()
        }

        if ($null -ne $profileKey) {
            $profileKey.Dispose()
        }

        if ($null -ne $profileListKey) {
            $profileListKey.Dispose()
        }

        if ($null -ne $baseKey) {
            $baseKey.Dispose()
        }
    }
}


function Repair-DirectFileInheritance {
    param(
        [Parameter(Mandatory)]
        [string] $Path,

        [Parameter(Mandatory)]
        [System.Security.Principal.SecurityIdentifier] $SecurityIdentifier
    )

    $security = [System.IO.Directory]::GetAccessControl(
        $Path,
        [System.Security.AccessControl.AccessControlSections]::Access
    )
    $rules = @(
        $security.GetAccessRules(
            $true,
            $false,
            [System.Security.Principal.SecurityIdentifier]
        ) | Where-Object {
            $_.IdentityReference -eq $SecurityIdentifier -and
            -not $_.IsInherited
        }
    )
    $requiredMask = [int] $script:requiredFileRights
    $requiredDirectoryMask = [int] $script:requiredDirectoryRights
    $changed = $false

    foreach ($rule in $rules) {
        $appliesToFiles = (
            $rule.InheritanceFlags -band
            [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
        ) -ne 0
        $appliesToDirectory = (
            $rule.PropagationFlags -band
            [System.Security.AccessControl.PropagationFlags]::InheritOnly
        ) -eq 0

        if (
            $rule.AccessControlType -ne
                [System.Security.AccessControl.AccessControlType]::Deny
        ) {
            continue
        }

        $conflictingFileMask = if ($appliesToFiles) {
            ([int] $rule.FileSystemRights) -band $requiredMask
        }
        else {
            0
        }
        $conflictingDirectoryMask = if ($appliesToDirectory) {
            ([int] $rule.FileSystemRights) -band $requiredDirectoryMask
        }
        else {
            0
        }
        $conflictingMask = (
            $conflictingFileMask -bor $conflictingDirectoryMask
        )

        if ($conflictingMask -eq 0) {
            continue
        }

        [void] $security.RemoveAccessRuleSpecific($rule)
        $remainingMask = (
            [int] $rule.FileSystemRights
        ) -band (-bnot $requiredMask)

        if ($remainingMask -ne 0) {
            $security.AddAccessRule(
                [System.Security.AccessControl.FileSystemAccessRule]::new(
                    $SecurityIdentifier,
                    [System.Security.AccessControl.FileSystemRights] $remainingMask,
                    $rule.InheritanceFlags,
                    $rule.PropagationFlags,
                    [System.Security.AccessControl.AccessControlType]::Deny
                )
            )
        }

        $directoryOnlyMask = if ($appliesToDirectory) {
            $conflictingFileMask -band (-bnot $requiredDirectoryMask)
        }
        else {
            0
        }

        if ($directoryOnlyMask -ne 0) {
            $security.AddAccessRule(
                [System.Security.AccessControl.FileSystemAccessRule]::new(
                    $SecurityIdentifier,
                    [System.Security.AccessControl.FileSystemRights] $directoryOnlyMask,
                    [System.Security.AccessControl.AccessControlType]::Deny
                )
            )
        }

        $childDirectoryMask = if (
            ($rule.InheritanceFlags -band
                [System.Security.AccessControl.InheritanceFlags]::ContainerInherit
            ) -ne 0
        ) {
            $conflictingMask
        }
        else {
            0
        }

        if (
            $childDirectoryMask -ne 0
        ) {
            $childPropagation = (
                [System.Security.AccessControl.PropagationFlags]::InheritOnly -bor
                ($rule.PropagationFlags -band
                    [System.Security.AccessControl.PropagationFlags]::NoPropagateInherit
                )
            )
            $security.AddAccessRule(
                [System.Security.AccessControl.FileSystemAccessRule]::new(
                    $SecurityIdentifier,
                    [System.Security.AccessControl.FileSystemRights] $childDirectoryMask,
                    [System.Security.AccessControl.InheritanceFlags]::ContainerInherit,
                    $childPropagation,
                    [System.Security.AccessControl.AccessControlType]::Deny
                )
            )
        }

        $changed = $true
    }

    $allowedMask = 0

    foreach ($rule in $rules) {
        $isDirectFileRule = (
            $rule.AccessControlType -eq
                [System.Security.AccessControl.AccessControlType]::Allow -and
            ($rule.InheritanceFlags -band
                [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
            ) -ne 0 -and
            ($rule.InheritanceFlags -band
                [System.Security.AccessControl.InheritanceFlags]::ContainerInherit
            ) -eq 0 -and
            ($rule.PropagationFlags -band
                [System.Security.AccessControl.PropagationFlags]::InheritOnly
            ) -ne 0 -and
            ($rule.PropagationFlags -band
                [System.Security.AccessControl.PropagationFlags]::NoPropagateInherit
            ) -ne 0
        )

        if ($isDirectFileRule) {
            $allowedMask = $allowedMask -bor [int] $rule.FileSystemRights
        }
    }

    $missingMask = $requiredMask -band (-bnot $allowedMask)

    if ($missingMask -ne 0) {
        # ObjectInherit without ContainerInherit targets files only;
        # NoPropagateInherit limits the rule to this directory level.
        $security.AddAccessRule(
            [System.Security.AccessControl.FileSystemAccessRule]::new(
                $SecurityIdentifier,
                [System.Security.AccessControl.FileSystemRights] $missingMask,
                [System.Security.AccessControl.InheritanceFlags]::ObjectInherit,
                (
                    [System.Security.AccessControl.PropagationFlags]::InheritOnly -bor
                    [System.Security.AccessControl.PropagationFlags]::NoPropagateInherit
                ),
                [System.Security.AccessControl.AccessControlType]::Allow
            )
        )

        $changed = $true
    }

    $allowedDirectoryMask = 0

    foreach ($rule in $rules) {
        $appliesToDirectory = (
            $rule.PropagationFlags -band
            [System.Security.AccessControl.PropagationFlags]::InheritOnly
        ) -eq 0

        if (
            $rule.AccessControlType -eq
                [System.Security.AccessControl.AccessControlType]::Allow -and
            $appliesToDirectory
        ) {
            $allowedDirectoryMask = (
                $allowedDirectoryMask -bor [int] $rule.FileSystemRights
            )
        }
    }

    $missingDirectoryMask = (
        $requiredDirectoryMask -band (-bnot $allowedDirectoryMask)
    )

    if ($missingDirectoryMask -ne 0) {
        $security.AddAccessRule(
            [System.Security.AccessControl.FileSystemAccessRule]::new(
                $SecurityIdentifier,
                [System.Security.AccessControl.FileSystemRights] $missingDirectoryMask,
                [System.Security.AccessControl.AccessControlType]::Allow
            )
        )

        $changed = $true
    }

    if ($changed) {
        [System.IO.Directory]::SetAccessControl($Path, $security)
    }
}


function Repair-ProfileFile {
    param(
        [Parameter(Mandatory)]
        [System.IO.FileInfo] $File,

        [Parameter(Mandatory)]
        [System.Security.Principal.SecurityIdentifier] $SecurityIdentifier
    )

    if (
        ($File.Attributes -band [System.IO.FileAttributes]::ReadOnly) -ne 0
    ) {
        $File.Attributes = (
            $File.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
        )
    }

    $security = [System.IO.File]::GetAccessControl(
        $File.FullName,
        [System.Security.AccessControl.AccessControlSections]::Access
    )
    $explicitRules = @(
        $security.GetAccessRules(
            $true,
            $false,
            [System.Security.Principal.SecurityIdentifier]
        ) | Where-Object {
            $_.IdentityReference -eq $SecurityIdentifier -and
            -not $_.IsInherited
        }
    )
    $requiredMask = [int] $script:requiredFileRights
    $changed = $false

    foreach ($rule in $explicitRules) {
        if (
            $rule.AccessControlType -ne
                [System.Security.AccessControl.AccessControlType]::Deny
        ) {
            continue
        }

        $conflictingMask = ([int] $rule.FileSystemRights) -band $requiredMask

        if ($conflictingMask -eq 0) {
            continue
        }

        [void] $security.RemoveAccessRuleSpecific($rule)
        $remainingMask = (
            [int] $rule.FileSystemRights
        ) -band (-bnot $requiredMask)

        if ($remainingMask -ne 0) {
            $security.AddAccessRule(
                [System.Security.AccessControl.FileSystemAccessRule]::new(
                    $SecurityIdentifier,
                    [System.Security.AccessControl.FileSystemRights] $remainingMask,
                    $rule.AccessControlType
                )
            )
        }

        $changed = $true
    }

    $allowedMask = 0
    $allowRules = $security.GetAccessRules(
        $true,
        $true,
        [System.Security.Principal.SecurityIdentifier]
    ) | Where-Object {
        $_.IdentityReference -eq $SecurityIdentifier -and
        $_.AccessControlType -eq
            [System.Security.AccessControl.AccessControlType]::Allow
    }

    foreach ($rule in $allowRules) {
        $allowedMask = $allowedMask -bor [int] $rule.FileSystemRights
    }

    $missingMask = $requiredMask -band (-bnot $allowedMask)

    if ($missingMask -ne 0) {
        $security.AddAccessRule(
            [System.Security.AccessControl.FileSystemAccessRule]::new(
                $SecurityIdentifier,
                [System.Security.AccessControl.FileSystemRights] $missingMask,
                [System.Security.AccessControl.AccessControlType]::Allow
            )
        )

        $changed = $true
    }

    if ($changed) {
        [System.IO.File]::SetAccessControl($File.FullName, $security)
    }
}


function Repair-SiteKioskProfile {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [PSCustomObject] $User
    )

    Write-RepairHost ''
    Write-RepairHost '----------------------------------------'
    Write-RepairHost "Sources: $($User.Sources -join ', ')"
    Write-RepairHost "User:    $($User.Domain)\$($User.UserName)"
    Write-RepairHost "SID:     $($User.Sid)"

    $profilePath = Get-OrRepairProfilePath `
        -Sid $User.Sid `
        -AccountName "$($User.Domain)\$($User.UserName)"

    if ([string]::IsNullOrWhiteSpace($profilePath)) {
        throw (
            'No user profile exists for this account yet. This script cannot ' +
            'repair this problem. Wait for a newer SiteKiosk installer that ' +
            'can create the required user profile.'
        )
    }

    Write-RepairHost "Profile: $profilePath"

    if (-not (Test-Path -LiteralPath $profilePath -PathType Container)) {
        throw (
            "The registered user profile does not exist: $profilePath. " +
            'This script cannot repair this problem. Wait for a newer ' +
            'SiteKiosk installer that can create the required user profile.'
        )
    }

    if (
        (Test-Path -LiteralPath "Registry::HKEY_USERS\$($User.Sid)") -and
        -not $WhatIfPreference
    ) {
        throw (
            "The profile of '$($User.Domain)\$($User.UserName)' " +
            'is currently loaded. Sign out the user and run the ' +
            'script again.'
        )
    }

    if (Test-Path -LiteralPath "Registry::HKEY_USERS\$($User.Sid)") {
        Write-RepairWarning (
            "The profile of '$($User.Domain)\$($User.UserName)' is " +
            'currently loaded. WhatIf will only preview the repair.'
        )
    }

    $locations = @(
        $profilePath,
        (Join-Path $profilePath 'AppData\Local\Microsoft\Windows')
    )

    $files = foreach ($location in $locations) {
        if (Test-Path -LiteralPath $location -PathType Container) {
            Get-ChildItem `
                -LiteralPath $location `
                -Force `
                -File
        }
    }

    $files = @($files | Sort-Object FullName -Unique)

    # NTUSER.DAT and UsrClass.dat may not exist yet. Repair all files that are
    # present without treating missing profile hive files as an error.
    $securityIdentifier = [System.Security.Principal.SecurityIdentifier]::new(
        $User.Sid
    )

    foreach ($location in $locations) {
        if (-not (Test-Path -LiteralPath $location -PathType Container)) {
            continue
        }

        if (
            $PSCmdlet.ShouldProcess(
                $location,
                'Repair access inheritance for direct profile files'
            )
        ) {
            Write-RepairHost "Repairing direct-file inheritance: $location"
            Repair-DirectFileInheritance `
                -Path $location `
                -SecurityIdentifier $securityIdentifier
        }
    }

    foreach ($file in $files) {
        if (
            $PSCmdlet.ShouldProcess(
                $file.FullName,
                'Repair user profile permissions'
            )
        ) {
            Write-RepairHost "Repairing: $($file.FullName)"
            Repair-ProfileFile `
                -File $file `
                -SecurityIdentifier $securityIdentifier
        }
    }
}


Initialize-RepairLog
if (-not [string]::IsNullOrWhiteSpace($repairLogPath)) {
    Write-RepairHost "Log file: $repairLogPath"
}

try {
    if (-not (Test-RunningElevated)) {
        throw (
            'Administrator privileges are required. Open PowerShell with ' +
            '"Run as administrator" and execute the script again.'
        )
    }

    $users = @(
        if ($PSCmdlet.ParameterSetName -eq 'Explicit') {
            ConvertTo-ResolvedUser -Candidate ([PSCustomObject]@{
                UserName = $UserName
                Domain   = $Domain
                Source   = 'Explicitly specified user'
            })
        }
        else {
            Get-SiteKioskUsers
        }
    )

    if ($users.Count -eq 0) {
        if ($PSCmdlet.ParameterSetName -eq 'Explicit') {
            throw "The specified account '$Domain\$UserName' was not found."
        }
        else {
            throw (
                'No configured SiteKiosk Online user, installer-created SiteKiosk Online ' +
                'user, restricted SiteKiosk Classic user, or installer-created ' +
                'SiteKiosk Classic user was found.'
            )
        }
    }

    $failedUsers = @()

    foreach ($user in $users) {
        try {
            Repair-SiteKioskProfile -User $user
        }
        catch {
            $failedUsers += $user

            Write-RepairError (
                "The repair failed for '$($user.Domain)\$($user.UserName)': " +
                $_.Exception.Message
            )
        }
    }

    if ($failedUsers.Count -gt 0) {
        throw (
            "The repair failed for $($failedUsers.Count) user profile(s)."
        )
    }

    Write-RepairHost ''
    Write-RepairHost 'All requested user profiles have been processed.'

    if ($Restart) {
        $computerName = if ([string]::IsNullOrWhiteSpace($env:COMPUTERNAME)) {
            'the local computer'
        }
        else {
            $env:COMPUTERNAME
        }

        if ($PSCmdlet.ShouldProcess($computerName, 'Restart computer')) {
            Write-RepairHost "Restarting computer '$computerName'."
            Restart-Computer -Force -Confirm:$false
        }
        else {
            Write-RepairHost "The requested restart of '$computerName' was not performed."
        }
    }
}
catch {
    if (-not $script:repairErrorWasLogged) {
        Write-RepairLog -Level ERROR -Message $_.Exception.ToString()
    }

    throw
}


Führen Sie anschließend einen Neustart des Rechners aus:

#PowerShell Befehl
Restart-Computer -Force

Kontakt

Bei weiteren Rückfragen wenden Sie sich gerne an unseren Support unter +49 (251) 846924-24 oder support-europe@sitekiosk.com.