Shutdown/Startup script for Intune managed devices (via Local GPO)

We all know that startup, shutdown, logon or logoff scripts are not natively available within Intune. I actually don’t know if it’s even supported but it is possible! In this blog i’ll show you how to configure a startup or shutdown script using a local GPO.

Note: Screenshots and code examples are based on a shutdown script. I’ve only tested the shutdown method. I assume the startup method will work either.

Prepare the required source files

Source files consist of a Install script, uninstall script and the group policy object which are stored in a Win32 package.

1: we have to generate or build the script which should be ran while shutting down the device. For example: terminate teams.exe using taskkill. Save the script somewhere on the device (for example c:\Programdata\Scripts\Shutdown\shutdownscript.bat).

2: We need to create the local GPO manually. In this example i used a Intune managed device. The Local Group Policy Editor requires administrator permissions. you need to have local administrator permissions. Simply run cmd.exe as a local administrator and start gpedit.msc from there.

The Local Group Policy Editor opens. Navigate to Computer Configuration -> Windows Settings -> Scripts (Startup/Shutdown) -> Shutdown. Import the created script from step 1.

Note: At this point you should test the script/policy while shutting down the device. If it does not work at this moment it will not work in the end! Make sure the policy actually works before continuing!

3: Go back to your administrative cmd.exe and open the registery using regedit.

Navigate to HKLM\Software\Microsoft\Windows\CurrentVersion\Group Policy and export the following keys to a .reg file:

  • HKLM\Software\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown
  • HKLM\Software\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown

Save these files as Shutdown.reg and Shutdownstate.reg

4: Open Explorer and navigate to C:\Windows\System32\Group Policy\. This is a hidden folder! Enable hidden files/folders to see the folder in Explorer. Copy all files and folders to C:\Programdata\Scripts\GPO. Make sure gpt.ini and scripts.ini are included! Without these files nothing will happen in the end.

5: Create a new file using a text editor and name it Install-Shutdownscript.ps1. Implement the following code here. It simply does the following:

  • Creates the required folders in C:\Windows\System32\GroupPolicy
  • Creates the required folder C:\ProgramData\Scripts\Shutdown\GPO
  • Stores the required files from the Win32 package into C:\ProgramData\Scripts\Shutdown
  • Stores the required files from the Win32 package into C:\ProgramData\Scripts\Shutdown\GPO
  • Installs the required .reg files into the Windows Registry
  • Copies the required GPO files from C:\ProgramData\Scripts\Shutdown\GPO to C:\Windows\System32\GroupPolicy.
# Create required folders
if (-not (Test-Path "$($env:systemroot)\system32\GroupPolicy\User"))
{
    Mkdir "$($env:systemroot)\system32\GroupPolicy\User"
}

if (-not (Test-Path "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Startup"))
{
    Mkdir "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Startup"
}

if (-not (Test-Path "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Shutdown\0\0"))
{
    Mkdir "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Shutdown\0\0"
}

if (-not (Test-Path "$($env:ProgramData)\Scripts\Shutdown\GPO"))
{
    Mkdir "$($env:ProgramData)\Scripts\Shutdown\GPO"
}

# Copy required files to the local device
Copy-item -path "$psscriptroot\shutdownscript.bat" -Destination "$($env:ProgramData)\Scripts\Shutdown" -Confirm:$false -Force
Copy-item -path "$psscriptroot\ShutdownState.reg" -Destination "$($env:ProgramData)\Scripts\Shutdown" -Confirm:$false -Force
Copy-item -path "$psscriptroot\Shutdown.reg" -Destination "$($env:ProgramData)\Scripts\Shutdown" -Confirm:$false -Force
Copy-item -path "$psscriptroot\GPO\gpt.ini" -Destination "$($env:ProgramData)\Scripts\Shutdown\GPO" -Confirm:$false -Force
Copy-item -path "$psscriptroot\GPO\Machine\Scripts\scripts.ini" -Destination "$($env:ProgramData)\Scripts\Shutdown\GPO" -Confirm:$false -Force

# Define the path to your .reg file
$regFile1 = "C:\Programdata\Scripts\Shutdown\Shutdown.reg"
$regFile2 = "C:\Programdata\Scripts\Shutdown\ShutdownState.reg"

# Use reg.exe to import the .reg file
Start-Process -FilePath "reg.exe" -ArgumentList "import $regFile1"
Start-Process -FilePath "reg.exe" -ArgumentList "import $regFile2"

# Copy required GPO files
Copy-item -path "$psscriptroot\GPO\gpt.ini" -Destination "$($env:SystemRoot)\System32\GroupPolicy" -Confirm:$false -Force
Copy-item -path "$psscriptroot\GPO\Machine\Scripts\scripts.ini" -Destination "$($env:SystemRoot)\System32\GroupPolicy\MAchine\Scripts" -Confirm:$false -Force

6: Create a new file using a text editor and name it Uninstall-Shutdownscript.ps1. Implement the following code here. It simply does the following:

  • Removes the source files from the Intune Win32 package from C:\ProgramData\Scripts\Shutdown
  • Remove the required registry keys from the registry
  • Removes the GPO data from C:\Windows\System32\GroupPolicy\Shutdown
# Remove the local GPO script data
Remove-Item -Path "$($env:ProgramData)\Scripts\Shutdown\" -Recurse -Confirm:$False -Force -Erroraction SilentlyContinue

# Remove the local GPO 
Remove-Item -Path "$($env:systemroot)\System32\GroupPolicy\Machine\Scripts\Shutdown" -Recurse -Confirm:$False -Force -Erroraction SilentlyContinue

# Remove the Shutdown GPO Key
$registryPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\"

