Scripting updates to the GPT.ini for the Local Group Policy

by Jeremy Saunders on August 26, 2008

Updated on 27th October 2010.

I came across a challenge whilst working on a big XenApp deployment in a Novell environment. I needed to make changes to the Local Group Policy Object on all Terminal / Citrix servers. This was specifically required for implementation of a logoff script, as logoff scripts are not supported via Zen Polices in a Terminal Services environment.

As always, I wanted to automate it and found that there was a real lack of information about how this can be easily achieved. Anyway, I thought that modifying the Local Policy on all XenApp servers would be a pain, but it ended up being very simple indeed.

Admittedly, I could have just copied a new gpt.ini and scripts.ini into place, but then it would not have respected any previous changes and settings that had already been made to the gpt.ini. So this script checks existing configuration settings before making any changes to the gpt.ini file.

Enjoy!!

'------------------------------------------------------------------------------------------------------------
' This script is used to add Logon and Logoff scripts to a local Group Policy.

' It modifies/creates the following two files needed to run logon and logoff scripts:
' - "%SystemRoot%\system32\GroupPolicy\gpt.ini"
' - "%SystemRoot%\system32\GroupPolicy\User\Scripts\scripts.ini"

' Notes:
'        - This script will modify the existing gpt.ini file, but overwrites an existing scripts.ini file.
'          It does NOT append or modify the scripts.ini file.
'        - This script does not copy the actual logon/logoff scripts into place.

' The motivation to write this script was to overcome some challenges in a Novell environment due to a
' limitation of the integration with Novell Zen Policies, where logoff scripts are NOT supported on
' Terminal Servers.

' Release 1.1 Modified by Jeremy@jhouseconsulting.com on 27th October 2010.
' Written by Jeremy@jhouseconsulting.com on 25th August 2008.

' When managing the policy versions you should adhere to the Microsoft standards. The version numbering
' differs for changes made to the User and Computer configuration. In order to track changes to each
' configuration, the GPO must track a version number for each configuration. With only one version number,
' the way two versions are tracked is to split the version number into two numbers.
' The top 16 bits of the version number corresponds to the user configuration version. The lower 16 bits
' of the version number corresponds to the computer configuration version. When looking at the version
' entry in the gpt.ini file what you are then seeing is:
' Version = [user version number top 16 bits] [computer version number lower 16 bits] 
' Esentially, each change to the User policy will increment the version by 131072, whilst each change to
' the Computer policy will increment the version by 1.
'
' http://blogs.technet.com/grouppolicy/archive/2007/12/14/understanding-the-gpo-version-number.aspx
'
' For example: In this script we are making a change to the User portion of the policy by adding a
' logon and logoff script. Therefore we will be incrementing the version number by 131072.
'
' We also need to add the Client Side Extension (CSE) GUIDs to instructs the winlogon process
' to run the Startup/Shutdown/Logon/Logoff scripts.
' CSE GUID for script processing = {42B5FAAE-6536-11D2-AE5A-0000F87571E3}
' Tool extension GUID for user and computer policy mode settings = {40B66650-4972-11D1-A7CA-0000F87571E3}
'
' gPCUserExtensionNames - includes a list of globally unique identifiers (GUIDs) that tells the client-side
' engine which client-side extensions have User data in the Group Policy object. The format is the following:
' [{<GUID of client-side extension>}{<GUID of MMC extension>}{<GUID of second MMC extension if appropriate>}]
'
' For example: In this script we are adding logon and logoff scripts. Therefore the gPCUserExtensionNames
' will be set to the following at minimum:
' gPCUserExtensionNames=[{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B66650-4972-11D1-A7CA-0000F87571E3}]
'
' gPCMachineExtensionNames - includes a list of GUIDs that tells the client side engine which Client Side
' Extensions have Machine data in the GPO.
' The default GUIDs are as follows.
' gPCMachineExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{0F6B957D-509E-11D1-A7CC-0000F87571E3}]
'
' gPCFunctionalityVersion - The Version number of the Group Policy extension tool that created the Group
' Policy object.
' The default version is 2.
'
' The Option setting has 4 values.
' 0 = Enable User and Computer Configurations
' 1 = Disable User Configuration ONLY
' 2 = Disable Computer Configuration ONLY
' 3 = Disable User and Computer Configurations
' If the Option value is not present, the User and Computer Configurations are both enabled.
'------------------------------------------------------------------------------------------------------------

Option Explicit

