180X IPU – Event Warning 1534 workaround

If you’re upgrading from 1709 or older to 1803 or newer, you might have noticed a bunch of 1534 Warnings in your event viewer for the User Profile Service

image

This seems to be a known issue with an upgrade from 1709 to 1809. Windows had a service called Tile Data Model Server in 1709 which was removed in the later version of the OS. Though the service was removed it seems that the subscription information for the Profile was not removed. Hence whenever the load/unload of the profile happens, the user profile service tries to reach out to the service which is not existent. Hence the error.

This issue does not occur if we have a clean build of 1809.

Work around… remove the subscription info in the registry:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileNotification\TDL

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\ProfileNotification\TDL

*I confirmed with MS Support that this workaround was ok.  I provided my script to them, and they said it was good.

Problem, you can’t just delete them, you need to take ownership, add permissions, then delete.. so two lines of code become much longer.. especially when you add logging..

This can be used in a package, or straight up script.  You’ll want to modify the log file location to make sense for the use case. (Or remove log function)

Setup: Run PowerShell Script some place after the machine has upgraded to 1809
image

Log File: I have it save to the ccm\logs directory.  I have a log SMSTS_PostActions that I log many of my Task Sequence scripts to that happen post upgrade, this gets appending into that log.
image

Code:

#Delete Keys to fix Event Warning ID 1534 (Need to Take OwnerShip First, then Remove)
#issue Description: https://answers.microsoft.com/en-us/windows/forum/all/windows-10-pro-version-1809-start-menu-wont-save/333655c9-9bf5-4ca1-809e-262bb2793ba8
#Most code from: https://social.technet.microsoft.com/Forums/windowsserver/en-US/e718a560-2908-4b91-ad42-d392e7f8f1ad/take-ownership-of-a-registry-key-and-change-permissions?forum=winserverpowershell

$LogFile = "C:\Windows\ccm\Logs\SMSTS_PostActions.log"
$ScriptName = $MyInvocation.MyCommand.Name

