<# This script will create a dummy device instance path of ROOT\SCSIADAPTER\0000 so that the new SCSI Adapter is created on the next available instance path, reserving instance 0000 for the Citrix Virtual Hardware Disk Adapter (PVS). This is required as the Citrix Virtual Hardware Disk Adapter is a "SCSI Controller Bully" and will forcibly use an ID of 0000, overwriting/merging into any existing device. This not only breaks that device, but also the Citrix PVS image. The ActionToTake parameter should either be add or remove. - To add the instance path run... .\Reserve-SCSI-Adapter-Instance-ID.ps1 -ActionToTake:Add - To remove the instance path run... .\Reserve-SCSI-Adapter-Instance-ID.ps1 -ActionToTake:Remove Script name: Reserve-SCSI-Adapter-Instance-ID.ps1 Release 1.1 Written by Jeremy Saunders (jeremy@jhouseconsulting.com) 29th September 2018 Modified by Jeremy Saunders (jeremy@jhouseconsulting.com) 14th February 2024 #> #------------------------------------------------------------- param( [switch]$CreateTask=$True, [Parameter(Mandatory = $true)] [ValidateSet("Add", "Remove")] [string]$ActionToTake ) # Set Powershell Compatibility Mode Set-StrictMode -Version 2.0 # Enable verbose, warning and error mode $VerbosePreference = 'Continue' $WarningPreference = 'Continue' $ErrorPreference = 'Continue' $StartDTM = (Get-Date) #------------------------------------------------------------- # Set the working folder to the users TEMP folder $LogFolder = [System.IO.Path]::GetTempPath() # Get the script name $ScriptName = [System.IO.Path]::GetFilenameWithoutExtension($MyInvocation.MyCommand.Path.ToString()) # Start the transcript try { Start-Transcript "$LogFolder$ScriptName.log" } catch { Write-Verbose "$(Get-Date -format "dd/MM/yyyy HH:mm:ss"): This host does not support transcription" } #------------------------------------ # Get the current script path and name $ScriptPath = {Split-Path $MyInvocation.ScriptName} $ScriptPath = $(&$ScriptPath) $ScriptNamewithExtention = [System.IO.Path]::GetFilename($MyInvocation.MyCommand.Path.ToString()) #------------------------------------------------------------- # Both XMLTime functions achieve the same output. function XMLTime1{ # This function was posted by Thomas Lee (tfl@psp.co.uk) 24th September 2010 # - http://pshscripts.blogspot.com.au/2010/09/new-taskps1.html Param ($T) $csecond = $t.Second.ToString() $cminute = $t.minute.ToString() $chour = $t.hour.ToString() $cday = $t.day.ToString() $cmonth = $t.month.ToString() $cyear = $t.year.ToString() $date = $cyear + "-" if ($cmonth.Length -eq 1) { $date += "0" + $cmonth + "-"} else { $date += $cmonth + "-"} if ($cday.length -eq 1) { $date += "0" + $cday + "T"} else { $date += $cday + "T"} if ($chour.length -eq 1) { $date += "0" + $chour + ":"} else { $date += $chour + ":"} if ($cminute.length -eq 1){ $date += "0" + $cminute + ":"} else { $date += $cminute + ":"} if ($csecond.length -eq 1){ $date += "0" + $csecond} else { $date += $csecond} return $date } function XMLTime2 ([datetime] $d){ $d.Touniversaltime().tostring("u") -replace " ","T"} #------------------------------------------------------------- If ($CreateTask) { # The name of the scheduled task $TaskName = "Reserve-SCSI-Adapter-Instance-ID" # The task description $TaskDescription = "" # The Task Action command #$TaskCommand = "${env:SystemRoot}\system32\WindowsPowerShell\v1.0\powershell.exe" $TaskCommand = @(Get-Command powershell.exe)[0].Definition # The script to be executed $TaskScript = "$env:SystemRoot\Temp\$ScriptNamewithExtention" # The Task Action command argument $TaskArguments = '-Executionpolicy bypass -Command "& ' + " '" + $TaskScript + "'" +' -CreateTask:$False -ActionToTake:' + "'" + $ActionToTake + "'" + '"' # The Task working directory $TaskWorkingDirectory = "$env:SystemRoot\Temp" copy-item -path "$ScriptPath\$ScriptNamewithExtention" -Destination "$env:SystemRoot\Temp\" -Recurse -Force -Verbose # Create the TaskService object. Try { [Object] $service = new-object -com("Schedule.Service") If (!($service.Connected)){ Try { $service.Connect() # Get a folder to create a task definition in # This is actually the %SystemRoot%\System32\Tasks folder. $rootFolder = $service.GetFolder("\") # Delete the task if already present $ScheduledTasks = $rootFolder.GetTasks(0) $Task = $ScheduledTasks | Where-Object{$_.Name -eq "$TaskName"} If ($Task -ne $Null){ Try { $rootFolder.DeleteTask($Task.Name,0) # 'Success' } Catch [System.Exception]{ # 'Exception Returned' } } Else { # "Task Not Found" } # Create the new task $taskDefinition = $service.NewTask(0) # Create a registration trigger with a trigger type of (7) TASK_TRIGGER_REGISTRATION $triggers = $taskDefinition.Triggers $trigger = $triggers.Create(7) # Set the task to expire so it can be deleted automatically #$time = ([system.datetime]::now).addminutes(1) #$endTime = XMLTime1($time) #$trigger.EndBoundary = $endTime $trigger.EndBoundary = XMLTime2 ((Get-date).addminutes(1)) # Create the action for the task to execute. $Action = $taskDefinition.Actions.Create(0) $Action.Path = $TaskCommand $Action.Arguments = $TaskArguments If ($TaskWorkingDirectory -eq "") { $Action.WorkingDirectory = $TaskWorkingDirectory } # Set the settings for the task $settings = $taskDefinition.Settings # Set the Task Compatibility to V2 (Windows 7/2008R2) $Settings.Compatibility = 3 # Delete the task immediately (PT0M) after the trigger expires # as per the EndBoundary $settings.DeleteExpiredTaskAfter = "PT0M" # Set the privilege level # Principal.RunLevel -- 0 is least privilege, 1 is highest privilege # #$Principal = $taskDefinition.Principal #$Principal.RunLevel = 1 # Register (create) the task. # Note that the task is created as an XML file under the %SystemRoot%\System32\Tasks folder $regInfo = $taskDefinition.RegistrationInfo $regInfo.Description = $taskDescription $regInfo.Author = $Env:Username # Note that the task is created as an XML file under the %SystemRoot%\System32\Tasks folder # 6 == Task Create or Update # 5 == A Local System, Local Service, or Network Service account is being used as a security context to run the task. $rootFolder.RegisterTaskDefinition($taskName, $taskDefinition, 6, "System", $null , 5) | out-null write-verbose "Scheduled Task Created Successfully" -verbose } Catch [System.Exception]{ write-warning "Scheduled Task Creation Failed" -verbose # " EXCEPTION:" $_ } } } Catch [System.Exception]{ write-warning "Scheduled Task Creation Failed" -verbose # " EXCEPTION:" $_ } } If (!$CreateTask) { $Path = "HKLM:\SYSTEM\CurrentControlSet\Enum\ROOT\SCSIADAPTER\0000" $KeyExists = $False $ErrorActionPreference = "stop" try { Get-Item -Path "$Path" | Out-Null $KeyExists = $true } catch { # } $ErrorActionPreference = "Continue" If ($KeyExists -eq $False -AND $ActionToTake -eq "Add") { write-verbose "Adding new path: `"$path`"" -vebose New-Item -Path "$path" -Force | Out-Null } If ($KeyExists -eq $True -AND $ActionToTake -eq "Remove") { If ((Get-ItemProperty -Path "$Path" | Select-Object -ExpandProperty "Service") -eq $null) { write-verbose "Removing path: `"$path`"" -vebose Remove-Item -Path "$path" -Force | Out-Null } Else { write-verbose "Unable to remove as it's a valid device: `"$path`"" -verbose } } Remove-item -Path "$env:SystemRoot\Temp\$ScriptNamewithExtention" -force -confirm:$false } #------------------------------------ Write-Verbose "Stop logging" -Verbose $EndDTM = (Get-Date) Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalSeconds) Seconds" -Verbose Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose # Stop the transcript try { Stop-Transcript } catch { Write-Verbose "$(Get-Date -format "dd/MM/yyyy HH:mm:ss"): This host does not support transcription" }