Dim WshShell, strSystemRoot, strGPOLocation, objFSO, objFile, blnOption, strLine, strContents
Dim arrLogonScripts, arrLogoffScripts, strValue, intUserPolicy, intComputerPolicy, blnUserPolicy
Dim blnComputerPolicy, strCSEUserGUID, strToolextensionUserGUID, blnSection, blnUserExtensionNames
Dim blnVersion, blnFunctionality, blnMachineExtensionNames, i

Const ForReading = 1
Const ForWriting = 2

Set WshShell = WScript.CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

strSystemRoot = WshShell.ExpandEnvironmentStrings("%SystemRoot%")
strGPOLocation = strSystemRoot & "\system32\GroupPolicy\"

'********** These are the script variables that can be changed **********

' Setup the arrays for the location and names of the logon and logoff scripts.
' Note that if you don't want to use either one or the other, just leave the
' array blank, or comment the line out altogether.
arrLogonScripts = Array("%SystemRoot%\LoadQAT.cmd","%SystemRoot%\RestoreDesktopIconPositions.Cmd")
arrLogoffScripts = Array("%SystemRoot%\SaveQAT.cmd","%SystemRoot%\SaveDesktopIconPositions.Cmd")

'************************************************************************

strCSEUserGUID = "{42B5FAAE-6536-11D2-AE5A-0000F87571E3}"
strToolextensionUserGUID = "{40B66650-4972-11D1-A7CA-0000F87571E3}"

blnUserPolicy = True
blnComputerPolicy = False

intUserPolicy = 131072
intComputerPolicy = 1

blnSection = False
blnFunctionality = False
blnMachineExtensionNames = False
blnVersion = False
blnUserExtensionNames = False
blnOption = False

If objFSO.FileExists(strGPOLocation & "gpt.ini") Then
  Set objFile = objFSO.OpenTextFile(strGPOLocation & "gpt.ini", ForReading)

  Do While objFile.AtEndOfStream = False 
    strLine = objFile.ReadLine

    If Instr(strLine,"[General]") > 0 Then
      blnSection = True
    End If

    If Len(strLine) > 0 AND Instr(strLine,";") <> 1 AND Instr(strLine,"[General]") <> 1 Then
    strValue=split(strLine,"=")

      Select Case strValue(0)
        Case "gPCFunctionalityVersion"
          blnFunctionality = True
        Case "gPCMachineExtensionNames"
          blnMachineExtensionNames = True
        Case "Version"
          If blnUserPolicy Then
            strValue(1)=strValue(1) + intUserPolicy
          End If
          blnVersion = True
        Case "gPCUserExtensionNames"
          If trim(strValue(1))="" Then
            strValue(1)="[" & strCSEUserGUID & strToolextensionUserGUID & "]"
            blnUserExtensionNames = True
          End If
          If instr(strValue(1),strCSEUserGUID) > 0 AND instr(strValue(1),strToolextensionUserGUID) > 0 Then
            blnUserExtensionNames = True
          Else
            If instr(strValue(1),strCSEUserGUID) = 0 Then
              If Right(trim(strValue(1)),1) = "]" Then
                strValue(1)=Left(trim(strValue(1)),Len(trim(strValue(1)))-1)
              End If
              strValue(1)=strValue(1) & strCSEUserGUID & "]"
              blnUserExtensionNames = True
            End If
            If instr(strValue(1),strToolextensionUserGUID) = 0 Then
              If Right(trim(strValue(1)),1) = "]" Then
                strValue(1)=Left(trim(strValue(1)),Len(trim(strValue(1)))-1)
              End If
              strValue(1)=strValue(1) & strToolextensionUserGUID & "]"
              blnUserExtensionNames = True
            End If
          End If
        Case "Options"
          If strValue(1)="0" Then
            blnOption = True
          End If
          If strValue(1)="1" Then
            strValue(1)="0"
            blnOption = True
          End If
          If strValue(1)="2" Then
            blnOption = True
          End If
          If strValue(1)="3" Then
            strValue(1)="2"
            blnOption = True
          End If
      End Select
      strContents = strContents & trim(strValue(0)) & "=" & trim(strValue(1)) & VbCrLf
    Else
      strContents = strContents & strLine & VbCrLf
    End If
  Loop
  objFile.Close
End If

If blnSection = False Then
  strContents="[General]" & VbCrLf & strContents
End If
If blnFunctionality = False Then
  strContents=strContents & "gPCFunctionalityVersion=2" & VbCrLf