function enable-privilege {
 param(
  ## The privilege to adjust. This set is taken from
  ## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
  [ValidateSet(
   "SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege",
   "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege",
   "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege",
   "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege",
   "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege",
   "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", "SeManageVolumePrivilege",
   "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege",
   "SeRestorePrivilege", "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege",
   "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", "SeSystemtimePrivilege",
   "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege",
   "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")]
  $Privilege,
  ## The process on which to adjust the privilege. Defaults to the current process.
  $ProcessId = $pid,
  ## Switch to disable the privilege, rather than enable it.
  [Switch] $Disable
 )

 ## Taken from P/Invoke.NET with minor adjustments.
 $definition = @'
 using System;
 using System.Runtime.InteropServices;
  
 public class AdjPriv
 {
  [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
  internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
   ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
  
  [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
  internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
  [DllImport("advapi32.dll", SetLastError = true)]
  internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
  [StructLayout(LayoutKind.Sequential, Pack = 1)]
  internal struct TokPriv1Luid
  {
   public int Count;
   public long Luid;
   public int Attr;
  }
  
  internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
  internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
  internal const int TOKEN_QUERY = 0x00000008;
  internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
  public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
  {
   bool retVal;
   TokPriv1Luid tp;
   IntPtr hproc = new IntPtr(processHandle);
   IntPtr htok = IntPtr.Zero;
   retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
   tp.Count = 1;
   tp.Luid = 0;
   if(disable)
   {
    tp.Attr = SE_PRIVILEGE_DISABLED;
   }
   else
   {
    tp.Attr = SE_PRIVILEGE_ENABLED;
   }
   retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
   retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
   return retVal;
  }
 }
'@

 $processHandle = (Get-Process -id $ProcessId).Handle
 $type = Add-Type $definition -PassThru
 $type[0]::EnablePrivilege($processHandle, $Privilege, $Disable)
}

#region: CMTraceLog Function formats logging in CMTrace style
  
        function CMTraceLog {
         [CmdletBinding()]
    Param (
		    [Parameter(Mandatory=$false)]
		    $Message,
 
		    [Parameter(Mandatory=$false)]
		    $ErrorMessage,
 
		    [Parameter(Mandatory=$false)]
		    $Component = "EventID1534Fix",
 
		    [Parameter(Mandatory=$false)]
		    [int]$Type,
		
		    [Parameter(Mandatory=$true)]
		    $LogFile
	    )
    <#
    Type: 1 = Normal, 2 = Warning (yellow), 3 = Error (red)
    #>
	    $Time = Get-Date -Format "HH:mm:ss.ffffff"
	    $Date = Get-Date -Format "MM-dd-yyyy"
 
	    if ($ErrorMessage -ne $null) {$Type = 3}
	    if ($Component -eq $null) {$Component = " "}
	    if ($Type -eq $null) {$Type = 1}
 
	    $LogMessage = "<![LOG[$Message $ErrorMessage" + "]LOG]!><time=`"$Time`" date=`"$Date`" component=`"$Component`" context=`"`" type=`"$Type`" thread=`"`" file=`"`">"
	    $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile
    }


CMTraceLog -Message  "---Starting $ScriptName Script---" -Type 1 -LogFile $LogFile
$RegistryPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileNotification\TDL' 
if (Test-Path -Path $RegistryPath)
    {
    CMTraceLog -Message  "Registry Key $RegistryPath Exists" -Type 1 -LogFile $LogFile
    enable-privilege SeTakeOwnershipPrivilege 
    $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileNotification\TDL",[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::takeownership)
    # You must get a blank acl for the key b/c you do not currently have access
    $acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::None)
    $me = [System.Security.Principal.NTAccount]"SYSTEM"
    $acl.SetOwner($me)
    $key.SetAccessControl($acl)

    # After you have set owner you need to get the acl with the perms so you can modify it.
    $acl = $key.GetAccessControl()
    $rule = New-Object System.Security.AccessControl.RegistryAccessRule ("SYSTEM","FullControl","Allow")
    $acl.SetAccessRule($rule)
    $key.SetAccessControl($acl)

    $key.Close()
    Remove-Item -Path $RegistryPath -Recurse
    CMTraceLog -Message  "Deleting Key $RegistryPath" -Type 1 -LogFile $LogFile
    if (Test-Path -Path $RegistryPath)
        {
        CMTraceLog -Message  "Failed to Delete Key $RegistryPath" -Type 1 -LogFile $LogFile
        }
    Else
        {
        CMTraceLog -Message  "Successfully Deleted Key $RegistryPath" -Type 1 -LogFile $LogFile
        }
    }
$RegistryPathWoW = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\ProfileNotification\TDL' 
if (Test-Path -Path $RegistryPathWoW)
    {
    CMTraceLog -Message  "Registry Key $RegistryPathWoW Exists" -Type 1 -LogFile $LogFile
    enable-privilege SeTakeOwnershipPrivilege 
    $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\ProfileNotification\TDL",[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::takeownership)
    # You must get a blank acl for the key b/c you do not currently have access
    $acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::None)
    $me = [System.Security.Principal.NTAccount]"SYSTEM"
    $acl.SetOwner($me)
    $key.SetAccessControl($acl)

    # After you have set owner you need to get the acl with the perms so you can modify it.
    $acl = $key.GetAccessControl()
    $rule = New-Object System.Security.AccessControl.RegistryAccessRule ("SYSTEM","FullControl","Allow")
    $acl.SetAccessRule($rule)
    $key.SetAccessControl($acl)

    $key.Close()
    Remove-Item -Path $RegistryPathWoW -Recurse
    CMTraceLog -Message  "Deleting Key $RegistryPath" -Type 1 -LogFile $LogFile
    if (Test-Path -Path $RegistryPathWoW)
        {
        CMTraceLog -Message  "Failed to Delete Key $RegistryPathWoW" -Type 1 -LogFile $LogFile
        }
    Else
        {
        CMTraceLog -Message  "Successfully Deleted Key $RegistryPathWoW" -Type 1 -LogFile $LogFile
        }
    }
CMTraceLog -Message  "---Finished $ScriptName Script---" -Type 1 -LogFile $LogFile

 

I’m assuming an up coming CU from Microsoft will resolve this, but until then…
Posted on GARYTOWN.COM

3 thoughts on “180X IPU – Event Warning 1534 workaround”

  1. Hi Gary, thanks you for this script. I was having trouble deleting the second key and I noticed what I believe is a type in your script. I believe line #158 she be as follows –

    $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey(“SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\ProfileNotification\TDL”,[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::takeownership)

    Thanks

    Reply
    • Yeah, thank you. I noticed that the other day and forgot to update this post, I only updated the script in my exported package. Appreciate it Carl!

      Reply
  2. I kept getting this message with “SYSTEM”

    Exception calling “SetAccessControl” with “1” argument(s): “The security identifier is not allowed to be the owner of this
    object.”
    At C:\Users\me\Desktop\TDL.ps1:134 char:5
    + $key.SetAccessControl($acl)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : InvalidOperationException

    So changed it to “BUILTIN\Administrators” in the 4 places and worked great

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.