diff --git a/Functions/Place_Additional_Functions_Here.txt b/Functions/Place_Additional_Functions_Here.txt new file mode 100644 index 0000000..fa7cc9c --- /dev/null +++ b/Functions/Place_Additional_Functions_Here.txt @@ -0,0 +1 @@ +### \ No newline at end of file diff --git a/Guides/Place_Additional_Guides_Here.txt b/Guides/Place_Additional_Guides_Here.txt new file mode 100644 index 0000000..fa7cc9c --- /dev/null +++ b/Guides/Place_Additional_Guides_Here.txt @@ -0,0 +1 @@ +### \ No newline at end of file diff --git a/Modules/Place_Additional_Modules_Here.txt b/Modules/Place_Additional_Modules_Here.txt new file mode 100644 index 0000000..fa7cc9c --- /dev/null +++ b/Modules/Place_Additional_Modules_Here.txt @@ -0,0 +1 @@ +### \ No newline at end of file diff --git a/Modules/SCCMTSClient/SCCMTSClient.psd1 b/Modules/SCCMTSClient/SCCMTSClient.psd1 new file mode 100644 index 0000000..b8180a0 Binary files /dev/null and b/Modules/SCCMTSClient/SCCMTSClient.psd1 differ diff --git a/Modules/SCCMTSClient/SCCMTSClient.psm1 b/Modules/SCCMTSClient/SCCMTSClient.psm1 new file mode 100644 index 0000000..42596fa --- /dev/null +++ b/Modules/SCCMTSClient/SCCMTSClient.psm1 @@ -0,0 +1,564 @@ +$Script:TaskSequenceEnvironment = $null +$Script:TaskSequenceProgressUi = $null + +function Confirm-TSEnvironmentSetup() + { + <# + .SYNOPSIS + Verifies the TSEnvironment Com Object is initiated into an object. + + .DESCRIPTION + Verifies the TSEnvironment Com Object is initiated into an object. + .INPUTS + None + .OUTPUTS + None + + .NOTE + This module can be used statically to initiate the TSEnvironment module, however, simply running one of the commands will initate it for you. + #> + + if ($Script:TaskSequenceEnvironment -eq $null) + { + try + { + $Script:TaskSequenceEnvironment = New-Object -ComObject Microsoft.SMS.TSEnvironment + } + catch + { + throw "Unable to connect to the Task Sequence Environment! Please verify you are in a running Task Sequence Environment.`n`nErrorDetails:`n$_" + } + } + } + +function Get-TSVariables() + { + <# + .SYNOPSIS + Get all Task Sequence Variable Names + + .DESCRIPTION + Returns a string array of all Task Sequence Variable Names (not values). + .INPUT + None + .OUTPUTS + String[] + .EXAMPLE + $arrayOfVariableNames = Get-TSVariables + #> + Confirm-TSEnvironmentSetup + + $allVar = @() + + foreach ($variable in $Script:TaskSequenceEnvironment.GetVariables()) + { + $allVar += $variable + } + + return $allVar + } + +function Get-TSValue() + { + <# + .SYNOPSIS + Get a Task Sequence Variables Value + + .DESCRIPTION + Obtains the value of a specific Task Sequence Variable. + + .PARAMETER Name + Specifies the variable name to resolve. + .INPUTS + String + .OUTPUTS + String + .EXAMPLE + $OsdComputerName = Get-TSValue -Name "OSDComputerName" + #> + param( + [Parameter(Mandatory=$true)] + [string] $Name + ) + + Confirm-TSEnvironmentSetup + + return $Script:TaskSequenceEnvironment.Value($Name) + } + +function Set-TSVariable() + { + <# + .SYNOPSIS + Set or Create a Task Sequence Variables Value + + .DESCRIPTION + Sets or Creates a Task Sequence Variables Value. + Will return a boolean value indicating success or failure. + + .PARAMETER Name + Specifies the variable name to set or create. + .PARAMETER Value + Specifies the variable value + .INPUTS + - Name: String + - Value: System.Boolean + .OUTPUTS + System.Boolean + .EXAMPLE + Sets the OSDComputerName task sequence variable to "MyComputer123" + $didSet = Set-TSVariable -Name "OSDComputerName" -Value "MyComputer123" + .EXAMPLE + Creates a new Task Sequence Variable, and sets its value + $didSet = Set-TSVariable -Name "MyNewVar" -Value 'MyNewVarsValue" + #> + param( + [Parameter(Mandatory=$true)] + [string] $Name, + [Parameter(Mandatory=$true)] + [string] $Value + ) + + Confirm-TSEnvironmentSetup + + try + { + $Script:TaskSequenceEnvironment.Value($Name) = $Value + return $true + } + catch + { + return $false + } + } + +function Get-TSAllValues() + { + <# + .SYNOPSIS + Gets all Task Sequence Variables and Values + + .DESCRIPTION + Gets all Task Sequence Variables with their associated values. + .INPUTS + None + .OUTPUTS + System.Object + .EXAMPLE + $TSVariableObject = Get-TSAllValues + #> + Confirm-TSEnvironmentSetup + + $Values = New-Object -TypeName System.Object + + foreach ($Variable in $Script:TaskSequenceEnvironment.GetVariables()) + { + $Values | Add-Member -MemberType NoteProperty -Name $Variable -Value "$($Script:TaskSequenceEnvironment.Value($Variable))" + } + + return $Values + } + +function Confirm-TSProgressUISetup() + { + <# + .SYNOPSIS + Verifies the TSProgresUI Com Object is initiated into an object. + + .DESCRIPTION + Verifies the TSProgresUI Com Object is initiated into an object. + .INPUTS + None + .OUTPUTS + None + + .NOTE + This module can be used statically but is intended to be used by other functions. + #> + if ($Script:TaskSequenceProgressUi -eq $null) + { + try + { + $Script:TaskSequenceProgressUi = New-Object -ComObject Microsoft.SMS.TSProgressUI + } + catch + { + throw "Unable to connect to the Task Sequence Progress UI! Please verify you are in a running Task Sequence Environment. Please note: TSProgressUI cannot be loaded during a prestart command.`n`nErrorDetails:`n$_" + } + } + } + +function Show-TSActionProgress() + { + <# + .SYNOPSIS + Shows task sequence secondary progress of a specific step + + .DESCRIPTION + Adds a second progress bar to the existing Task Sequence Progress UI. + This progress bar can be updated to allow for a real-time progress of + a specific task sequence sub-step. + The Step and Max Step parameters are calculated when passed. This allows + you to have a "max steps" of 400, and update the step parameter. 100% + would be achieved when step is 400 and max step is 400. The percentages + are calculated behind the scenes by the Com Object. + + .PARAMETER Message + The message to display the progress + .PARAMETER Step + Integer indicating current step + .PARAMETER MaxStep + Integer indicating 100%. A number other than 100 can be used. + .INPUTS + - Message: String + - Step: Long + - MaxStep: Long + .OUTPUTS + None + .EXAMPLE + Set's "Custom Step 1" at 30 percent complete + Show-TSActionProgress -Message "Running Custom Step 1" -Step 100 -MaxStep 300 + + .EXAMPLE + Set's "Custom Step 1" at 50 percent complete + Show-TSActionProgress -Message "Running Custom Step 1" -Step 150 -MaxStep 300 + .EXAMPLE + Set's "Custom Step 1" at 100 percent complete + Show-TSActionProgress -Message "Running Custom Step 1" -Step 300 -MaxStep 300 + #> + param( + [Parameter(Mandatory=$true)] + [string] $Message, + [Parameter(Mandatory=$true)] + [long] $Step, + [Parameter(Mandatory=$true)] + [long] $MaxStep + ) + + Confirm-TSProgressUISetup + Confirm-TSEnvironmentSetup + + $Script:TaskSequenceProgressUi.ShowActionProgress(` + $Script:TaskSequenceEnvironment.Value("_SMSTSOrgName"),` + $Script:TaskSequenceEnvironment.Value("_SMSTSPackageName"),` + $Script:TaskSequenceEnvironment.Value("_SMSTSCustomProgressDialogMessage"),` + $Script:TaskSequenceEnvironment.Value("_SMSTSCurrentActionName"),` + [Convert]::ToUInt32($Script:TaskSequenceEnvironment.Value("_SMSTSNextInstructionPointer")),` + [Convert]::ToUInt32($Script:TaskSequenceEnvironment.Value("_SMSTSInstructionTableSize")),` + $Message,` + $Step,` + $MaxStep) + } + +function Close-TSProgressDialog() + { + <# + .SYNOPSIS + Hides the Task Sequence Progress Dialog + + .DESCRIPTION + Hides the Task Sequence Progress Dialog + + .INPUTS + None + .OUTPUTS + None + .EXAMPLE + Close-TSProgressDialog + #> + Confirm-TSProgressUISetup + + $Script:TaskSequenceProgressUi.CloseProgressDialog() + } + +function Show-TSProgress() + { + <# + .SYNOPSIS + Shows task sequence progress of a specific step + + .DESCRIPTION + Manipulates the Task Sequence progress UI; top progress bar only. + This progress bar can be updated to allow for a real-time progress of + a specific task sequence step. + The Step and Max Step parameters are calculated when passed. This allows + you to have a "max steps" of 400, and update the step parameter. 100% + would be achieved when step is 400 and max step is 400. The percentages + are calculated behind the scenes by the Com Object. + + .PARAMETER CurrentAction + Step Title. Modifies the "Running action: " Message + .PARAMETER Step + Integer indicating current step + .PARAMETER MaxStep + Integer indicating 100%. A number other than 100 can be used. + .INPUTS + - CurrentAction: String + - Step: Long + - MaxStep: Long + .OUTPUTS + None + .EXAMPLE + Set's "Custom Step 1" at 30 percent complete + Show-TSProgress -CurrentAction "Running Custom Step 1" -Step 100 -MaxStep 300 + + .EXAMPLE + Set's "Custom Step 1" at 50 percent complete + Show-TSProgress -CurrentAction "Running Custom Step 1" -Step 150 -MaxStep 300 + .EXAMPLE + Set's "Custom Step 1" at 100 percent complete + Show-TSProgress -CurrentAction "Running Custom Step 1" -Step 300 -MaxStep 300 + #> + param( + [Parameter(Mandatory=$true)] + [string] $CurrentAction, + [Parameter(Mandatory=$true)] + [long] $Step, + [Parameter(Mandatory=$true)] + [long] $MaxStep + ) + + Confirm-TSProgressUISetup + Confirm-TSEnvironmentSetup + + $Script:TaskSequenceProgressUi.ShowTSProgress(` + $Script:TaskSequenceEnvironment.Value("_SMSTSOrgName"), ` + $Script:TaskSequenceEnvironment.Value("_SMSTSPackageName"), ` + $Script:TaskSequenceEnvironment.Value("_SMSTSCustomProgressDialogMessage"), ` + $CurrentAction, ` + $Step, ` + $MaxStep) + } + +function Show-TSErrorDialog() + { + <# + .SYNOPSIS + Shows the Task Sequence Error Dialog + + .DESCRIPTION + Shows a task sequence error dialog allowing for custom failure pages. + + .PARAMETER OrganizationName + Name of your Organization + .PARAMETER CustomTitle + Custom Error Title + .PARAMETER ErrorMessage + Message details of the error + .PARAMETER ErrorCode + Error Code the Task sequence will exit with + .PARAMETER TimeoutInSeconds + Timout for the Reboot Prompt + .PARAMETER ForceReboot + Indicates whether a reboot will be forced or not + .INPUTS + - OrganizationName: String + - CustomTitle: String + - ErrorMessage: String + - ErrorCode: Long + - TimeoutInSeconds: Long + - ForceReboot: System.Boolean + .OUTPUTS + None + .EXAMPLE + Sets an Error but does not force a reboot + Show-TSErrorDialog -OrganizationName "My Organization" -CustomTitle "An Error occured during the things" -ErrorMessage "That thing you tried...it didnt work" -ErrorCode 123456 -TimeoutInSeconds 90 -ForceReboot $false + + .EXAMPLE + Sets an Error and forces a reboot + Show-TSErrorDialog -OrganizationName "My Organization" -CustomTitle "An Error occured during the things" -ErrorMessage "He's dead Jim!" -ErrorCode 123456 -TimeoutInSeconds 90 -ForceReboot $true + #> + param( + [Parameter(Mandatory=$true)] + [string] $OrganizationName, + [Parameter(Mandatory=$true)] + [string] $CustomTitle, + [Parameter(Mandatory=$true)] + [string] $ErrorMessage, + [Parameter(Mandatory=$true)] + [long] $ErrorCode, + [Parameter(Mandatory=$true)] + [long] $TimeoutInSeconds, + [Parameter(Mandatory=$true)] + [bool] $ForceReboot + ) + + Confirm-TSProgressUISetup + Confirm-TSEnvironmentSetup + + if ($ForceReboot) + { + $Script:TaskSequenceProgressUi.ShowErrorDialog($OrganizationName, $Script:TaskSequenceEnvironment.Value("_SMSTSPackageName"), $CustomTitle, $ErrorMessage, $ErrorCode, $TimeoutInSeconds, 1) + } + else + { + $Script:TaskSequenceProgressUi.ShowErrorDialog($OrganizationName, $Script:TaskSequenceEnvironment.Value("_SMSTSPackageName"), $CustomTitle, $ErrorMessage, $ErrorCode, $TimeoutInSeconds, 0) + } + } + +function Show-TSMessage() + { + <# + .SYNOPSIS + Shows a Windows Forms Message Box + + .DESCRIPTION + Shows a Windows Forms Message Box, but does not return the response. + This will halt any current operations while the prompt is shown. + + .PARAMETER Message + Message to be displayed + .PARAMETER Title + Title of the message box + .PARAMETER Type + Button Style for the MessageBox + 0 = OK + 1 = OK, Cancel + 2 = Abort, Retry, Ignore + 3 = Yes, No, Cancel + 4 = Yes, No + 5 = Retry, Cancel + 6 = Cancel, Try Again, Continue + .INPUTS + - Message: String + - Title: String + - Type: Long + .OUTPUTS + None + .EXAMPLE + Sets an Error but does not force a reboot + Show-TSErrorDialog -OrganizationName "My Organization" -CustomTitle "An Error occured during the things" -ErrorMessage "That thing you tried...it didnt work" -ErrorCode 123456 -TimeoutInSeconds 90 -ForceReboot $false + + .EXAMPLE + Sets an Error and forces a reboot + Show-TSErrorDialog -OrganizationName "My Organization" -CustomTitle "An Error occured during the things" -ErrorMessage "He's dead Jim!" -ErrorCode 123456 -TimeoutInSeconds 90 -ForceReboot $true + #> + param( + [Parameter(Mandatory=$true)] + [string] $Message, + [Parameter(Mandatory=$true)] + [string] $Title, + [Parameter(Mandatory=$true)] + [ValidateRange(0,6)] + [long] $Type + ) + + Confirm-TSProgressUISetup + + $Script:TaskSequenceProgressUi.ShowMessage($Message, $Title, $Type) + } + +function Show-TSRebootDialog() + { + <# + .SYNOPSIS + Shows the Reboot Dialog + + .DESCRIPTION + Shows the Task Sequence "System Restart" Dialog. This allows you + to trigger custom Task Sequence Reboot Messages. + + .PARAMETER OrganizationName + Name of your Organization + .PARAMETER CustomTitle + Custom Title for the Reboot Dialog + .PARAMETER Message + Detailed Message regarding the reboot + .PARAMETER TimeoutInSeconds + Timout before the system reboots + .INPUTS + - OrganizationName: String + - CustomTitle: String + - Message: String + - TimeoutInSeconds: Long + .OUTPUTS + None + .EXAMPLE + Show's a Reboot Dialog + Show-TSRebootDialog -OrganizationName "My Organization" -CustomTitle "I need a reboot!" -Message "I need to reboot to complete something..." -TimeoutInSeconds 90 + #> + param( + [Parameter(Mandatory=$true)] + [string] $OrganizationName, + [Parameter(Mandatory=$true)] + [string] $CustomTitle, + [Parameter(Mandatory=$true)] + [string] $Message, + [Parameter(Mandatory=$true)] + [long] $TimeoutInSeconds + ) + + Confirm-TSProgressUISetup + Confirm-TSEnvironmentSetup + + $Script:TaskSequenceProgressUi.ShowRebootDialog($OrganizationName, $Script:TaskSequenceEnvironment.Value("_SMSTSPackageName"), $CustomTitle, $Message, $TimeoutInSeconds) + } + +function Show-TSSwapMediaDialog() + { + <# + .SYNOPSIS + Shows Task Sequence Swap Media Dialog. + + .DESCRIPTION + Shows Task Sequence Swap Media Dialog. + + .PARAMETER TaskSequenceName + Name of the Task Sequence + .PARAMETER MediaNumber + Media Number to insert + .INPUTS + - TaskSequenceName: String + - CustomTitle: Long + .OUTPUTS + None + .EXAMPLE + Prompts to insert media #2 for the Task Sequence "My Task Sequence" + Show-TSSwapMediaDialog -TaskSequenceName "My Task Sequence" -MediaNumber 2 + #> + param( + [Parameter(Mandatory=$true)] + [string] $TaskSequenceName, + [Parameter(Mandatory=$true)] + [long] $MediaNumber + ) + + Confirm-TSProgressUISetup + + $Script:TaskSequenceProgressUi.ShowSwapMediaDialog($TaskSequenceName, $MediaNumber) + } + +#Encode a plain text string to a Base64 string + Function ConvertTo-Base64 + { + [CmdletBinding(SupportsShouldProcess=$False)] + Param + ( + [Parameter(Mandatory=$True, ValueFromPipeline=$True)] + [ValidateNotNullOrEmpty()] + [String]$String + ) + + $EncodedString = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($String)) + Write-Verbose -Message "$($NewLine)`"$($String)`" has been converted to the following Base64 encoded string `"$($EncodedString)`"$($NewLine)" + + Return $EncodedString + } + +#Decode a Base64 string to a plain text string + Function ConvertFrom-Base64 + { + [CmdletBinding(SupportsShouldProcess=$False)] + Param + ( + [Parameter(Mandatory=$True, ValueFromPipeline=$True)] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$')] + [String]$String + ) + + $DecodedString = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($String)) + Write-Verbose -Message "$($NewLine)`"$($String)`" has been converted from the following Base64 encoded string `"$($DecodedString)`"$($NewLine)" + + Return $DecodedString + } \ No newline at end of file diff --git a/Remove-UWPs.ps1 b/Remove-UWPs.ps1 new file mode 100644 index 0000000..bba0ff9 --- /dev/null +++ b/Remove-UWPs.ps1 @@ -0,0 +1,325 @@ +#Requires -Version 3 + +<# + .SYNOPSIS + Removes an app package (.appx) from a Windows image using a regular expression that can be modified from the command line without changing the script code. + + .DESCRIPTION + Slightly more detailed description of what your function does + + .PARAMETER ImagePath + The directory path to the mounted windows image or the drive letter of a windows image that has been expanded onto a disk while in WindowsPE. + + .PARAMETER PackagesToRemove + A valid regular expression that matches the DisplayName(s) of the universal windows applications you wish to deprovision. + + .PARAMETER LogDir + A valid folder path. If the folder does not exist, it will be created. This parameter can also be specified by the alias "LogPath". + + .PARAMETER ContinueOnError + Ignore failures. + + .EXAMPLE + powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File "%FolderPathContainingScript%\%ScriptName%.ps1" -ImagePath "%OSDisk%\\" -PackagesToRemove '.*Xbox.*|.*Alarms.*|.*Bing.*|.*Skype.*|.*Communi.*' + + .NOTES + Any useful tidbits + + .LINK + https://docs.microsoft.com/en-us/powershell/module/dism/remove-appxprovisionedpackage?view=win10-ps#:~:text=The%20Remove%2DAppxProvisionedPackage%20cmdlet%20removes,removed%20from%20existing%20user%20accounts. +#> + +[CmdletBinding()] + Param + ( + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [ValidateScript({($_ -imatch '^[a-zA-Z][\:]\\{1,}$') -or ($_ -imatch '^[a-zA-Z][\:]\\.*?[^\\]$')})] + [System.IO.DirectoryInfo]$ImagePath, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Regex]$PackagesToRemove = '.*Xbox.*|.*Alarms.*|.*Bing.*|.*Skype.*|.*Communi.*|.*Zune.*|.*Solit.*|.*Games.*|.*3D.*|.*Camera.*|.*Phone.*|.*Soundrec.*|.*Xboxapp.*|.*Feedback.*|.*Message.*|.*Messaging.*|.*Gethelp.*|.*Mobile.*|.*Mixed.*|.*Microsoft\.MicrosoftOfficeHub.*|.*OneNote.*|.*Microsoft\.People.*|.*Windows.*Defender.*|.*Movies.*|.*Microsoft\.ZuneMusic.*|.*Microsoft\.Windows\.HolographicFirstRun.*|.*Microsoft\.Oneconnect.*|.*Microsoft\.WindowsStore.*|.*Microsoft\.Getstarted.*|.*Microsoft\.MSPaint.*|.*Microsoft\.ZuneVideo.*|.*Microsoft\.PPIProjection.*|.*Microsoft\.WindowsMaps.*', + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [ValidateScript({($_ -imatch '^[a-zA-Z][\:]\\.*?[^\\]$') -or ($_ -imatch "^\\(?:\\[^<>:`"/\\|?*]+)+$")})] + [Alias('LogPath')] + [System.IO.DirectoryInfo]$LogDir, + + [Parameter(Mandatory=$False)] + [Switch]$ContinueOnError + ) + +#Define Default Action Preferences + $Script:DebugPreference = 'SilentlyContinue' + $Script:ErrorActionPreference = 'Stop' + $Script:VerbosePreference = 'SilentlyContinue' + $Script:WarningPreference = 'Continue' + $Script:ConfirmPreference = 'None' + +#Load WMI Classes + $Baseboard = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Baseboard" -Property * | Select-Object -Property * + $Bios = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Bios" -Property * | Select-Object -Property * + $ComputerSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_ComputerSystem" -Property * | Select-Object -Property * + $OperatingSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_OperatingSystem" -Property * | Select-Object -Property * + +#Retrieve property values + $OSArchitecture = $($OperatingSystem.OSArchitecture).Replace("-bit", "").Replace("32", "86").Insert(0,"x").ToUpper() + +#Define variable(s) + $DateTimeLogFormat = 'dddd, MMMM dd, yyyy hh:mm:ss tt' ###Monday, January 01, 2019 10:15:34 AM### + [ScriptBlock]$GetCurrentDateTimeLogFormat = {(Get-Date).ToString($DateTimeLogFormat)} + $DateTimeFileFormat = 'yyyyMMdd_hhmmsstt' ###20190403_115354AM### + [ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)} + [System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Definition)" + [System.IO.DirectoryInfo]$ScriptDirectory = "$($ScriptPath.Directory.FullName)" + [System.IO.DirectoryInfo]$FunctionsDirectory = "$($ScriptDirectory.FullName)\Functions" + [System.IO.DirectoryInfo]$ModulesDirectory = "$($ScriptDirectory.FullName)\Modules" + [System.IO.DirectoryInfo]$ToolsDirectory = "$($ScriptDirectory.FullName)\Tools" + [System.IO.DirectoryInfo]$ToolsDirectory_OSAll = "$($ToolsDirectory.FullName)\All" + [System.IO.DirectoryInfo]$ToolsDirectory_OSArchSpecific = "$($ToolsDirectory.FullName)\$($OSArchitecture)" + $IsWindowsPE = Test-Path -Path 'HKLM:\SYSTEM\ControlSet001\Control\MiniNT' -ErrorAction SilentlyContinue + +#Log task sequence variables if debug mode is enabled within the task sequence + Try + { + [System.__ComObject]$TSEnvironment = New-Object -ComObject "Microsoft.SMS.TSEnvironment" + + If ($TSEnvironment -ine $Null) + { + $IsRunningTaskSequence = $True + } + } + Catch + { + $IsRunningTaskSequence = $False + } + +#Determine the default logging path if the parameter is not specified and is not assigned a default value + If (($PSBoundParameters.ContainsKey('LogDir') -eq $False) -and ($LogDir -ieq $Null)) + { + If ($IsRunningTaskSequence -eq $True) + { + [String]$_SMSTSLogPath = "$($TSEnvironment.Value('_SMSTSLogPath'))" + + If ([String]::IsNullOrEmpty($_SMSTSLogPath) -eq $False) + { + [System.IO.DirectoryInfo]$TSLogDirectory = "$($_SMSTSLogPath)" + } + Else + { + [System.IO.DirectoryInfo]$TSLogDirectory = "$($Env:Windir)\Temp\SMSTSLog" + } + + [System.IO.DirectoryInfo]$LogDir = "$($TSLogDirectory.FullName)\$($ScriptPath.BaseName)" + } + ElseIf ($IsRunningTaskSequence -eq $False) + { + [System.IO.DirectoryInfo]$LogDir = "$($Env:Windir)\Logs\Software\$($ScriptPath.BaseName)" + } + } + +#Start transcripting (Logging) + Try + { + [System.IO.FileInfo]$ScriptLogPath = "$($LogDir.FullName)\$($ScriptPath.BaseName)_$($GetCurrentDateTimeFileFormat.Invoke()).log" + If ($ScriptLogPath.Directory.Exists -eq $False) {[Void][System.IO.Directory]::CreateDirectory($ScriptLogPath.Directory.FullName)} + Start-Transcript -Path "$($ScriptLogPath.FullName)" -IncludeInvocationHeader -Force -Verbose + } + Catch + { + If ([String]::IsNullOrEmpty($_.Exception.Message)) {$ExceptionMessage = "$($_.Exception.Errors.Message)"} Else {$ExceptionMessage = "$($_.Exception.Message)"} + + $ErrorMessage = "[Error Message: $($ExceptionMessage)][ScriptName: $($_.InvocationInfo.ScriptName)][Line Number: $($_.InvocationInfo.ScriptLineNumber)][Line Position: $($_.InvocationInfo.OffsetInLine)][Code: $($_.InvocationInfo.Line.Trim())]" + Write-Error -Message "$($ErrorMessage)" + } + +#Log any useful information + $LogMessage = "IsWindowsPE = $($IsWindowsPE.ToString())" + Write-Verbose -Message "$($LogMessage)" -Verbose + + $LogMessage = "Script Path = $($ScriptPath.FullName)" + Write-Verbose -Message "$($LogMessage)" -Verbose + + $LogMessage = "Packages To Remove = $($PackagesToRemove.ToString())" + Write-Verbose -Message "$($LogMessage)" -Verbose + + $DirectoryVariables = Get-Variable | Where-Object {($_.Value -ine $Null) -and (($_.Value -is [System.IO.DirectoryInfo]) -or ($_.Value -is [System.IO.DirectoryInfo[]]))} + + ForEach ($DirectoryVariable In $DirectoryVariables) + { + $LogMessage = "$($DirectoryVariable.Name) = $($DirectoryVariable.Value.FullName -Join ', ')" + Write-Verbose -Message "$($LogMessage)" -Verbose + } + +#region Import Dependency Modules +$Modules = Get-Module -Name "$($ModulesDirectory.FullName)\*" -ListAvailable -ErrorAction Stop + +$ModuleGroups = $Modules | Group-Object -Property @('Name') + +ForEach ($ModuleGroup In $ModuleGroups) + { + $LatestModuleVersion = $ModuleGroup.Group | Sort-Object -Property @('Version') -Descending | Select-Object -First 1 + + If ($LatestModuleVersion -ine $Null) + { + $LogMessage = "Attempting to import dependency powershell module `"$($LatestModuleVersion.Name) [Version: $($LatestModuleVersion.Version.ToString())]`". Please Wait..." + Write-Verbose -Message "$($LogMessage)" -Verbose + Import-Module -Name "$($LatestModuleVersion.Path)" -Global -DisableNameChecking -Force -ErrorAction Stop + } + } +#endregion + +#region Dot Source Dependency Scripts +#Dot source any additional script(s) from the functions directory. This will provide flexibility to add additional functions without adding complexity to the main script and to maintain function consistency. + Try + { + If ($FunctionsDirectory.Exists -eq $True) + { + [String[]]$AdditionalFunctionsFilter = "*.ps1" + + $AdditionalFunctionsToImport = Get-ChildItem -Path "$($FunctionsDirectory.FullName)" -Include ($AdditionalFunctionsFilter) -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + $AdditionalFunctionsToImportCount = $AdditionalFunctionsToImport | Measure-Object | Select-Object -ExpandProperty Count + + If ($AdditionalFunctionsToImportCount -gt 0) + { + ForEach ($AdditionalFunctionToImport In $AdditionalFunctionsToImport) + { + Try + { + $LogMessage = "Attempting to dot source dependency script `"$($AdditionalFunctionToImport.Name)`". Please Wait...`r`n`r`nScript Path: `"$($AdditionalFunctionToImport.FullName)`"" + Write-Verbose -Message "$($LogMessage)" -Verbose + + . "$($AdditionalFunctionToImport.FullName)" + } + Catch + { + $ErrorMessage = "[Error Message: $($_.Exception.Message)]`r`n`r`n[ScriptName: $($_.InvocationInfo.ScriptName)]`r`n[Line Number: $($_.InvocationInfo.ScriptLineNumber)]`r`n[Line Position: $($_.InvocationInfo.OffsetInLine)]`r`n[Code: $($_.InvocationInfo.Line.Trim())]" + Write-Error -Message "$($ErrorMessage)" -Verbose + } + } + } + } + } + Catch + { + $ErrorMessage = "[Error Message: $($_.Exception.Message)]`r`n`r`n[ScriptName: $($_.InvocationInfo.ScriptName)]`r`n[Line Number: $($_.InvocationInfo.ScriptLineNumber)]`r`n[Line Position: $($_.InvocationInfo.OffsetInLine)]`r`n[Code: $($_.InvocationInfo.Line.Trim())]" + Write-Error -Message "$($ErrorMessage)" -Verbose + } +#endregion + +#Perform script action(s) + Try + { + #Tasks defined within this block will only execute if a task sequence is running + If (($IsRunningTaskSequence -eq $True)) + { + If (($PSBoundParameters.ContainsKey('ImagePath') -eq $False) -and ($ImagePath -eq $Null)) + { + [System.IO.DirectoryInfo]$ImagePath = "$($TSEnvironment.Value('OSDisk'))\" + } + } + + #Tasks defined here will execute whether only if a task sequence is not running + If ($IsRunningTaskSequence -eq $False) + { + $WarningMessage = "There is no task sequence running.`r`n" + Write-Warning -Message "$($WarningMessage)" -Verbose + } + #Remove Metro Application(s) + If ($ImagePath -ine $Null) + { + $LogMessage = "ImagePath = $($ImagePath.FullName)" + Write-Verbose -Message "$($LogMessage)" -Verbose + + $AppXProvisionedPackages = (Get-AppxProvisionedPackage -Path "$($ImagePath.FullName)" | Select-Object * | Sort-Object DisplayName) + + $AppXProvisionedPackagesToRemove = $AppXProvisionedPackages | Where-Object {($_.DisplayName -imatch $PackagesToRemove.ToString())} + + $AppXProvisionedPackagesToRemoveCount = $AppXProvisionedPackagesToRemove | Measure-Object | Select-Object -ExpandProperty Count + + $LogMessage = "Found $($AppXProvisionedPackagesToRemoveCount.ToString()) Appx provisioned package(s) to remove." + Write-Verbose -Message "$($LogMessage)" -Verbose + + $Counter = 1 + + ForEach ($AppXProvisionedPackageToRemove In $AppXProvisionedPackagesToRemove) + { + Try + { + [Int]$ProgressID = 1 + [String]$ActivityMessage = "Remove-AppxProvisionedPackage $($AppXProvisionedPackageToRemove.DisplayName) [Version: $($AppXProvisionedPackageToRemove.Version)]" + [String]$StatusMessage = "Attempting to remove $($AppXProvisionedPackageToRemove.DisplayName) $($AppXProvisionedPackageToRemove.Version) ($($Counter.ToString()) of $($AppXProvisionedPackagesToRemoveCount.ToString()))" + [Int]$PercentComplete = (($Counter / $AppXProvisionedPackagesToRemoveCount) * 100) + + $LogMessage = "$($StatusMessage). Please Wait..." + Write-Verbose -Message "$($LogMessage)" -Verbose + + Write-Progress -ID ($ProgressID) -Activity ($ActivityMessage) -Status ($StatusMessage) -PercentComplete ($PercentComplete) + + [System.IO.FileInfo]$AppXProvisionedPackageToRemoveLogPath = "$($LogDir.FullName)\Remove-AppxProvisionedPackage\$($AppXProvisionedPackageToRemove.DisplayName).log" + + If ($AppXProvisionedPackageToRemoveLogPath.Directory.Exists -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($AppXProvisionedPackageToRemoveLogPath.Directory.FullName)} + + $LogMessage = "Log Path = `"$($AppXProvisionedPackageToRemoveLogPath.FullName)`"" + Write-Verbose -Message "$($LogMessage)" -Verbose + + $RemoveAppXProvisionedPackage = Remove-AppxProvisionedPackage -PackageName "$($AppXProvisionedPackageToRemove.PackageName)" -Path "$($ImagePath.FullName)" -LogPath "$($AppXProvisionedPackageToRemoveLogPath.FullName)" -LogLevel 'Errors' -Verbose + + If ($? -eq $True) + { + $LogMessage = "Removal of the `"$($AppXProvisionedPackageToRemove.DisplayName)`" AppX provisioned package was successful!" + Write-Verbose -Message "$($LogMessage)" -Verbose + } + ElseIf ($? -eq $False) + { + $LogMessage = "Removal of the `"$($AppXProvisionedPackageToRemove.DisplayName)`" AppX provisioned package was unsuccessful!" + Write-Error -Message "$($LogMessage)" -Verbose + } + } + Catch + { + If ([String]::IsNullOrEmpty($_.Exception.Message)) {$ExceptionMessage = "$($_.Exception.Errors.Message)"} Else {$ExceptionMessage = "$($_.Exception.Message)"} + + $ErrorMessage = "[Error Message: $($ExceptionMessage)][ScriptName: $($_.InvocationInfo.ScriptName)][Line Number: $($_.InvocationInfo.ScriptLineNumber)][Line Position: $($_.InvocationInfo.OffsetInLine)][Code: $($_.InvocationInfo.Line.Trim())]" + Write-Error -Message "$($ErrorMessage)" -Verbose + } + + $Counter++ + } + } + Else + { + $ErrorMessage = "[Error Message: A valid image path was either not specified or contains an invalid value.]" + Throw "$($ErrorMessage)" + } + + #Stop transcripting (Logging) + Try + { + Stop-Transcript -Verbose + } + Catch + { + If ([String]::IsNullOrEmpty($_.Exception.Message)) {$ExceptionMessage = "$($_.Exception.Errors.Message)"} Else {$ExceptionMessage = "$($_.Exception.Message)"} + + $ErrorMessage = "[Error Message: $($ExceptionMessage)][ScriptName: $($_.InvocationInfo.ScriptName)][Line Number: $($_.InvocationInfo.ScriptLineNumber)][Line Position: $($_.InvocationInfo.OffsetInLine)][Code: $($_.InvocationInfo.Line.Trim())]" + Write-Error -Message "$($ErrorMessage)" + } + } + Catch + { + If ([String]::IsNullOrEmpty($_.Exception.Message)) {$ExceptionMessage = "$($_.Exception.Errors.Message -Join "`r`n`r`n")"} Else {$ExceptionMessage = "$($_.Exception.Message)"} + + $ErrorMessage = "[Error Message: $($ExceptionMessage)]`r`n`r`n[ScriptName: $($_.InvocationInfo.ScriptName)]`r`n[Line Number: $($_.InvocationInfo.ScriptLineNumber)]`r`n[Line Position: $($_.InvocationInfo.OffsetInLine)]`r`n[Code: $($_.InvocationInfo.Line.Trim())]`r`n" + Write-Error -Message "$($ErrorMessage)" + + Stop-Transcript -Verbose + + If ($ContinueOnError.IsPresent -eq $False) + { + [System.Environment]::Exit(50) + } + } \ No newline at end of file diff --git a/Tools/X64/Place_Additional_Tools_Here.txt b/Tools/X64/Place_Additional_Tools_Here.txt new file mode 100644 index 0000000..fa7cc9c --- /dev/null +++ b/Tools/X64/Place_Additional_Tools_Here.txt @@ -0,0 +1 @@ +### \ No newline at end of file diff --git a/Tools/X86/Place_Additional_Tools_Here.txt b/Tools/X86/Place_Additional_Tools_Here.txt new file mode 100644 index 0000000..fa7cc9c --- /dev/null +++ b/Tools/X86/Place_Additional_Tools_Here.txt @@ -0,0 +1 @@ +### \ No newline at end of file