End If
If blnMachineExtensionNames = False Then
  strContents=strContents & "gPCMachineExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{0F6B957D-509E-11D1-A7CC-0000F87571E3}]" & VbCrLf
End If
If blnVersion = False Then
  If blnUserPolicy Then
    strContents=strContents & "Version=131073" & VbCrLf
  Else
    strContents=strContents & "Version=1" & VbCrLf
  End If
End If
If blnUserExtensionNames = False Then
  strContents=strContents & "gPCUserExtensionNames=[" & strCSEUserGUID & strToolextensionUserGUID & "]" & VbCrLf
End If
If blnOption = False Then
  strContents=strContents & "Options=0" & VbCrLf
End If

Set objFile = objFSO.OpenTextFile(strGPOLocation & "gpt.ini", ForWriting, True)
objFile.Write(strContents)
objFile.Close
Set objFile = Nothing

WScript.Echo "Local GPO Updated (gpt.ini): " & chr(34) & strGPOLocation & "gpt.ini" & chr(34)

If NOT objfso.FolderExists(strGPOLocation & "User") Then
  objfso.CreateFolder(strGPOLocation & "User")
End If
If NOT objfso.FolderExists(strGPOLocation & "User\Scripts") Then
  objfso.CreateFolder(strGPOLocation & "User\Scripts")
End If
If NOT objfso.FolderExists(strGPOLocation & "User\Scripts\Logoff") Then
  objfso.CreateFolder(strGPOLocation & "User\Scripts\Logoff")
End If
If NOT objfso.FolderExists(strGPOLocation & "User\Scripts\Logon") Then
  objfso.CreateFolder(strGPOLocation & "User\Scripts\Logon")
End If

' Need to unhide the scripts.ini file to prevent any "permission denied" errors.
If objfso.FileExists(strGPOLocation & "User\Scripts\scripts.ini") Then
  Set objFile = objFSO.GetFile(strGPOLocation & "User\Scripts\scripts.ini")
  If objFile.Attributes = objFile.Attributes AND 2 Then
    objFile.Attributes = objFile.Attributes XOR 2
  End If
  Set objFile = Nothing
End If

strContents = "[Logon]" & VbCrLf
If IsArray(arrLogonScripts) Then
  For i = 0 to ubound(arrLogonScripts)
    strContents = strContents & i & "CmdLine=" & arrLogonScripts(i) & VbCrLf
    strContents = strContents & i & "Parameters=" & VbCrLf
  Next
End If
strContents = strContents & "[Logoff]" & VbCrLf
If IsArray(arrLogoffScripts) Then
  For i = 0 to ubound(arrLogoffScripts)
    strContents = strContents & i & "CmdLine=" & arrLogoffScripts(i) & VbCrLf
    strContents = strContents & i & "Parameters=" & VbCrLf
  Next
End If

On Error Resume Next
Set objFile = objFSO.OpenTextFile(strGPOLocation & "User\Scripts\scripts.ini", ForWriting, True)
If Err.Number = 0 Then
  objFile.Write(strContents)
  objFile.Close
  Set objFile = Nothing
  WScript.Echo "Scripts Added (scripts.ini): " & chr(34) & strGPOLocation & "User\Scripts\scripts.ini" & chr(34)
' Hide the scripts.ini file.
  Set objFile = objFSO.GetFile(strGPOLocation & "User\Scripts\scripts.ini")
  objFile.Attributes = objFile.Attributes XOR 2
  Set objFile = Nothing
Else
  wscript.echo "Error: " & Err.Description & ". The scripts.ini file has not been set."
End If
On Error Goto 0

Set WshShell = Nothing
Set objFSO = Nothing

wscript.quit (0)
Jeremy Saunders

Jeremy Saunders