# Check if the registry key exists before removing it
if (Test-Path -Path $registryPath) {
    # Remove the registry key
    Remove-Item -Path $registryPath -Recurse
    Write-Host "Registry key removed successfully."
} else {
    Write-Host "Registry key not found."
}

# Remove the Shutdown GPO State Key
$registryPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown"

# Check if the registry key exists before removing it
if (Test-Path -Path $registryPath) {
    # Remove the registry key
    Remove-Item -Path $registryPath -Recurse
    Write-Host "Registry key removed successfully."
} else {
    Write-Host "Registry key not found."
}

While having these files created and stored in one single folder we can start creating the Intune Win32 Package. Check once again if the have all the files shown below! The Output folder is already created for the Intune Win32 Package.

Create the Intune Win32 Package

I Assume everyone reading this post is well known with creating Intune Win32 Packages. Therefor i won’t tell step by step what to do. Make sure you download the latest IntuneWinAppUtil.exe from this Github repo before creating the package: microsoft/Microsoft-Win32-Content-Prep-Tool: A tool to wrap Win32 App and then it can be uploaded to Intune (github.com)

Once we created the package using this command-line utility we have the following output file:

This is the package we are uploading to Intune as our Win32 application.

Deploy the Win32 Package

Select the recently created Install-Shutdownscript.intunewin file.

Modify the name and description if you like. Fill in a Publisher name which is required and click Next.

Use the following install and uninstall commands:

  • %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -executionpolicy Bypass -File .\Install-Shutdownscript.ps1
  • %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -executionpolicy Bypass -File .\Uninstall-Shutdownscript.ps1

Note: I assume you’re using a Windows 64 bit operating system. In that case you should be aware of the sysnative installation method. Read more: Intune Win32 App Deployment System32 Vs Syswow64 (anoopcnair.com)

Make sure you install the application under System context.

Configure the requirements.

Configure the detection rule. There are a few options which can be used. I’ll explain them later.

  • File: C:\ProgramData\Scripts\Shutdown\GPO\scripts.ini. This simply checks if the file is stored in the ProgramData location. This does not check if the GPO is successfully created! Therefor the second one would be better.
  • File: C:\Windows\System32\GroupPolicy\Machine\Scripts\scripts.ini. This checks if the script has copied the RAW files (needed by the Local GPO) are stored in the correct path which are required by the GPO.
  • Registry: Is the registry key created by the Win32 App? If yes, the installation is completed successfully. This is be the best one by far! Except, if you are using the optional Proactive Remediation which i show below.

Note: It is possible to add multiple detections like File and Registry together. This is not required! This script requires the script.ini file in C:\Windows\System32\GroupPolicy\Machine\Scripts\ and the registry keys in order to launch the shutdownscript. Therefor it would be recommended to combine these.

Proactive Remediation (Optionally)

Not everyone is allowed to use Proactive Remediations. This totally depends on the licenses you have available. For example Business Premium licensed users are not allowed to use them. This Proactive Remediation script can be added optionally to make sure the registry keys are created from the source files which are downloaded to the endpoint by the Win32 package.

Detection: Use the following detection script. This checks if the required files/registry for the local GPO are present. If not it will launch the remediation script.

$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\0\0"
$Name = "Script"
$Type = "STRING"
$Value = "C:\ProgramData\Scripts\Shutdown\Shutdownscript.bat"

Try {
    $Registry = Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop | Select-Object -ExpandProperty $Name
    If ($Registry -eq $Value){
        Write-Output "Compliant"
        Exit 0
    } 
    Write-Warning "Not Compliant"
    Exit 1
} 
Catch {
    Write-Warning "Not Compliant"
    Exit 1
}

Remediation: This remediation script imports the registry source files into the registry and copies the required files to correct location on the local system drive.

# Define the path to your .reg file
$regFile1 = "C:\Programdata\Scripts\Shutdown\Shutdown.reg"
$regFile2 = "C:\Programdata\Scripts\Shutdown\ShutdownState.reg"

# Use reg.exe to import the .reg file
Start-Process -FilePath "reg.exe" -ArgumentList "import $regFile1"
Start-Process -FilePath "reg.exe" -ArgumentList "import $regFile2"

# Create required folders
if (-not (Test-Path "$($env:systemroot)\system32\GroupPolicy\User"))
{
    Mkdir "$($env:systemroot)\system32\GroupPolicy\User"
}

if (-not (Test-Path "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Startup"))
{
    Mkdir "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Startup"
}

if (-not (Test-Path "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Shutdown\0\0"))
{
    Mkdir "$($env:systemroot)\system32\GroupPolicy\Machine\Scripts\Shutdown\0\0"
}

#Copy required files to destinations
$file1 = "C:\ProgramData\Scripts\Shutdown\GPO\gpt.ini"
$dest1 = "C:\Windows\system32\GroupPolicy\"
$file2 = "C:\ProgramData\Scripts\Shutdown\GPO\scripts.ini"
$dest2 = "C:\Windows\system32\GroupPolicy\Machine\Scripts"

Copy-item -Path $file1 -Destination $dest1 -Confirm:$false -Force
Copy-item -Path $file2 -Destination $dest2 -Confirm:$false -Force

Verify the Local GPO

As soon as the application is installed on the endpoint, we need to verify if the local GPO is actually shown in the Local Group Policy Editor. Open an administrative command prompt and run gpedit.msc. Navigate to Computer Configuration -> Windows Settings -> Scripts (Startup/Shutdown) -> Shutdown and verify if the script is shown!

That’s it! Your script and GPO should now work from intune using a Win32 Package!

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

eighteen + 4 =