How to Create a Restore Point in Windows with PowerShell

Creating system restore points is one of the most effective methods for ensuring operational resilience in Windows environments. Whether it’s a routine patch, a new driver installation, or a major configuration change, being able to roll back to a known good state is a safety net that administrators rely on.

While System Restore is typically associated with manual user actions or predefined Windows triggers, PowerShell provides a more controlled and automatable pathway. For managed service providers (MSPs) and IT professionals, the ability to automate restore point creation at scale is invaluable.

Background

System Restore, a long-standing feature of Windows, allows users to revert their system files and settings to a previous state without affecting personal files. However, relying on the default behavior can be limiting. Restore points may not be created frequently enough, or worse, the feature might be disabled altogether. This is where the featured PowerShell script steps in. Here’s a quick guide on how to perform system restore in windows.

The script, which supports Windows 10 and 11, is designed to automate the creation of restore points on the system drive. It includes mechanisms to handle edge cases—like when System Restore is disabled or registry constraints block frequent restore point creation. For MSPs managing diverse endpoint environments, this script ensures consistency and control, particularly during critical operations like software deployment or registry modifications.

The Script

#Requires -Version 5.1

<#
.SYNOPSIS
    This script will create a restore point for the system drive. If system restore is disabled, you can use the -EnableIfDisabled switch to enable it before creating the restore point.

.DESCRIPTION
    This script will create a restore point for the system drive. If system restore is disabled, you can use the -EnableIfDisabled switch to enable it before creating the restore point.

By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).

.PARAMETER -Description
    Description of the restore point to be created. Default is "NinjaOne Restore Point."

.PARAMETER -EnableIfDisabled
    Enable system restore if it is found to be disabled. The restore point will be created after. 

.PARAMETER -ForceCreateSystemRestorePoint
    Forcibly creates a system restore point in case the registry key for time between restore points would block the creation. Sets the registry key back to its previous state once finished.

.EXAMPLE
    (No Parameters)

    [Info] Testing current system restore status...
    [Info] System restore is enabled.
    [Info] Creating system restore point.
    [Info] Restore point successfully created:

    CreationTime           Description            SequenceNumber EventType           RestorePointType
    ------------           -----------            -------------- ---------           ----------------
    12/23/2024 12:40:09 PM NinjaOne Restore Point 16             BEGIN_SYSTEM_CHANGE APPLICATION_INSTALL

.EXAMPLE
    -EnableIfDisabled

    [Info] Testing current system restore status...
    [Info] Enabling System Restore on system drive C:
    [Info] Enabled System Restore on system drive C:
    [Info] Creating system restore point.
    [Info] Restore point successfully created:

    CreationTime           Description            SequenceNumber EventType           RestorePointType
    ------------           -----------            -------------- ---------           ----------------
    12/23/2024 12:40:09 PM NinjaOne Restore Point 16             BEGIN_SYSTEM_CHANGE APPLICATION_INSTALL

.EXAMPLE
    -ForceCreateSystemRestorePoint

    [Info] Testing current system restore status...
    [Info] System restore is enabled.
    [Info] Temporarily changing registry settings to allow for restore point creation.
    Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore\SystemRestorePointCreationFrequency changed from 1440 to 0
    [Info] Restore point successfully created:

    CreationTime          Description            SequenceNumber EventType           RestorePointType
    ------------          -----------            -------------- ---------           ----------------
    12/27/2024 1:07:17 PM NinjaOne Restore Point 15             BEGIN_SYSTEM_CHANGE APPLICATION_INSTALL

    Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore\SystemRestorePointCreationFrequency changed from 0 to 1440
    [Info] Registry changes have been undone.

.NOTES
    Minimum OS Architecture Supported: Windows 10, Windows 11
    Release Notes: Initial Release
#>

[CmdletBinding()]
param (
    [Parameter()]
    [string]$Description = "NinjaOne Restore Point",

    [Parameter()]
    [switch]$EnableIfDisabled = [System.Convert]::ToBoolean($env:enableIfDisabled),

    [Parameter()]
    [switch]$ForceCreateSystemRestorePoint = [System.Convert]::ToBoolean($env:ForceCreateSystemRestorePoint)
)

begin {
    
    if ($env:description -and $env:description -ne 'null'){
        $Description = $env:description
    }

    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    function Test-SystemRestore {
        $path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore\"
        
        if (Test-Path $path){
            try {
                $RegValue = Get-ItemPropertyValue -Path $path -Name "RPSessionInterval" -ErrorAction Stop
            }
            catch{
                # if error, assume false
                return $false
            }
        }
        else{
            # if the reg path does not exist, assume false
            return $false
        }

        if ($RegValue -ge 1) {
            return $true
        }
        else {
            return $false
        }
    }

    function Set-RegKey {
        param (
            $Path,
            $Name,
            $Value,
            [ValidateSet("DWord", "QWord", "String", "ExpandedString", "Binary", "MultiString", "Unknown")]
            $PropertyType = "DWord"
        )
    
        # Check if the specified registry path exists
        if (!(Test-Path -Path $Path)) {
            try {
                # If the path does not exist, create it
                New-Item -Path $Path -Force -ErrorAction Stop | Out-Null
            }
            catch {
                # If there is an error creating the path, output an error message and exit
                Write-Host "[Error] Unable to create the registry path $Path for $Name. Please see the error below!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
        }
    
        # Check if the registry key already exists at the specified path
        if (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue) {
            # Retrieve the current value of the registry key
            $CurrentValue = (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name
            if ($CurrentValue -eq $Value) {
                Write-Host "$Path\$Name is already the value '$Value'."
            }
            else {
                try {
                    # Update the registry key with the new value
                    Set-ItemProperty -Path $Path -Name $Name -Value $Value -Force -Confirm:$false -ErrorAction Stop | Out-Null
                }
                catch {
                    # If there is an error setting the key, output an error message and exit
                    Write-Host "[Error] Unable to set registry key for $Name at $Path. Please see the error below!"
                    Write-Host "[Error] $($_.Exception.Message)"
                    exit 1
                }
                # Output the change made to the registry key
                Write-Host "$Path\$Name changed from $CurrentValue to $((Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
            }
        }
        else {
            try {
                # If the registry key does not exist, create it with the specified value and property type
                New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $PropertyType -Force -Confirm:$false -ErrorAction Stop | Out-Null
            }
            catch {
                # If there is an error creating the key, output an error message and exit
                Write-Host "[Error] Unable to set registry key for $Name at $Path. Please see the error below!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
            # Output the creation of the new registry key
            Write-Host "Set $Path\$Name to $((Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
        }
    }

    function Get-NextSystemRestorePointTime {
        try{
            $currentErrorActionPref = $ErrorActionPreference
            $ErrorActionPreference = "Stop"

            $date = @{Label = "Date"; Expression = {$_.ConvertToDateTime($_.CreationTime)}}

            $lastRestoreTime = (Get-ComputerRestorePoint | Sort-Object CreationTime | Sort-Object -Descending | Select-Object -First 1 | Select-Object $date).Date

            # if above is null, no restore points exist, so return a date in the past
            if (-not $lastRestoreTime){
                return (Get-Date).AddMinutes(-1)
            }
            
            $systemRestoreSettingsReg = "Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore"

            if (Test-Path $systemRestoreSettingsReg){
                $minutesBetweenRestores = (Get-ItemProperty $systemRestoreSettingsReg -Name "SystemRestorePointCreationFrequency" -ErrorAction SilentlyContinue).SystemRestorePointCreationFrequency

                # if no results from above, assume default of 24 hours/1440 minutes
                if ($null -eq $minutesBetweenRestores){
                    $minutesBetweenRestores = 1440
                }
            }
            else{
                # if the registry path does not exist, assume default of 24 hours/1440 minutes
                $minutesBetweenRestores = 1440
            }

            $restoreTime = $lastRestoreTime.AddMinutes($minutesBetweenRestores)
            $ErrorActionPreference = $currentErrorActionPref
            return $restoreTime
        }
        catch {
            Write-Host "[Error] Error finding next system restore point time."
            Write-Host "$($_.Exception.Message)"
            exit 1
        }
    }
}
process {
    if (-not (Test-IsElevated)) {
        Write-Host -Object "[Error] Access Denied. Please run with Administrator privileges."
        exit 1
    }

    # check if system restore is enabled
    Write-Host "[Info] Testing current system restore status..."
    $systemRestoreStatus = Test-SystemRestore

    if ($systemRestoreStatus -eq $false -and -not $EnableIfDisabled){
        Write-Host "[Error] System restore is not enabled and -EnableIfDisabled was not used. Please run again with the -EnableIfDisabled option."
        exit 1
    }
    elseif ($systemRestoreStatus -eq  $false){
        try {
            Write-Host "[Info] Enabling System Restore on system drive $env:SystemDrive"
            Enable-ComputerRestore -Drive $env:SystemDrive -ErrorAction Stop
            Write-Host "[Info] Enabled System Restore on system drive $env:SystemDrive"
        }
        catch {
            Write-Host "[Error] Failed to enable System Restore:"
            Write-Host "$($_.Exception.Message)"
            exit 1
        }
    }
    else{
        Write-Host "[Info] System restore is enabled."
    }
    
    # create restore point if system restore is enabled
    try {
        $nextTime = Get-NextSystemRestorePointTime

        # returns true if the next valid system restore point time has already passed
        $readyToCreate = $nextTime -lt (Get-Date)

        if (-not $readyToCreate -and -not $ForceCreateSystemRestorePoint){
            Write-Host "[Error] Due to registry settings, you must wait until $nextTime to create a new restore point."
            Write-Host "You can change this by creating or modifying the SystemRestorePointCreationFrequency DWORD value (measured in minutes) at HKLM:\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore."
            Write-Host "Or use the -ForceCreateSystemRestorePoint switch on this script."
            exit 1
        }
        else{
            if ($readyToCreate){
                Write-Host "[Info] Creating system restore point."

                Checkpoint-Computer -Description $Description -ErrorAction Stop

                # create restore point and output info
                Write-Host "[Info] Restore point successfully created:`n"
                (Get-ComputerRestorePoint | Sort-Object CreationTime | Select-Object -Last 1 | Format-Table -AutoSize | Out-String).Trim() + ("`n") | Write-Host
                $ExitCode = 0
            }
            else{
                $registryChanged = $false
                $regPath = "Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore"
                $key = "SystemRestorePointCreationFrequency"
                $currentValue = (Get-ItemProperty -Path $regPath -Name $key -ErrorAction SilentlyContinue).$Key

                Write-Host "[Info] Temporarily changing registry settings to allow for restore point creation."

                Set-RegKey -Path $regPath -Name $key -PropertyType DWord -Value 0
                $registryChanged = $true

                # create restore point and output info
                Checkpoint-Computer -Description $Description -ErrorAction Stop

                Write-Host "[Info] Restore point successfully created:`n"
                (Get-ComputerRestorePoint | Sort-Object CreationTime | Select-Object -Last 1 | Format-Table -AutoSize | Out-String).Trim() + ("`n") | Write-Host
                $ExitCode = 0
            }
        }
    }
    catch {
        Write-Host "[Error] Failed to create a system restore point."
        Write-Host "$($_.Exception.Message)"
        $ExitCode = 1
    }

    # set registry key back if it was changed, if it did not exist before it will be deleted
    if ($registryChanged){
        if ($null -eq $currentValue){
            Remove-ItemProperty -Path $regPath -Name $key
        }
        else{
            Set-RegKey -Path $regPath -name $key -PropertyType Dword -Value $currentValue
        }
        Write-Host "[Info] Registry changes have been undone."
    }
    exit $ExitCode
}
end {
    
    
    
}

 

Detailed Breakdown

Here’s how the script functions in detail:

  1. Initialization & Parameters

    The script accepts three optional parameters:

    • -Description: Custom text for the restore point.
    • -EnableIfDisabled: Enables System Restore if it’s currently off.
    • -ForceCreateSystemRestorePoint: Bypasses the default 24-hour limit between restore points by temporarily altering a registry key.
  2. Privilege Check

    The script checks if it is running with administrative privileges—a requirement for interacting with System Restore.

  3. System Restore Status

    It evaluates if System Restore is active using a registry check (RPSessionInterval). If disabled and -EnableIfDisabled is provided, it enables System Restore for the system drive.

  4. Timing Constraints

    Microsoft enforces a time-based restriction (default: 1440 minutes) between restore points. The script checks when the last point was created and compares it against this policy.

  5. Registry Modification (Optional)

    If the policy prevents a new point and -ForceCreateSystemRestorePoint is used, it sets the SystemRestorePointCreationFrequency value to 0 in the registry. This effectively disables the timing limitation.

  6. Restore Point Creation

    The script uses Checkpoint-Computer to create the restore point. Afterwards, it restores the registry to its original state to maintain default policies.

  7. Output and Exit

    Success or failure is logged with detailed output, including the creation timestamp and sequence number.

Potential Use Cases

Consider a scenario where a mid-sized MSP is deploying a major application update across 200 Windows 11 endpoints. Before the update, the team runs this script via a NinjaOne automation policy. In cases where System Restore is off, the script re-enables it.

If restore point timing policies prevent new ones, it overrides the restriction. Post-deployment, the team has peace of mind knowing that every machine can be restored to its pre-update state if necessary.

Comparisons

Traditionally, system restore points are created:

  • Manually via GUI: Not scalable.
  • Using Checkpoint-Computer alone: Lacks pre-checks, fails silently if restrictions apply.
  • Group Policy or Task Scheduler: Requires more setup and is less dynamic.

This script consolidates all these methods while addressing their shortcomings through automated checks and fallback logic.

Frequently Asked Questions

Q: What permissions are required?

A: The script must be run with administrator privileges to interact with system restore services and registry settings.

Q: What happens if the registry setting is modified and not restored?

A: The script ensures registry keys are reverted to their original values, preserving system policy integrity.

Q: Will this affect user data?

A: No. System Restore points only capture system files and settings, not personal user data.

Q: Can this be deployed remotely?

A: Yes. It’s compatible with RMM tools like NinjaOne for remote execution.

Implications

Creating regular restore points significantly enhances operational resilience. However, bypassing built-in safeguards, such as time-based restrictions, should be handled judiciously. Frequent restore point creation may lead to increased disk usage and performance degradation if not monitored.

Security-wise, having restore points makes rollback possible after malware infection or misconfiguration—but attackers aware of this might attempt to delete them. Administrators should ensure restore points are created with proper logging and access controls in place.

Recommendations

  • Use -ForceCreateSystemRestorePoint sparingly, only when timing restrictions interfere with critical tasks.
  • Automate execution with your RMM for consistent system states across your fleet.
  • Document restore points with meaningful descriptions.
  • Combine with scheduled tasks to create restore points before and after planned changes.
  • Audit and clean up old restore points regularly to avoid excessive disk usage.

If you want to undo System Restore here’s a quick guide

 How to Undo a System Restore in Windows 10/11

Final Thoughts

For IT professionals and MSPs seeking dependable ways to manage change in Windows environments, this PowerShell script offers a robust, intelligent approach to creating system restore points. Integrated into automation tools like NinjaOne, it becomes even more powerful—enabling wide-scale deployment, auditability, and reliable system rollback capabilities.

Whether used pre-patch, pre-deployment, or as part of a broader disaster recovery plan, this script exemplifies the best of PowerShell automation.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service delivery tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Categories:

You might also like

×

See NinjaOne in action!

By submitting this form, I accept NinjaOne's privacy policy.

NinjaOne Terms & Conditions

By clicking the “I Accept” button below, you indicate your acceptance of the following legal terms as well as our Terms of Use:

  • Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
  • Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
  • Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library belonging to or under the control of any other software provider.
  • Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
  • Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
  • Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
  • EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).