Independent Consultant | Contractor | Microsoft & Citrix Specialist | Desktop Virtualization Specialist at J House Consulting
Jeremy is a highly respected, IT Professional, with over 30 years’ experience in the industry. He is an independent IT consultant providing expertise to enterprise, corporate, higher education and government clients. His skill set, high ethical standards, integrity, morals and attention to detail, coupled with his friendly nature and exceptional design and problem solving skills, makes him one of the most highly respected and sought after Microsoft and Citrix technical resources in Australia. His alignment with industry and vendor best practices puts him amongst the leaders of his field.
Jeremy Saunders
Jeremy Saunders
Jeremy Saunders
  • Manel Rodero

    Hello,

    I have a question about how the computer knows what’s the logoff and logon files. You have an string “strLogonScript” but is not used anywhere in the script.

    OK, you set the different settings in “gpt.ini” but I don’t understand how the computer knows that the logoff script is “%SystemRoot%\SaveQAT.cmd”.

    Can you explain this?

    Thanks.

  • jeremy

    Hi Manel,

    Yeah…sorry, I wasn’t clear enough in my explanation. This script just modifies the “%SystemRoot%\system32\GroupPolicy\gpt.ini”.

    You also need to run the script from the http://www.jhouseconsulting.com/2008/08/06/running-logoff-scripts-in-novell-environments-36 post. That script modifies the “%SystemRoot%\system32\GroupPolicy\User\Scripts\scripts.ini”.

    I was planning on merging both scripts, and is why there is a strLogonScript variable not in use in this script. I did this two years ago for one customer, and have had no need to revisit it since.

    Running both scripts will give you the results you want.

    Cheers,
    Jeremy.

  • Pingback: Running Logoff Scripts in Novell Environments()

  • Pingback: Add Group Policy by Script - Admins Goodies()

  • Tomas

    Hi Jeremy,

    I am wonder if you can help me. Hole day I was looking for implementing logoff script to WindowsXpSp3 withou using domain or gpedit. After many craps I found out your great script http://www.jhouseconsulting.com/2008/08/26/scripting-updates-to-the-gptini-for-the-local-group-policy-39 .
    Looks like you are one of few which know what is about…I mean gpolicy behavior.
    Unfortunetlly even script ends with OK status and all files looks good, the policy dosnt work until I open gpedit, then edit logoff script and just clikc to OK.

    I know it is few years from you did that script, but maybe you have some tip…I am getting be desperate:-)

    Anyway good day to you
    Tomas N.

    • Jeremy

      Hi Thomas,

      Sure. I’ve only tested this on Windows 2003 servers, so there may be a quirk with Windows XP, especially with SP3 applied. However, it sounds like the gpt.ini file may not be 100% correct, such as the version. It sounds like something basic like that. Take a copy of the gpt.ini and scripts.ini files and compare the working and not working versions. Check them very closely. If there is a difference, copy the lines from the working ones to manually make the change. Let me know if that works, and what the difference was and I’ll see if I can update and test the script.

      Cheers,
      Jeremy.

      • Tomas

        Hi Jeremy,

        ini files are correct (only Version in gpt.ini growth about 131072). So far I found out that after manual editing in gp-console, reg key appeared in registry:

        [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\System\Scripts\Logoff]

        [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\System\Scripts\Logoff]
        “GPO-ID”=”LocalGPO”
        “SOM-ID”=”Local”
        “FileSysPath”=”C:\\WINDOWS\\System32\\GroupPolicy\\User”
        “DisplayName”=”Local Group Policy”
        “GPOName”=”Local Group Policy”

        [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\System\Scripts\Logoff]
        “Script”=”%HOMESHARE%\\script.bat”
        “Parameters”=””
        “ExecTime”=hex(b):00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00

        I tried import this registry key before run your vbs script, but system still ignore log-off script. So I guess there must another element (probably in registry) which play some role here 🙂 I wish I found him…

        Regards Tomas

  • Senthil

    Script is working great Jeremy.

    Thank you very much.

    BTW, any plan to migrate this script to powershell?

    Regards,
    Senthil Kumar K

    • Hi Senthil,

      Thanks for the feedback. I’ll only convert it to PowerShell if I have a need to. But you’re more than welcome too 🙂

      Cheers,
      Jeremy

  • Oskar Sørensen

    Thank you for this script,

    I have succesfully run your script and added a logoff script, but how can I configure it to create a shutdown script instead, I don’t quite get the procedure outlined in the comments of the script, thank you.

    • Hi Oskar,

      I never had the time to revisit this and add the functionality to this script to help you further, but I hope the documentation I provided within the script helped guide you to the answer. The script was specifically written for logon and logoff functionality, but can relatively easily be modified for startup and shutdown functionality too.

      Cheers,
      Jeremy

  • Pascal Serveurperso

    Need the same thing for Win 8.1

    • Sorry, don’t have the time to look into that Pascal. You can take my logic and explanations and probably re-write it in PowerShell and extend it for Win 8.1.

      Cheers,
      Jeremy

      • Pascal Serveurperso

        Hi, I found myself a method : GPT.INI (ID and version +1) + reg add some Registry key and it work:D I need this because registry ACL need System Account to mod drivers keys (disable hotplug Capabilities) each boot on my XEN virtual machine!!!

Previous post:

Next post: