├── AutoPilot ├── Set-APComputerName.ps1 ├── WindowsAutoPilotIntune.ps1 └── WindowsAutoPilotIntune.psm1 ├── CMScripts ├── Collectionless.md ├── DiscoveryMethods.ps1 ├── Get-InstalledSoftware.ps1 └── Remove-InstalledSoftware.ps1 ├── ClientTests └── Get-SpeedTest.ps1 ├── CompareADtoCM ├── Get-ADCMComparison.ps1 ├── Get-DirectoryCMComparison.ps1 ├── Update-ADCMComputers.ps1 └── readme.md ├── DesktopAnalytics └── Update-TelemetryPolicy.ps1 ├── EfficientArrays └── CompareArrayBuildTimes.ps1 ├── GatherScript ├── Gather.vbs └── README.md ├── Graph └── SharePoint │ ├── PWSharePoint │ ├── Commands │ │ ├── Add-pwSharePointListItem.ps1 │ │ ├── Clear-pwSharePointList.ps1 │ │ ├── Get-pwGraphAuthorizationHeader.ps1 │ │ ├── Get-pwListDifferences.ps1 │ │ ├── Get-pwSharePointListItems.ps1 │ │ ├── New-pwListItemJsonBody.ps1 │ │ ├── Remove-pwSharePointListItem.ps1 │ │ ├── Set-pwPropertyMap.ps1 │ │ └── Test-pwSharePointListConnection.ps1 │ ├── PWSharePoint.psd1 │ └── PWSharePoint.psm1 │ └── SampleSharePointListUpdate.ps1 ├── IntuneGraph ├── AppAssignment.ps1 ├── Get-IntuneDeviceNotes.ps1 └── Set-IntuneDeviceNotes.ps1 ├── InvokeToastAsUser ├── Invoke-ConditionRed.ps1 ├── Invoke-ToastAsUser.ps1 ├── SampleToast.jpg ├── ToastHeroImage.jpg └── ToastLogoImage.jpg ├── NumberTaskSequenceSteps ├── Invoke-ToolInstallation.ps1 ├── NumberTsSteps.xml ├── Set-TSStepNumbers.ps1 └── readme.md ├── OnDemandScripts └── Set-LoggedInUserAsAdmin.ps1 ├── Pestering ├── Invoke-PwPester.ps1 ├── MakeBreakfast.ps1 ├── MakeBreakfast.tests.ps1 └── Modules │ ├── pwBitmapper │ ├── Commands │ │ ├── Convert-pwBmpToPixels.ps1 │ │ ├── Convert-pwPixelstoBmp.ps1 │ │ ├── Copy-pwBitmapImage.ps1 │ │ ├── Get-pwBitmap.ps1 │ │ ├── Get-pwPixelMax.ps1 │ │ ├── Move-pwPixels.ps1 │ │ ├── New-pwBmpFileFromPixels.ps1 │ │ ├── New-pwEmptyBitmap.ps1 │ │ ├── Save-pwBitmapFile.ps1 │ │ ├── Set-pwBmpPixels.ps1 │ │ ├── Set-pwBmpRotate.ps1 │ │ ├── Set-pwPixelRotate.ps1 │ │ └── Update-pwBmpFileFromPixels.ps1 │ ├── Tests │ │ ├── Convert-pwBmpToPixels.tests.ps1 │ │ ├── Convert-pwPixelstoBmp.tests.ps1 │ │ ├── Copy-pwBitmapImage.tests.ps1 │ │ ├── Get-pwBitmap.tests.ps1 │ │ ├── Get-pwPixelMax.tests.ps1 │ │ ├── Move-pwPixels.tests.ps1 │ │ ├── New-pwBmpFileFromPixels.tests.ps1 │ │ ├── New-pwEmptyBitmap.tests.ps1 │ │ ├── Save-pwBitmapFile.tests.ps1 │ │ ├── Set-pwBmpPixels.tests.ps1 │ │ ├── Set-pwBmpRotate.tests.ps1 │ │ ├── Set-pwPixelRotate.tests.ps1 │ │ └── Update-pwBmpFileFromPixels.tests.ps1 │ ├── pwBitmapper.psd1 │ └── pwBitmapper.psm1 │ └── pwBreakFast │ ├── Breakfast.psd1 │ ├── Breakfast.psm1 │ ├── Commands │ ├── Add-Eggs.ps1 │ ├── Add-Toast.ps1 │ ├── Add-pwBreakfastMeat.ps1 │ ├── Add-pwPastry.ps1 │ └── Get-Beverage.ps1 │ └── WorkingFile.ps1 ├── PolicyReverse └── Set-AllowRun.ps1 ├── SQL └── Invoke-SqlDataReader.ps1 ├── SetupDiagGUI ├── Invoke-SetupDaig.ps1 ├── SetupDiag.exe ├── SetupDiagSC.png ├── SetupDiagUI.png └── readme.md ├── TSSimulator ├── Invoke-TSSimulator.ps1 ├── Invoke-ToolInstallation.ps1 ├── InvokeTSSim.xml ├── NumberTsSteps.xml └── readme.md ├── WPF ├── FuntionalSample.ps1 ├── PictureInUI.ps1 ├── ReturnToParent.ps1 └── WorkingWithWPF.ps1 └── simcity └── loadmessages.txt /AutoPilot/Set-APComputerName.ps1: -------------------------------------------------------------------------------- 1 | #Rename Computer if domain available. 2 | #Useful during the autopilot process. 3 | [CmdletBinding()] 4 | param ( 5 | #This is the Prefix you want to append to your computer names. Should not exceed 5 characters. 6 | [String]$Prefix = 'BOB' 7 | ) 8 | 9 | $PSDefaultParameterValues["Write-Log:LogFile"] = "$env:Windir\Temp\Set-APComputerName.log" 10 | $PSDefaultParameterValues["Write-Log:Verbose"] = $false 11 | 12 | function Write-Log { 13 | [CmdletBinding()] 14 | Param ( 15 | [Parameter(Mandatory = $false)] 16 | $Message, 17 | 18 | [Parameter(Mandatory = $false)] 19 | $ErrorMessage, 20 | 21 | [Parameter(Mandatory = $false)] 22 | $Component, 23 | 24 | [Parameter(Mandatory = $false, HelpMessage = "1 = Normal, 2 = Warning (yellow), 3 = Error (red)")] 25 | [ValidateSet(1, 2, 3)] 26 | [int]$Type, 27 | 28 | [Parameter(Mandatory = $false, HelpMessage = "Size in KB")] 29 | [int]$LogSizeKB = 512, 30 | 31 | [Parameter(Mandatory = $true)] 32 | $LogFile 33 | ) 34 | <# 35 | Type: 1 = Normal, 2 = Warning (yellow), 3 = Error (red) 36 | #> 37 | Try{ 38 | IF (!(Test-Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName)){ 39 | New-Item -ItemType directory -Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName 40 | } 41 | } 42 | Catch{ 43 | Throw 'Failed to find/set parent directory path' 44 | } 45 | $LogLength = $LogSizeKB * 1024 46 | try { 47 | $log = Get-Item $LogFile -ErrorAction Stop 48 | If (($log.length) -gt $LogLength) { 49 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 50 | $Date = Get-Date -Format "MM-dd-yyyy" 51 | $LogMessage = "" 52 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 53 | Move-Item -Path "$LogFile" -Destination "$($LogFile.TrimEnd('g'))_" -Force 54 | } 55 | } 56 | catch {Write-Verbose "Nothing to move or move failed."} 57 | 58 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 59 | $Date = Get-Date -Format "MM-dd-yyyy" 60 | 61 | if ($null -ne $ErrorMessage) {$Type = 3} 62 | if ($null -eq $Component) {$Component = " "} 63 | if ($null -eq $Type) {$Type = 1} 64 | 65 | $LogMessage = "" 66 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 67 | } 68 | 69 | If ($Prefix.Length -gt 5){ 70 | Write-Log -Message "Prefix set to [$Prefix]. This cannot exceed 5 characters." -Type 3 71 | exit 99 72 | } 73 | 74 | #Checks if domain is available 75 | Try{ 76 | [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()>$Null 77 | $DomainAvailable=$true 78 | } 79 | Catch { 80 | $DomainAvailable=$False 81 | } 82 | If ($DomainAvailable -eq $False){ 83 | Write-Log "Domain not available. Exiting script.." 84 | Exit 85 | } 86 | 87 | #Check if computer exists Returns True/False 88 | function Test-ComputerExists{ 89 | [CmdletBinding()] 90 | param ( 91 | [String] 92 | $ComputerName 93 | ) 94 | $objDomain = New-Object System.DirectoryServices.DirectoryEntry 95 | $objSearcher = New-Object System.DirectoryServices.DirectorySearcher 96 | $objSearcher.SearchRoot = $objDomain 97 | $objSearcher.Filter = "(&(objectClass=Computer)(name=$ComputerName))" 98 | $colResults = $objSearcher.FindAll() 99 | ![string]::IsNullOrEmpty($colResults) 100 | } 101 | 102 | function Set-SNRightTen { 103 | [CmdletBinding()] 104 | param ( 105 | [Parameter(Mandatory = $true)] 106 | [String] 107 | $SN 108 | ) 109 | $SN = $SN -replace '[\\/\:\*\?\"\<\>\|\- ]', '' 110 | $SNLast10 = $SN.Length - 10 111 | $SN.Substring($SNLast10) 112 | } 113 | 114 | #Getting Chassis type for appending to name (if used). 115 | #$CTs=(Get-CimInstance -ClassName Win32_SystemEnclosure).ChassisTypes 116 | #$DesktopChassisTypes = @("3","4","5","6","7","15","16","35","36") 117 | #$LatopChassisTypes = @("8","9","10","11","12","14","18","21","30","31","32") 118 | #$ServerChassisTypes = @("23","28") 119 | #foreach ($CT in $CTs) { 120 | # If ($CT -in $LatopChassisTypes){$CompType = "L"} 121 | # If ($CT -in $DesktopChassisTypes){$CompType = "D"} 122 | #} 123 | 124 | $SN = (Get-CimInstance -ClassName Win32_BIOS).SerialNumber 125 | $MFG = (Get-CimInstance -ClassName Win32_BIOS).Manufacturer 126 | # Remove any special characters (\/:*?"<>|) that could cause issues in a computer name (plus - because of HyperV). https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou 127 | $SN = $SN -replace '[\\/\:\*\?\"\<\>\|\- ]', '' 128 | 129 | #lets check and see if this thing is a VMWare device, if it is, use last 10 chars of SN 130 | If ($MFG -like 'VMware*'){ 131 | Write-Log "Looks like this is a VMware VM. Lets use the last ten chars of the serial number [$SN]." 132 | $SN = Set-SNRightTen -SN $SN 133 | Write-Log "Serial number to be used for computer name [$SN]." 134 | } 135 | 136 | # if we found a serial number, let try to rename the computer. 137 | If ([string]::IsNullOrEmpty($Prefix)){ 138 | $NewCompName = $SN 139 | } 140 | else { 141 | $NewCompName = $Prefix + $SN 142 | } 143 | 144 | if ($NewCompName.Length -gt 15){ 145 | $NewCompName = $NewCompName.Substring(0,15) 146 | } 147 | 148 | #Checks if it already is named what you want it to be named. 149 | If ($NewCompName -eq $env:COMPUTERNAME){ 150 | Write-Log "Already named what you want. Exiting Script.." 151 | Exit 152 | } 153 | 154 | $NewComputerExists = Test-ComputerExists -ComputerName $NewCompName 155 | $Inc = 0 156 | Write-Log "Computer [$NewCompName] Exists in AD: $NewComputerExists" 157 | while ($NewComputerExists){ 158 | Write-Log -Message "Computer [$NewCompName] Exists in AD (incrementing name and trying again)" -Type 2 159 | $Inc++ 160 | if ($Inc -gt 9) { 161 | Write-Log -Message "Tried 9 interations of the computer name and they all existed." -type 3 162 | exit 9 163 | } 164 | If ($NewCompName.Length -gt 13){ 165 | $NewCompName = $NewCompName.Substring(0,13) 166 | } 167 | $IncName = "$NewCompName-$Inc" 168 | $NewComputerExists = Test-ComputerExists -ComputerName $IncName 169 | Write-Log -Message "Computer [$IncName] Exists in AD: [$NewComputerExists]" 170 | if (!$NewComputerExists){ 171 | Write-Log -Message "Arrived at available incremented Computer [$IncName]. Resetting variable." 172 | $NewCompName = $IncName 173 | } 174 | } 175 | 176 | # Now that we know we have a computer name that is ok, let's try to rename it. 177 | # if it fails to rename it, exit with a general error code of 7. 178 | try { 179 | Rename-Computer -NewName $NewCompName -ErrorAction Stop 180 | Write-Log -Message "Computer rename to [$NewCompName] Complete." 181 | } 182 | catch { 183 | Write-Log -Message "Computer rename to [$NewCompName] failed." -Type 3 184 | exit 7 185 | } 186 | -------------------------------------------------------------------------------- /CMScripts/Collectionless.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | graph TD 5 | ExistingApp[Existing Application w/ \n proper deployment \n for Collectionless] 6 | Request[[Request for Application]] --> ACME[Engine Request] 7 | ACME --> ExAp{Existing Approval for \n app on Machine?} 8 | ExAp -->|No| NewApprove 9 | ExistingApp --> NewApprove[WMI Approval Created] 10 | ExAp -->|Yes| UpdateApprove[Update approval to Approved] 11 | NewApprove --> SM[Status Message ID 30804 created] 12 | UpdateApprove --> SM 13 | SM --> CP1[Sends client push machine policy] 14 | CP1 --> Client[Machine Policy Refresh] 15 | Client --> NPI[New Policy installs Software] 16 | NPI --> STME[State Message sent back] 17 | STME --> MON[Monitor vAppDeploymentAssetDetails] 18 |
19 | 20 | 21 | ```powershell 22 | $AppName = "Test App 4" 23 | 24 | #$Deployment = New-CMApplicationDeployment -CollectionName "Windows Workstation" -Name "$AppName" -DeployAction Install -DeployPurpose Available -ApprovalRequired $true 25 | 26 | $application = Get-CMApplication -Name "$AppName" 27 | 28 | $machine = Get-WmiObject -Namespace 'root\sms\site_XXL' -Query "SELECT * FROM SMS_R_SYSTEM WHERE Name = 'Win10-2'" 29 | 30 | $machinename = $machine.Name 31 | 32 | $clientGuid = $machine.SMSUniqueIdentifier 33 | $appid = $Application.ModelName 34 | $autoInstall = "true" 35 | $comments = "Software Marketplace Approved" 36 | 37 | Invoke-WmiMethod ` 38 | -Path "SMS_ApplicationRequest" ` 39 | -Namespace 'root\sms\site_XXL' ` 40 | -Name CreateApprovedRequest ` 41 | -ArgumentList @($appid, $autoInstall, $clientGuid, $comments) 42 | 43 | 44 | $reqObj = Get-WmiObject -Namespace 'root\sms\site_XXL' -Class SMS_UserApplicationRequest | ` 45 | Where {$_.ModelName -eq $appid -and $_.RequestedMachine -eq $machinename } 46 | $reqObj.Approve('Approved', 6) 47 | 48 | 49 | $reqObj = Get-WmiObject -Namespace 'root\sms\site_XXL' -Query "select * from SMS_UserApplicationRequest where ModelName='$appid' and RequestedMachine='$machinename'" 50 | $reqObj.Deny('Not for you', 6) 51 | 52 | 53 | $reqObj.RetryInstall("Again",20) 54 | 55 | Denial does not Uninstall the app. 56 | 57 | 58 | Not seeing any notification of success/failure. 59 | All the approval sends throug the BGB channel is a machine policy refresh. 60 | 61 | 62 | 63 | 64 | Approval of app install: 65 | Starting to send push task (PushID: 1039 TaskID: 1024 TaskGUID: 5F5734C6-2B0A-4998-86B0-F0CF6D7B778E TaskType: 1 TaskParam: ) to 1 clients with throttling (strategy: 1 param: 42) 66 | * Task Type 1 : Request Machine Policy 67 | 68 | Status Message in CM for: 69 | System: Unknown Machine 70 | Component: Unkonwn Application 71 | Type: Audit 72 | Message ID: 30804 73 | Message: Administrative user SOUP\cmboss approved request of application ScopeId_5D76BDE2-98FC-4855-B205-4DF992B18F54/Application_508a00b2-bc40-42c6-af5b-db400224ad2a for device WIN10-1. 74 | 75 | Status Message in CM for: 76 | System: Unknown Machine 77 | Component: Unkonwn Application 78 | Type: Audit 79 | Message ID: 30803 80 | Message: Administrative user SOUP\cmboss denied request of application Test App for user None for device WIN10-1. 81 | 82 | Status Message in CM for: 83 | System: Unknown Machine 84 | Component: Unkonwn Application 85 | Type: Audit 86 | Message ID: 30809 87 | Message: Administrative user SOUP\cmboss retried installation of application Test App 1 for device WIN10-1. 88 | 89 | 90 | Sending a script: 91 | Starting to send push task (PushID: 1040 TaskID: 1025 TaskGUID: C600F7C7-17A6-4F4F-A83D-52A89D34EF90 TaskType: 15 TaskParam: PFNjcmlwdENvbnRlbnQgU2NyaXB0R3VpZD0nOEVEQjQyN0MtQzA0OS00MzZDLUEyNEQtMjE2RjJDNzZEOTU1Jz48U2NyaXB0VmVyc2lvbj4xPC9TY3JpcHRWZXJzaW9uPjxTY3JpcHRUeXBlPjA8L1NjcmlwdFR5cGU+PFNjcmlwdEhhc2ggU2NyaXB0SGFzaEFsZz0nU0hBMjU2Jz4zMTFDMTJDOTU3MDcxNTUwMkQxMEYyNzlDQUQwQ0M5NTEyNUY5MDlBQzIwM0Q0MjgwNERCMERFMUY4RkQzRjc4PC9TY3JpcHRIYXNoPjxTY3JpcHRQYXJhbWV0ZXJzPjwvU2NyaXB0UGFyYW1ldGVycz48UGFyYW1ldGVyR3JvdXBIYXNoIFBhcmFtZXRlckhhc2hBbGc9J1NIQTI1Nic+PC9QYXJhbWV0ZXJHcm91cEhhc2g+PC9TY3JpcHRDb250ZW50Pg==) to 1 clients with throttling (strategy: 1 param: 42) 92 | 93 | * Task Type 15: Request Script Execution 94 | 95 | 96 | 97 | 98 | Status of App installs. No immediate status found. Best bet State Messaging. 99 | 100 | 101 | [vAppDeploymentAssetDetails] 102 | 103 | 104 | 105 | #BGB machine policy refresh 106 | $ServerName = "" 107 | $SiteCode = "" 108 | $NameSpace = "root\SMS\Site_{0}" -f $SiteCode 109 | $ClassName = "SMS_ClientOperation" 110 | $MethodName = "InitiateClientOperation" 111 | [string]$TargetCollectionID = "SMS00001" 112 | [uint32]$Type = 1 #Machine Policy 113 | [uint32]$RandomizationWindow = 1 114 | [uint32[]]$TargetResourceIDs = 16777325 115 | 116 | #Using CIM 117 | $Args = @{ 118 | TargetCollectionID = $TargetCollectionID 119 | Type = $Type 120 | RandomizationWindow = $RandomizationWindow 121 | TargetResourceIDs = $TargetResourceIDs 122 | } 123 | Invoke-CimMethod -Namespace $NameSpace -ClassName $ClassName -MethodName $MethodName -Arguments $Args 124 | 125 | ``` 126 | 127 | App Requests go away when app is deleted it seems. -------------------------------------------------------------------------------- /CMScripts/Get-InstalledSoftware.ps1: -------------------------------------------------------------------------------- 1 | Function Get-InstalledSoftware { 2 | <# 3 | .SYNOPSIS 4 | Attempts to get all installed software on a system and exports them to a list 5 | 6 | .DESCRIPTION 7 | This script will attempt to search all the selected Uninstall registry keys for software installed on the computer and will return 8 | 9 | .PARAMETER Architecture 10 | Choose if you want to search for only 32 or 64 apps [x86|x64]. Default is both 11 | 12 | .PARAMETER RegHives 13 | Choose if you want to search both the machine (HKLM) and the user (HKCU - that the process is running as) hives [HKLM|HKCU]. default is machine only. 14 | 15 | .EXAMPLE 16 | Get-InstalledSoftware 17 | 18 | .NOTES 19 | #> 20 | param( 21 | [Parameter(Mandatory = $false)] 22 | [ValidateSet('x86', 'x64')] 23 | [string[]]$Architecture = @('x86', 'x64'), 24 | [Parameter(Mandatory = $false)] 25 | [ValidateSet('HKLM', 'HKCU')] 26 | [string[]]$RegHives = 'HKLM', 27 | [Parameter(Mandatory = $false)] 28 | [switch]$ShowAll 29 | ) 30 | 31 | $RegHivesHash = @{ 32 | 'HKLM' = 'registry::HKEY_LOCAL_MACHINE\' 33 | 'HKCU' = 'registry::HKEY_CURRENT_USER\' 34 | } 35 | 36 | $UninstallKeys = New-Object Collections.Generic.List[string] 37 | #IntPtr will be 4 on a 32 bit process and then there is only one uninstall key. 38 | if (([IntPtr]::Size -eq 4)) { 39 | $UninstallKeys.Add('Software\Microsoft\Windows\CurrentVersion\Uninstall\*') 40 | } 41 | else { 42 | If ($Architecture.Contains('x86')){ 43 | $UninstallKeys.Add('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') 44 | } 45 | If ($Architecture.Contains('x64')){ 46 | $UninstallKeys.Add('Software\Microsoft\Windows\CurrentVersion\Uninstall\*') 47 | } 48 | } 49 | 50 | $FullKeyPaths = New-Object Collections.Generic.List[string] 51 | foreach ($Hive in $RegHives){ 52 | foreach ($Key in $UninstallKeys){ 53 | $FullKeyPaths.Add("$($RegHivesHash.$Hive)$Key") 54 | } 55 | } 56 | $Properties = 'DisplayName', 'DisplayVersion', 'PSChildName', 'Publisher', 'UninstallString' 57 | 58 | $AllUninstalls = Get-ItemProperty -Path $FullKeyPaths -Name $Properties -ErrorAction SilentlyContinue 59 | 60 | If ($PSBoundParameters.ContainsKey('ShowAll')){ 61 | $AllUninstalls | Select-Object -Property $Properties 62 | } 63 | else { 64 | foreach ($Uninstall in $AllUninstalls) { 65 | if (-not [string]::IsNullOrEmpty($Uninstall.DisplayName)) { 66 | $Uninstall | Select-Object -Property $Properties 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /CMScripts/Remove-InstalledSoftware.ps1: -------------------------------------------------------------------------------- 1 | $AppNameLike = 'Adobe Acrobat Reader' 2 | 3 | #powershell.exe -ExecutionPolicy ByPass -noprofile -NonInteractive -nologo -file .\Uninstall.ps1 4 | 5 | Function Get-InstalledSoftware { 6 | <# 7 | .SYNOPSIS 8 | Attempts to get all installed software on a system and exports them to a list 9 | 10 | .DESCRIPTION 11 | This script will attempt to search all the selected Uninstall registry keys for software installed on the computer and will return 12 | 13 | .PARAMETER Architecture 14 | Choose if you want to search for only 32 or 64 apps [x86|x64]. Default is both 15 | 16 | .PARAMETER RegHives 17 | Choose if you want to search both the machine (HKLM) and the user (HKCU - that the process is running as) hives [HKLM|HKCU]. default is machine only. 18 | 19 | .EXAMPLE 20 | Get-InstalledSoftware 21 | 22 | .NOTES 23 | #> 24 | param( 25 | [Parameter(Mandatory = $false)] 26 | [ValidateSet('x86', 'x64')] 27 | [string[]]$Architecture = @('x86', 'x64'), 28 | [Parameter(Mandatory = $false)] 29 | [ValidateSet('HKLM', 'HKCU')] 30 | [string[]]$RegHives = 'HKLM', 31 | [Parameter(Mandatory = $false)] 32 | [switch]$ShowAll 33 | ) 34 | 35 | $RegHivesHash = @{ 36 | 'HKLM' = 'registry::HKEY_LOCAL_MACHINE\' 37 | 'HKCU' = 'registry::HKEY_CURRENT_USER\' 38 | } 39 | 40 | $UninstallKeys = New-Object Collections.Generic.List[string] 41 | #IntPtr will be 4 on a 32 bit process and then there is only one uninstall key. 42 | if (([IntPtr]::Size -eq 4)) { 43 | $UninstallKeys.Add('Software\Microsoft\Windows\CurrentVersion\Uninstall\*') 44 | } 45 | else { 46 | If ($Architecture.Contains('x86')){ 47 | $UninstallKeys.Add('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') 48 | } 49 | If ($Architecture.Contains('x64')){ 50 | $UninstallKeys.Add('Software\Microsoft\Windows\CurrentVersion\Uninstall\*') 51 | } 52 | } 53 | 54 | $FullKeyPaths = New-Object Collections.Generic.List[string] 55 | foreach ($Hive in $RegHives){ 56 | foreach ($Key in $UninstallKeys){ 57 | $FullKeyPaths.Add("$($RegHivesHash.$Hive)$Key") 58 | } 59 | } 60 | $Properties = 'DisplayName', 'DisplayVersion', 'PSChildName', 'Publisher', 'UninstallString' 61 | 62 | $AllUninstalls = Get-ItemProperty -Path $FullKeyPaths -Name $Properties -ErrorAction SilentlyContinue 63 | 64 | If ($PSBoundParameters.ContainsKey('ShowAll')){ 65 | $AllUninstalls | Select-Object -Property $Properties 66 | } 67 | else { 68 | foreach ($Uninstall in $AllUninstalls) { 69 | if (-not [string]::IsNullOrEmpty($Uninstall.DisplayName)) { 70 | $Uninstall | Select-Object -Property $Properties 71 | } 72 | } 73 | } 74 | } 75 | 76 | [pscustomobject[]]$AllApps = Get-InstalledSoftware 77 | $FilteredApp = $AllApps.Where({$_.DisplayName -like "*$AppNameLike*"}) 78 | If ($FilteredApp.count -ne 1) { 79 | exit 1603 80 | } 81 | 82 | try { 83 | $null = Start-Process -FilePath "C:\Windows\System32\MsiExec.exe" -ArgumentList "/x $($FilteredApp.PSChildName) /qn /norestart" -Wait -NoNewWindow -PassThru 84 | } 85 | catch { 86 | exit 1603 87 | } 88 | -------------------------------------------------------------------------------- /ClientTests/Get-SpeedTest.ps1: -------------------------------------------------------------------------------- 1 | #Replace the Download URL to where you've uploaded the ZIP file yourself. We will only download this file once. 2 | #Latest version can be found at: https://www.speedtest.net/nl/apps/cli 3 | $DownloadURL = "https://install.speedtest.net/app/cli/ookla-speedtest-1.1.1-win64.zip" 4 | $DownloadLocation = "$($Env:Temp)\SpeedtestCLI" 5 | try { 6 | if (!(Test-Path $DownloadLocation)) { 7 | new-item $DownloadLocation -ItemType Directory -force 8 | } 9 | If (!(Test-Path "$($DownloadLocation)\speedtest.exe")){ 10 | If ((Test-Path "$($DownloadLocation)\speedtest.zip")){ 11 | Remove-Item -Path "$($DownloadLocation)\speedtest.zip" -Force 12 | } 13 | Invoke-WebRequest -Uri $DownloadURL -OutFile "$($DownloadLocation)\speedtest.zip" 14 | Expand-Archive "$($DownloadLocation)\speedtest.zip" -DestinationPath $DownloadLocation -Force 15 | } 16 | } 17 | catch { 18 | write-host "The download and extraction of SpeedtestCLI failed. Error: $($_.Exception.Message)" 19 | exit 1 20 | } 21 | $SpeedtestResults = & "$($DownloadLocation)\speedtest.exe" --format=json --accept-license --accept-gdpr 22 | $SpeedtestResults = $SpeedtestResults | ConvertFrom-Json 23 | 24 | #creating object 25 | [PSCustomObject]@{ 26 | downloadspeed = [math]::Round($SpeedtestResults.download.bandwidth / 1000000 * 8, 2) 27 | uploadspeed = [math]::Round($SpeedtestResults.upload.bandwidth / 1000000 * 8, 2) 28 | packetloss = [math]::Round($SpeedtestResults.packetLoss) 29 | isp = $SpeedtestResults.isp 30 | ExternalIP = $SpeedtestResults.interface.externalIp 31 | InternalIP = $SpeedtestResults.interface.internalIp 32 | UsedServer = $SpeedtestResults.server.host 33 | ResultsURL = $SpeedtestResults.result.url 34 | Jitter = [math]::Round($SpeedtestResults.ping.jitter) 35 | Latency = [math]::Round($SpeedtestResults.ping.latency) 36 | } 37 | -------------------------------------------------------------------------------- /CompareADtoCM/Get-ADCMComparison.ps1: -------------------------------------------------------------------------------- 1 | Function Get-ComputersFromAD { 2 | $RootDSE = [ADSI]"LDAP://rootDSE" 3 | $obj = "LDAP://" + $rootDSE.Defaultnamingcontext 4 | $domain = New-Object System.DirectoryServices.DirectoryEntry($obj) 5 | $searcher = New-Object system.DirectoryServices.DirectorySearcher 6 | $searcher.searchroot = $Domain 7 | $searcher.Filter = "(&(objectclass=computer))" 8 | $searcher.pagesize = 1 9 | $searcher.searchscope = "subtree" 10 | $proplist = "name", "pwdLastSet", "lastLogonTimestamp", "operatingSystem", "CanonicalName" 11 | foreach ($i in $proplist) { [void]$searcher.propertiesToLoad.add($i) } 12 | $results = $searcher.FindAll() 13 | foreach ($i in $results) { 14 | $CN = "$($i.properties.canonicalname)" 15 | $CompName = "$($i.properties.name)" 16 | $OU = $CN.Replace("/$CompName", '') 17 | New-Object -TypeName psobject -Property @{'ComputerName' = "$($i.properties.name)"; 'OperatingSystem' = "$($i.properties.operatingsystem)"; 'LastLogon' = "$([datetime]::FromFileTime($($i.properties.lastlogontimestamp)))"; 'PasswordLastSet' = "$([datetime]::FromFileTime($($i.properties.pwdlastset)))"; 'CanonicalName' = "$($i.properties.canonicalname)"; 'OrgUnit' = $OU } 18 | } 19 | } 20 | 21 | Function Get-ComputersFromCM { 22 | param( 23 | [Parameter(Mandatory = $true)] 24 | [ValidateNotNullOrEmpty()] 25 | [string]$SiteServer, 26 | [Parameter(Mandatory = $false)] 27 | [ValidateNotNullOrEmpty()] 28 | [string]$CMSite = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\CCM\CcmEval -Name LastSiteCode -ErrorAction SilentlyContinue).LastSiteCode, 29 | [Parameter(Mandatory = $false)] 30 | [ValidateNotNullOrEmpty()] 31 | [System.Management.Automation.PSCredential]$Credential 32 | ) 33 | if ($Credential) { 34 | Get-WmiObject -Namespace "ROOT\SMS\site_$CMSite" -Query "select distinct SMS_R_System.Name, SMS_R_System.Client, SMS_G_System_CH_ClientSummary.LastPolicyRequest from SMS_R_System left join SMS_G_System_CH_ClientSummary on SMS_G_System_CH_ClientSummary.ResourceID = SMS_R_System.ResourceId" -ComputerName $SiteServer -Credential $Credential | select -Property @{Name='Name'; Expression={$_.SMS_R_System.Name}},@{Name='Client'; Expression={$_.SMS_R_System.Client}},@{Name='LastPolicyRequest'; Expression={[Management.ManagementDateTimeConverter]::ToDateTime($_.SMS_G_System_CH_ClientSummary.LastPolicyRequest)}} 35 | } 36 | else { 37 | Get-WmiObject -Namespace "ROOT\SMS\site_$CMSite" -Query "select distinct SMS_R_System.Name, SMS_R_System.Client, SMS_G_System_CH_ClientSummary.LastPolicyRequest from SMS_R_System left join SMS_G_System_CH_ClientSummary on SMS_G_System_CH_ClientSummary.ResourceID = SMS_R_System.ResourceId" -ComputerName $SiteServer | select -Property @{Name='Name'; Expression={$_.SMS_R_System.Name}},@{Name='Client'; Expression={$_.SMS_R_System.Client}},@{Name='LastPolicyRequest'; Expression={[Management.ManagementDateTimeConverter]::ToDateTime($_.SMS_G_System_CH_ClientSummary.LastPolicyRequest)}} 38 | } 39 | } 40 | 41 | function Get-ADWithCMComputers { 42 | param( 43 | # An array of computer objects from AD 44 | [Parameter(Mandatory = $true)] 45 | $AdComputers, 46 | # An array of computer objects from CM 47 | [Parameter(Mandatory = $true)] 48 | $CmComputers 49 | ) 50 | foreach ($Comp in $AdComputers) { 51 | If ($Comp.ComputerName -in $CmComputers.Name) { 52 | $InCM = 'True' 53 | $CMComputer = $CmComputers | Where-Object { $_.Name -like $Comp.ComputerName } 54 | if ($CMComputer.Client -eq 1) { $CMClient = 'True' }else { $CMClient = 'False' } 55 | $CMPolicyRequest = $CMComputer.LastPolicyRequest 56 | } 57 | else { 58 | $InCM = 'False' 59 | $CMClient = 'False' 60 | $CMPolicyRequest = '' 61 | } 62 | [pscustomobject][ordered]@{ 63 | 'ComputerName' = "$($Comp.ComputerName)" 64 | 'OperatingSystem' = "$($Comp.OperatingSystem)" 65 | 'LastLogon' = "$($Comp.LastLogon)" 66 | 'PasswordLastSet' = "$($Comp.PasswordLastSet)" 67 | 'CanonicalName' = "$($Comp.CanonicalName)" 68 | 'OrgUnit' = "$($Comp.OrgUnit)" 69 | 'InConfigMgr' = "$InCM" 70 | 'CMClient' = "$CMClient" 71 | 'LastPolicyRequest'="$CMPolicyRequest" 72 | } 73 | } 74 | } 75 | 76 | Function Get-ADCMComparison { 77 | param( 78 | # Primary site server for this Configuration Manager Site 79 | [Parameter(Mandatory = $true)] 80 | [ValidateNotNullOrEmpty()] 81 | [string] 82 | $SiteServer, 83 | # Site name for this primary site server 84 | [Parameter(Mandatory = $false)] 85 | [ValidateNotNullOrEmpty()] 86 | [string] 87 | $CMSite = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\CCM\CcmEval -Name LastSiteCode -ErrorAction SilentlyContinue).LastSiteCode, 88 | [Parameter(Mandatory = $false)] 89 | [ValidateNotNullOrEmpty()] 90 | [System.Management.Automation.PSCredential]$Credential 91 | ) 92 | $adc = Get-ComputersFromAD 93 | if ($Credential) { 94 | $cmc = Get-ComputersFromCM -SiteServer $SiteServer -CMSite $CMSite -Credential $Credential 95 | } 96 | else { 97 | $cmc = Get-ComputersFromCM -SiteServer $SiteServer -CMSite $CMSite 98 | } 99 | Get-ADWithCMComputers -AdComputers $adc -CmComputers $cmc 100 | } 101 | 102 | Function Export-ADCMComparison { 103 | param( 104 | # Primary site server for this Configuration Manager Site 105 | [Parameter(Mandatory = $true)] 106 | [ValidateNotNullOrEmpty()] 107 | [string] 108 | $SiteServer, 109 | # Site name for this primary site server 110 | [Parameter(Mandatory = $false)] 111 | [ValidateNotNullOrEmpty()] 112 | [string] 113 | $CMSite = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\CCM\CcmEval -Name LastSiteCode -ErrorAction SilentlyContinue).LastSiteCode, 114 | [Parameter(Mandatory = $false)] 115 | [ValidateNotNullOrEmpty()] 116 | [System.Management.Automation.PSCredential]$Credential, 117 | # File you want to export your CSV of computers to 118 | [Parameter(Mandatory = $false)] 119 | [ValidateScript( { 120 | if ($_ | Test-Path) { 121 | throw "File or folder already exists!" 122 | } 123 | return $true 124 | })] 125 | [System.IO.FileInfo] 126 | $CSVPath = ".\CMADComputerComparison$(Get-Date -Format 'yyyyMMdd-hhmmss').csv" 127 | ) 128 | Write-Host "Exporting computer list to: $CSVPath" 129 | $adc = Get-ComputersFromAD 130 | if ($Credential) { 131 | $cmc = Get-ComputersFromCM -SiteServer $SiteServer -CMSite $CMSite -Credential $Credential 132 | } 133 | else { 134 | $cmc = Get-ComputersFromCM -SiteServer $SiteServer -CMSite $CMSite 135 | } 136 | Get-ADWithCMComputers -AdComputers $adc -CmComputers $cmc | Export-Csv -Path $CSVPath -NoTypeInformation 137 | } 138 | -------------------------------------------------------------------------------- /CompareADtoCM/readme.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | Set of functions that compares computers in Active Directory or Azure Active Directory to computers in SCCM. Useful in seeing what in AD/AAD and CM are stale records. 4 | 5 | Load the fuctions into memory: 6 | 7 | ``` powershell 8 | . Get-DirectoryCMComparison.ps1 9 | ``` 10 | 11 | ## For AD 12 | 13 | Then execute with something similar to this for an AD comparison to CM: 14 | 15 | ``` powershell 16 | Export-ADCMComparison -SiteServer CM01.domain.com -CMSite SS1 -CSVPath C:\Shared\Computers.csv 17 | ``` 18 | 19 | Or, Add a Date to the file name you export: 20 | 21 | ``` powershell 22 | Export-ADCMComparison -SiteServer CM01.domain.com -CMSite SS1 -CSVPath C:\Shared\Computers_$(get-date -Format yyyyMMdd).csv 23 | ``` 24 | 25 | ## For Azure AD 26 | 27 | For AAD to CM comparison: 28 | First, you may have to install the Microsoft Graph Intune module first to get access to the needed cmdlts. 29 | 30 | `Install-Module -Name Microsoft.Graph.Intune` 31 | 32 | If it is already installed, then connect to Graph and then run the export. 33 | 34 | ``` powershell 35 | Connect-MSGraph 36 | Export-AADCMComparison -SiteServer CM01.domain.com -CMSite SS1 -CSVPath C:\Shared\Computers.csv 37 | ``` 38 | 39 | **NOTE:** By default the above will only export Azure natively joined computers. Use the 'Type' swith to get native and hybrid joined computers, with something like this: 40 | 41 | ``` powershell 42 | Export-AADCMComparison -SiteServer CM01.domain.com -CMSite SS1 -Type AzureNative,HybridJoined -CSVPath C:\Shared\Computers.csv 43 | ``` 44 | 45 | Or if you only want Hybrid joined, then this: 46 | 47 | ``` powershell 48 | Export-AADCMComparison -SiteServer CM01.domain.com -CMSite SS1 -Type HybridJoined -CSVPath C:\Shared\Computers.csv 49 | ``` 50 | -------------------------------------------------------------------------------- /DesktopAnalytics/Update-TelemetryPolicy.ps1: -------------------------------------------------------------------------------- 1 | function Write-Log { 2 | [CmdletBinding()] 3 | Param ( 4 | [Parameter(Mandatory = $false)] 5 | $Message, 6 | 7 | [Parameter(Mandatory = $false)] 8 | $ErrorMessage, 9 | 10 | [Parameter(Mandatory = $false)] 11 | $Component, 12 | 13 | [Parameter(Mandatory = $false, HelpMessage = "1 = Normal, 2 = Warning (yellow), 3 = Error (red)")] 14 | [ValidateSet(1, 2, 3)] 15 | [int]$Type, 16 | 17 | [Parameter(Mandatory = $false, HelpMessage = "Size in KB")] 18 | [int]$LogSizeKB = 512, 19 | 20 | [Parameter(Mandatory = $true)] 21 | $LogFile 22 | ) 23 | <# 24 | Type: 1 = Normal, 2 = Warning (yellow), 3 = Error (red) 25 | #> 26 | Write-Verbose -Message $Message 27 | If ($ErrorMessage) { Write-Verbose -Message $ErrorMessage } 28 | Try { 29 | IF (!(Test-Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName)) { 30 | New-Item -ItemType directory -Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName 31 | } 32 | } 33 | Catch { 34 | Throw 'Failed to find/set parent directory path' 35 | } 36 | $LogLength = $LogSizeKB * 1024 37 | try { 38 | $log = Get-Item $LogFile -ErrorAction Stop 39 | If (($log.length) -gt $LogLength) { 40 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 41 | $Date = Get-Date -Format "MM-dd-yyyy" 42 | $LogMessage = "" 43 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 44 | Move-Item -Path "$LogFile" -Destination "$($LogFile.TrimEnd('g'))_" -Force 45 | } 46 | } 47 | catch { Write-Verbose "Nothing to move or move failed." } 48 | 49 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 50 | $Date = Get-Date -Format "MM-dd-yyyy" 51 | 52 | if ($ErrorMessage -ne $null) { $Type = 3 } 53 | if ($Component -eq $null) { $Component = " " } 54 | if ($Type -eq $null) { $Type = 1 } 55 | 56 | $LogMessage = "" 57 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 58 | } 59 | 60 | #$Items = get-item -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection\Users\*'|where {$_.Property.AllowTelemetry -eq 3} 61 | 62 | $PSDefaultParameterValues["Write-Log:LogFile"] = "C:\Windows\Logs\Software\Update-TelemetryPolicy.log" 63 | $PSDefaultParameterValues["Write-Log:Verbose"] = $false 64 | 65 | 66 | $TempHive = 'TempUser' 67 | Write-Log "******************Beginning Telemetry User Blocking Evaluation******************" 68 | Write-Log "Getting all users from registry that are blocking telemetry." 69 | $BlockingUsers = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection\Users\*' 70 | Write-Log "Getting all user profiles on this machine." 71 | $MachineUserProfiles = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' 72 | Write-Log "Getting all user profile hives already loaded on this machine." 73 | $LoadedHives = Get-ChildItem -Path 'Microsoft.PowerShell.Core\Registry::HKEY_USERS' | Where-Object { $_.PSChildName -notlike "*_classes" } 74 | Write-Log "looping through all of the blocking users [$($BlockingUsers.count)]." 75 | Foreach ($user in $BlockingUsers) { 76 | $UserSid = $User.PSChildName 77 | Write-Log "Processing user with following SID [$UserSid]." 78 | If ($User.AllowTelemetry -eq 0) { 79 | Write-Log "Searching Machine Profiles for user that matches the SID of the blocking user [$UserSid]." 80 | Foreach ($Profile in $MachineUserProfiles) { 81 | If ($Profile.PSChildName -eq $UserSid) { 82 | Write-Log "Found Machine Profile that matches the SID of the blocking user [$UserSid]." 83 | If ($Profile.PSChildName -in ($LoadedHives.PSChildName)) { 84 | Write-Log "Profile matches that of one of the already loaded registry hive files. No need to load a hive." 85 | IF (!([string]::IsNullOrEmpty((Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$($Profile.PSChildName)\Software\Policies\Microsoft\Windows\DataCollection" -ErrorAction Ignore).AllowTelemetry))) { 86 | Write-Log "AllowTelemetry Value found in [HKEY_USERS\$($Profile.PSChildName)\Software\Policies\Microsoft\Windows\DataCollection]" 87 | try { 88 | Remove-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$($Profile.PSChildName)\Software\Policies\Microsoft\Windows\DataCollection" -Name 'AllowTelemetry' 89 | Write-Log "Successfully removed AllowTelemetry value from HKEY_USERS\$($Profile.PSChildName)\Software\Policies\Microsoft\Windows\DataCollection" 90 | } 91 | Catch { 92 | Write-Log "Failed to remove AllowTelemetry value from HKEY_USERS\$($Profile.PSChildName)\Software\Policies\Microsoft\Windows\DataCollection" -Type 3 93 | Write-Log "Exception: $($_.Exception.Message) Reason: $($_.CategoryInfo.Reason)" -Type 3 94 | } 95 | } 96 | Else { 97 | Write-Log "AllowTelemetry Value Not Found in [HKEY_USERS\$($Profile.PSChildName)\Software\Policies\Microsoft\Windows\DataCollection]" -Type 2 98 | } 99 | } 100 | else { 101 | REG.EXE LOAD HKU\$TempHive "$($Profile.ProfileImagePath)\NTUSER.DAT" 102 | If (Test-Path -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$TempHive\Software\Policies\Microsoft\Windows") { 103 | Write-Log "Loaded user hive for [$($Profile.PSChildName)] into [HKEY_USERS\$TempHive] successfully." 104 | try { 105 | Remove-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$TempHive\Software\Policies\Microsoft\Windows\DataCollection" -Name 'AllowTelemetry' 106 | Write-Log "Successfully removed AllowTelemetry value from HKEY_USERS\$TempHive\Software\Policies\Microsoft\Windows\DataCollection" 107 | } 108 | Catch { 109 | Write-Log "failed to remove AllowTelemetry value from HKEY_USERS\$TempHive\Software\Policies\Microsoft\Windows\DataCollection" -Type 3 110 | Write-Log "Exception: $($_.Exception.Message) Reason: $($_.CategoryInfo.Reason)" -Type 3 111 | } 112 | } 113 | else { 114 | Write-Log "Failed to load Hive into temporary space." -Type 3 115 | } 116 | REG.EXE UNLOAD HKU\$TempHive 117 | If (!(Test-Path -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$TempHive\Software")) { 118 | Write-Log "Successfully Unloaded user hive for [$($Profile.PSChildName)] out of [HKEY_USERS\$TempHive]" 119 | } 120 | else { 121 | Write-Log "Failed to Unloaded user hive for [$($Profile.PSChildName)] out of [HKEY_USERS\$TempHive]" -Type 3 122 | } 123 | } 124 | } 125 | } 126 | } 127 | Write-Log "Removing Telemetry collected blocking user [$($User.PSPath)]" 128 | try { 129 | Remove-Item -Path "$($User.PSPath)" -Force 130 | Write-Log "Successfully removed Telemetry Collected blocking user [$($User.PSPath)]" 131 | } 132 | Catch { 133 | Write-Log "Error removing Telemetry Collected blocking user [$($User.PSPath)]" -Type 3 134 | Write-Log "Exception: $($_.Exception.Message) Reason: $($_.CategoryInfo.Reason)" -Type 3 135 | } 136 | Write-Log "Completed processing user with following SID [$UserSid]." 137 | } 138 | Write-Log "******************Completed Telemetry User Blocking Evaluation******************" 139 | -------------------------------------------------------------------------------- /EfficientArrays/CompareArrayBuildTimes.ps1: -------------------------------------------------------------------------------- 1 | #Building an array of 10,000 items in variable $ArrayList using the classic method of adding an index 2 | $ArrayList = @() 3 | foreach ($val in 1..20000) { 4 | $ArrayList += $val} 5 | "The array ArrayList has [$($ArrayList.count)] values" 6 | 7 | #Building an array of 10,000 items in variable $Genericlist using the .Net data type 8 | [System.Collections.Generic.List[int]]$Genericlist = @() 9 | foreach ($val in 1..20000) { 10 | $Genericlist.Add($val)} 11 | "The array Genericlist has [$($Genericlist.count)] values" 12 | -------------------------------------------------------------------------------- /GatherScript/Gather.vbs: -------------------------------------------------------------------------------- 1 | 'Version: 1.12 2 | 'Author: Paul Wetter 3 | 'Modified Date: 10/22/19 4 | 'This script is provided AS-IS with no guarantees, no warranties, and they confer no rights. 5 | 6 | 'These are the arrays of all of the know chassis types: 7 | arrDesktopChassisTypes = array("3","4","5","6","7","15","16","35","36") 8 | arrLatopChassisTypes = array("8","9","10","11","12","14","18","21","30","31","32") 9 | arrServerChassisTypes = array("23","28") 10 | 11 | 'This way if it hangs up on one query, you don't lose them all. More like what a powershell script would do. 12 | On Error Resume Next 13 | 14 | 15 | 'This function will create and write a variable to the task sequence environment. 16 | Function WriteTSVar(VarName,VarValue) 17 | On Error Resume Next 18 | Set env = CreateObject("Microsoft.SMS.TSEnvironment") 19 | error_returned = Err.Number 20 | error_description = Err.Description 21 | 'on error goto 0 22 | if (error_returned <> 0) then 23 | Err.Clear 24 | wscript.echo "TS not running, would have set [" & VarName & "] to value [" & VarValue & "] (Error: " & error_returned & ")." 25 | else 26 | Err.Clear 27 | If VarValue = "" Then 28 | env(VarName) = "" 29 | Else 30 | env(VarName) = VarValue 31 | End If 32 | End If 33 | End Function 34 | 35 | 'This function will attempt to get the value of a variable in the TS environment. 36 | Function GetTSVar(VarName) 37 | On Error Resume Next 38 | Set env = CreateObject("Microsoft.SMS.TSEnvironment") 39 | error_returned = Err.Number 40 | error_description = Err.Description 41 | 'on error goto 0 42 | if (error_returned <> 0) then 43 | Err.Clear 44 | wscript.echo "TS not running, GetTSVar could not find value for [" & VarName & "]." 45 | else 46 | Err.Clear 47 | GetTSVar = env(varName) 48 | End If 49 | End Function 50 | 51 | 'This function will checked if a value (needle) is in an array (haystack). It will return a boolean True or False 52 | Function In_Array(needle, haystack) 53 | findings = False 54 | needle = trim(needle) 55 | For Each hay in haystack 56 | If trim(hay) = needle Then 57 | findings = True 58 | Exit For 59 | End If 60 | Next 61 | In_Array = findings 62 | End Function 63 | 64 | 'Get the CPU architecture: X86, X64 65 | strArch = "X86" 66 | Set wshShell = CreateObject( "WScript.Shell" ) 67 | IF wshShell.ExpandEnvironmentStrings( "%PROCESSOR_ARCHITECTURE%" ) = "AMD64" then 68 | strArch = "X64" 69 | End If 70 | 'wshShell = Nothing 71 | WriteTSVar "Architecture", strArch 72 | 73 | 'Detects if the system is on a battery: True or False 74 | strIsOnBattery = "False" 75 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 76 | set colItems = objWMIService.ExecQuery("SELECT * FROM win32_Battery where BatteryStatus != 2",,32) 77 | for each objItem in colItems 78 | strIsOnBattery = "True" 79 | next 80 | WriteTSVar "IsOnBattery", strIsOnBattery 81 | 82 | 'Gets information on the make and model of the computer 83 | IsVM = "False" 84 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 85 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_ComputerSystemProduct",,32) 86 | for each objItem in colItems 87 | If (objItem.Vendor = "LENOVO") And (UseOldLenovoName <> "true") Then 88 | tempModel = objItem.Version 89 | Else 90 | tempModel = objItem.Name 91 | End If 92 | WriteTSVar "Model", Trim(tempModel) 93 | WriteTSVar "UUID", Trim(ObjItem.UUID) 94 | WriteTSVar "Vendor", Trim(ObjItem.Vendor) 95 | WriteTSVar "Make", Trim(ObjItem.Vendor) 96 | 97 | 'Checks if this computer is a Virtual Machine: True or False 98 | 'Then also detects the virtual platform: Hyper-V, VMware, VirtualBox, or Xen 99 | Select Case tempModel 100 | Case "Virtual Machine" 101 | IsVM = "True" 102 | VMPlatform = "Hyper-V" 103 | Case "VMware Virtual Platform" 104 | IsVM = "True" 105 | VMPlatform = "VMware" 106 | Case "VMware7,1" 107 | IsVM = "True" 108 | VMPlatform = "VMware" 109 | Case "VirtualBox" 110 | IsVM = "True" 111 | VMPlatform = "VirtualBox" 112 | Case "Xen" 113 | IsVM = "True" 114 | VMPlatform = "Xen" 115 | End Select 116 | 117 | WriteTSVar "IsVM", IsVM 118 | WriteTSVar "VMPlatform", VMPlatform 119 | 120 | Next 121 | 122 | 'Get the total memory installed 123 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 124 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_ComputerSystem",,32) 125 | for each objItem in colItems 126 | strMemory = objItem.TotalPhysicalMemory/1024/1024 127 | arrMemory = Split(strMemory,".") 128 | strMemory = arrMemory(0) 129 | WriteTSVar "Memory", strMemory 130 | Next 131 | 132 | 'Gets the product for the machine. Most useful with Lenovo and HP 133 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 134 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_BaseBoard",,32) 135 | for each objItem in colItems 136 | WriteTSVar "Product", Trim(objItem.Product) 137 | Next 138 | 139 | 'Gets the serial number and other bios info 140 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 141 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_BIOS",,32) 142 | for each objItem in colItems 143 | WriteTSVar "SerialNumber", Trim(objItem.SerialNumber) 144 | WriteTSVar "BIOSVersion", Trim(objItem.SMBIOSBIOSVersion) 145 | WriteTSVar "BIOSReleaseDate", Trim(objItem.ReleaseDate) 146 | Next 147 | 148 | 'Gets the OS version and build of the system. If in PE, this will be the PE version info 149 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 150 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem",,32) 151 | for each objItem in colItems 152 | WriteTSVar "OSCurrentVersion", Trim(objItem.Version) 153 | WriteTSVar "OSCurrentBuild", Trim(objItem.BuildNumber) 154 | Next 155 | 156 | 'Will detect if it is a Desktop, Laptop, or Server, based on the ChassisTypes 157 | IsDesktop = "False" 158 | IsLaptop = "False" 159 | IsServer = "False" 160 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 161 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_SystemEnclosure",,32) 162 | for each objItem in colItems 163 | WriteTSVar "AssetTag", objItem.SMBIOSAssetTag 164 | For each CT in objItem.ChassisTypes 165 | If In_Array(CT,arrDesktopChassisTypes) Then 166 | IsDesktop = "True" 167 | End If 168 | If In_Array(CT,arrLatopChassisTypes) Then 169 | IsLaptop = "True" 170 | End If 171 | If In_Array(CT,arrServerChassisTypes) Then 172 | IsServer = "True" 173 | End If 174 | Next 175 | WriteTSVar "IsDesktop", IsDesktop 176 | WriteTSVar "IsLaptop", IsLaptop 177 | WriteTSVar "IsServer", IsServer 178 | Next 179 | 180 | 'Returns a boolean true if the gateway value exists 181 | Function GatewayDefined(obj) 182 | Success = false 183 | On Error Resume Next 184 | intType = vartype(obj) 185 | if Err.Number = 0 Then 186 | If intType > 1 Then 187 | Success = true 188 | End If 189 | End if 190 | 'On Error Goto 0 191 | GatewayDefined = Success 192 | End Function 193 | 194 | 'This gets network information like IP Address and Gateway 195 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 196 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_NetworkAdapterConfiguration where IPEnabled = 1",,32) 197 | for each objItem in colItems 198 | for each IpAddr in objItem.IPAddress 199 | strCurrentIPs = GetTSVar("IPAddress") 200 | if IpAddr <> "" Then 201 | SplitIP = Split (IpAddr, ".") 202 | If (InStr(IpAddr,".") > 0) Then 203 | If (((SplitIP(0) = "169") And (SplitIP(1) = "254")) Or (IpAddr = "0.0.0.0")) Then 204 | 'Not a good IP 205 | else 206 | If strCurrentIPs <> "" Then 207 | WriteTSVar "IPAddress", strCurrentIPs & "," & IpAddr 208 | Else 209 | WriteTSVar "IPAddress", IpAddr 210 | End If 211 | End If 212 | End If 213 | End If 214 | Next 215 | If (GatewayDefined(ObjItem.DefaultIPGateway) = true) Then 216 | for each DefaultGW in objItem.DefaultIPGateway 217 | strCurrentGW = GetTSVar("DefaultGateway") 218 | if DefaultGW <> "" Then 219 | SplitGW = Split (DefaultGW, ".") 220 | If (InStr(DefaultGW,".") > 0) Then 221 | If (DefaultGW = "0.0.0.0") Then 222 | 'Not a good IP 223 | else 224 | If strCurrentGW <> "" Then 225 | WriteTSVar "DefaultGateway", strCurrentGW & "," & DefaultGW 226 | Else 227 | WriteTSVar "DefaultGateway", DefaultGW 228 | End If 229 | End If 230 | End If 231 | End If 232 | Next 233 | End If 234 | Next 235 | 236 | 'Gets the Mac Address off of the network adapter 237 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 238 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_NetworkAdapter where NetConnectionStatus = 2",,32) 239 | for each objItem in colItems 240 | MacAddress = objItem.MACAddress 241 | Next 242 | WriteTSVar "MacAddress", MacAddress 243 | 244 | 'Gets the max clock speed off of the CPU 245 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 246 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Processor",,32) 247 | for each objItem in colItems 248 | WriteTSVar "ProcessorSpeed", objItem.MaxClockSpeed 249 | Next 250 | 251 | 'Gets Bitlocker information. 252 | IsBDE = "False" 253 | BitlockerEncryptionType = "N/A" 254 | BitlockerEncryptionMethod = "N/A" 255 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2\Security\MicrosoftVolumeEncryption") 256 | set colItems = objWMIService.ExecQuery("Select * from Win32_EncryptableVolume where ProtectionStatus != 0",,32) 257 | for each objItem in colItems 258 | IsBDE = "True" 259 | Select Case objItem.EncryptionMethod 260 | Case 0 261 | strEncMethod = "None" 262 | Case 1 263 | strEncMethod = "AES_128_WITH_DIFFUSER" 264 | Case 2 265 | strEncMethod = "AES_256_WITH_DIFFUSER" 266 | Case 3 267 | strEncMethod = "AES_128" 268 | Case 4 269 | strEncMethod = "AES_256" 270 | Case 5 271 | strEncMethod = "HARDWARE_ENCRYPTION" 272 | Case 6 273 | strEncMethod = "XTS_AES_128" 274 | Case 7 275 | strEncMethod = "XTS_AES_256" 276 | End Select 277 | Next 278 | WriteTSVar "IsBDE", IsBDE 279 | WriteTSVar "BitlockerEncryptionMethod", strEncMethod 280 | 'Set objBde = objWMIService.Get("Win32_EncryptableVolume") 281 | 282 | 'UEFI And Secureboot: True or False 283 | Err.Clear 284 | strSecureBoot = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecureBoot\State\UEFISecureBootEnabled" 285 | strUEFI = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecureBoot\State\" 286 | Set objShell = WScript.CreateObject("WScript.Shell") 287 | 'On Error Resume Next 288 | boolUEFI = objShell.RegRead(strUEFI) 289 | error_returned = Err.Number 290 | error_description = Err.Description 291 | 'on error goto 0 292 | If (error_returned <> 0) then 293 | IsUEFI = "False" 294 | Else 295 | Err.Clear 296 | IsUEFI = "True" 297 | End If 298 | WriteTSVar "IsUEFI", IsUEFI 299 | 'On Error Resume Next 300 | Err.Clear 301 | SecureBootEnabled = "False" 302 | intSecureBoot = objShell.RegRead(strSecureBoot) 303 | error_returned = Err.Number 304 | error_description = Err.Description 305 | 'on error goto 0 306 | If (error_returned <> 0) then 307 | SecureBootEnabled = "False" 308 | Else 309 | Err.Clear 310 | If (intSecureBoot = 1) Then 311 | SecureBootEnabled = "True" 312 | End If 313 | End If 314 | WriteTSVar "SecureBootEnabled", SecureBootEnabled 315 | 316 | 'Checks if the boot disk is AHCI: True or False 317 | 'Optionally, we can be more specific and have a third option of "Probably" or "Likely" 318 | IsAHCI = "False" 319 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 320 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_IDEController where Name LIKE ""%AHCI%""",,32) 321 | for each objItem in colItems 322 | IsAHCI = "True" 323 | Next 324 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_DiskDrive WHERE InterfaceType LIKE ""IDE"" AND MediaType Like ""Fixed hard disk media""",,32) 325 | for each objItem in colItems 326 | HasIDEDrives = "True" 327 | Next 328 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 329 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_IDEController where Name LIKE ""%AHCI%""",,32) 330 | for each objItem in colItems 331 | IsAHCI = "True" 332 | Next 333 | 334 | 'Find any SCSI controller, excluding the Microsoft storage spaces controller 335 | HasSCSI = "False" 336 | set objWMIService = GetObject("winmgmts:\\.\root\cimv2") 337 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_SCSIController WHERE DriverName != ""spaceport""",,32) 338 | for each objItem in colItems 339 | HasSCSI = "True" 340 | Next 341 | 342 | If ((isAHCI = "True") And (HasIDEDrives = "True") And (HasSCSI = "False")) Then 343 | IsAHCI = "True" 344 | ElseIf ((isAHCI = "True") And (HasIDEDrives = "True")) Then 345 | 'This section is most likely that it is using AHCI. Because, if it has scsi, it may be using a scsi boot instead of AHCI. 346 | IsAHCI = "True" 347 | Else 348 | IsAHCI = "False" 349 | End If 350 | WriteTSVar "IsAHCI", IsAHCI 351 | 352 | 'See if TPM Is enabled 353 | TPMExists = "False" 354 | TMPIsActivated = "False" 355 | TPMIsEnabled = "False" 356 | set objWMIService = GetObject("winmgmts:\\.\root\CIMV2\Security\MicrosoftTpm") 357 | set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Tpm",,32) 358 | for each objItem in colItems 359 | TPMExists = "True" 360 | If (objItem.IsActivated_InitialValue = "True") Then 361 | TMPIsActivated = "True" 362 | End If 363 | If (objItem.IsEnabled_InitialValue = "True") Then 364 | TPMIsEnabled = "True" 365 | End If 366 | Next 367 | WriteTSVar "TPMExists", TPMExists 368 | WriteTSVar "TMPIsActivated", TMPIsActivated 369 | WriteTSVar "TPMIsEnabled", TPMIsEnabled 370 | -------------------------------------------------------------------------------- /GatherScript/README.md: -------------------------------------------------------------------------------- 1 | Gather.vbs 2 | ========= 3 | 4 | This VBScript attempts to gather all the same variables that the MDT gather step would collect. VBScript has the advantage over PowerShell because you don't have to add PowerShell to the boot image. 5 | 6 | See Blog Post here: https://wetterssource.com/gather-script-replace-mdt 7 | 8 | Execute 9 | ========= 10 | Must execute from elevated command prompt. Should be run with cscript to output to command line. 11 | 12 | ```cscript.exe //nologo gather.vbs``` 13 | 14 | Output Variables 15 | ========= 16 | Sample of the variables and values that it would output. 17 | 18 | ```Architecture = X64 19 | IsOnBattery = True 20 | Model = Surface Book 2 21 | UUID = 0FFFFFFF-DFFF-7777-0999-5E4FACEB00C5 22 | Vendor = Microsoft Corporation 23 | Make = Microsoft Corporation 24 | IsVM = False 25 | VMPlatform = 26 | Memory = 16308 27 | Product = Surface Book 2 28 | SerialNumber = 002123456789 29 | BIOSVersion = 389.2370.769 30 | BIOSReleaseDate = 20181002000000.000000+000 31 | OSCurrentVersion = 10.0.17134 32 | OSCurrentBuild = 17134 33 | AssetTag = 34 | IsDesktop = False 35 | IsLaptop = True 36 | IsServer = False 37 | IPAddress = 192.168.99.122 38 | DefaultGateway = 192.168.99.1 39 | MacAddress = 62:45:BB:22:AD:33 40 | ProcessorSpeed = 2112 41 | IsBDE = True 42 | BitlockerEncryptionMethod = XTS_AES_128 43 | IsUEFI = True 44 | SecureBootEnabled = True 45 | IsAHCI = False``` 46 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Add-pwSharePointListItem.ps1: -------------------------------------------------------------------------------- 1 | function Add-pwSharePointListItem { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | [hashtable] 6 | $AuthHeader, 7 | [Parameter(Mandatory=$true)] 8 | [guid] 9 | $SiteId, 10 | [Parameter(Mandatory=$true)] 11 | [string] 12 | $ListTitle, 13 | [Parameter(Mandatory=$true)] 14 | [string] 15 | $ItemJson 16 | ) 17 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 18 | $GraphUrl = "https://graph.microsoft.com/v1.0/sites/$SiteID/lists/$ListTitle/items" 19 | Invoke-RestMethod -Uri $GraphUrl -Method 'POST' -Body $ItemJson -Headers $AuthHeader -ContentType "application/json" 20 | } 21 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Clear-pwSharePointList.ps1: -------------------------------------------------------------------------------- 1 | function Clear-pwSharePointList { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | [hashtable] 6 | $AuthHeader, 7 | [Parameter(Mandatory=$true)] 8 | [guid] 9 | $SiteId, 10 | [Parameter(Mandatory=$true)] 11 | [string] 12 | $ListTitle 13 | ) 14 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 15 | $GraphGetItems = "https://graph.microsoft.com/v1.0/sites/$SiteID/lists/$ListTitle/items" 16 | $Items = Invoke-RestMethod -Uri $GraphGetItems -Method 'GET' -Headers $AuthHeader -ContentType "application/json" 17 | foreach ($Id in $Items.value.id){ 18 | $GraphDeleteItem = "https://graph.microsoft.com/v1.0/sites/$SiteID/lists/$ListTitle/items/$Id" 19 | try { 20 | $null = Invoke-RestMethod -Uri $GraphDeleteItem -Method 'DELETE' -Headers $AuthHeader -ContentType "application/json" 21 | } 22 | catch{ 23 | Throw "Failed to remove item from list" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Get-pwGraphAuthorizationHeader.ps1: -------------------------------------------------------------------------------- 1 | function Get-pwGraphAuthorizationHeader { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [string] 6 | $Tenant, 7 | [Parameter(Mandatory = $true, ParameterSetName = 'Credman')] 8 | [string] 9 | $CredManagerCredentialTarget, 10 | [Parameter(Mandatory = $true, ParameterSetName = 'Cred')] 11 | [pscredential] 12 | $Credential, 13 | [Parameter(Mandatory = $false)] 14 | [string] 15 | $Scope = "https://graph.microsoft.com/.default" 16 | ) 17 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 18 | $CredManCred = Get-StoredCredential -Target $CredManagerCredentialTarget 19 | $AppRegSecret = [System.Net.NetworkCredential]::new('', $CredManCred.Password).Password 20 | $Body = @{ 21 | client_id = $CredManCred.UserName 22 | client_secret = $AppRegSecret 23 | scope = $Scope 24 | grant_type = 'client_credentials' 25 | } 26 | $GraphUrl = "https://login.microsoftonline.com/$($Tenant).onmicrosoft.com/oauth2/v2.0/token" 27 | $AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method "Post" -Body $Body 28 | 29 | $Header = @{ 30 | Authorization = $AuthorizationRequest.access_token 31 | "Content-Type"= "application/json" 32 | } 33 | return $Header 34 | } -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Get-pwListDifferences.ps1: -------------------------------------------------------------------------------- 1 | function Get-pwListDifferences { 2 | <# 3 | .SYNOPSIS 4 | Gets a hashtable that contains 2 lists. Removal and Addition. 5 | 6 | .DESCRIPTION 7 | Gets a hashtable that contains 2 lists. Removal and Addition. 8 | Removal is a list of items that should be removed from the destination table/list. These items no longer exist in the source but still exist in the destination. 9 | Addition is a list of items that should be added to the destination table/list. These items exist in the source but do not exist in the destination. 10 | 11 | .PARAMETER Source 12 | The source list of items as a hashtable. 13 | 14 | .PARAMETER Destination 15 | The destination list of items as a hashtable. 16 | 17 | .PARAMETER Fields 18 | A hashtable that has the field mapping of the destination to the source. 19 | 20 | .EXAMPLE 21 | $FieldMap = @{'Name' = 'Title'; 'Age' = 'AgeInYears'} 22 | $List = @{1 = [pscustomobject]@{Name='Bob';Age=27};2 = [pscustomobject]@{Name='Dave';Age=55};} 23 | $Dest = @{1 = [pscustomobject]@{Name='Bob';Age=29};2 = [pscustomobject]@{Name='Dave';Age=55};} 24 | Get-pwListDifferences -Source $list -Destination $dest -FieldMap $FieldMap 25 | 26 | .NOTES 27 | General notes 28 | #> 29 | [OutputType([hashtable])] 30 | [CmdletBinding()] 31 | param ( 32 | [Parameter(Mandatory=$true)] 33 | [hashtable] 34 | $Source, 35 | [Parameter(Mandatory=$true)] 36 | [hashtable] 37 | $Destination, 38 | [Parameter(Mandatory=$true)] 39 | [string[]] 40 | $Fields 41 | ) 42 | # initialize the results as an hash of 2 empty lists 43 | $results = @{ 44 | 'Addition' = New-Object System.Collections.Generic.List[Object] 45 | 'Removal' = New-Object System.Collections.Generic.List[Object] 46 | } 47 | 48 | #region Make sure the hash tables are sequential. 49 | $FreshSource = @{} 50 | $FreshDestination = @{} 51 | [int]$Ctr = 0 52 | foreach($SV in $Source.Values) { 53 | $Ctr++ 54 | $FreshSource.Add($Ctr, $SV) 55 | } 56 | [int]$Ctr = 0 57 | foreach($DV in $Destination.Values) { 58 | $Ctr++ 59 | $FreshDestination.Add($Ctr, $DV) 60 | } 61 | #endregion 62 | 63 | #clone the hashtables to use for self eliminating lists. 64 | $AddToList = $FreshSource.Clone() 65 | $RemoveFromList = $FreshDestination.Clone() 66 | 67 | # For loop to find items to add 68 | for ($i = 1; $i -le $FreshSource.Count; $i++) { 69 | Write-Verbose "Checking for additions: $i" 70 | $row = $FreshSource.$i 71 | $match = $false 72 | foreach ($j in $FreshDestination.Values){ 73 | foreach ($p in $Fields){ 74 | if ($p -eq "id") {Continue} 75 | if ($j.$p -eq $row.$p){ 76 | $match = $true 77 | } else { 78 | $match = $false 79 | break 80 | } 81 | } 82 | If ($match -eq $true){ 83 | Write-Verbose "Match $i : $($row.$p)" 84 | $AddToList.Remove($i) 85 | } 86 | } 87 | } 88 | 89 | # For loop to find items to remove 90 | for ($i = 1; $i -le $FreshDestination.Count; $i++) { 91 | Write-Verbose "Checking for Removals: $i" 92 | $row = $FreshDestination.$i 93 | $match = $false 94 | foreach ($j in $FreshSource.Values){ 95 | foreach ($p in $Fields){ 96 | if ($p -eq "id") {Continue} 97 | if ($j.$p -eq $row.$p){ 98 | $match = $true 99 | } else { 100 | $match = $false 101 | break 102 | } 103 | } 104 | If ($match -eq $true){ 105 | Write-Verbose "Match $i : $($row.$p)" 106 | $RemoveFromList.Remove($i) 107 | } 108 | } 109 | } 110 | foreach($AddTo in $AddToList.Values) { 111 | $results.Addition.Add($AddTo) 112 | } 113 | foreach($RemoveFrom in $RemoveFromList.Values) { 114 | $results.Removal.Add($RemoveFrom) 115 | } 116 | return $results 117 | } 118 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Get-pwSharePointListItems.ps1: -------------------------------------------------------------------------------- 1 | function Get-pwSharePointListItems { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | [hashtable] 6 | $AuthHeader, 7 | [Parameter(Mandatory=$true)] 8 | [guid] 9 | $SiteId, 10 | [Parameter(Mandatory=$true)] 11 | [string] 12 | $ListTitle, 13 | [Parameter(Mandatory=$false)] 14 | [string[]] 15 | $Fields 16 | ) 17 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 18 | $GraphGetItems = "https://graph.microsoft.com/v1.0/sites/$SiteID/lists/$ListTitle/items?expand=fields" 19 | try { 20 | $Items = Invoke-RestMethod -Uri $GraphGetItems -Method 'GET' -Headers $AuthHeader -ContentType "application/json" 21 | } 22 | catch { 23 | Throw "Unable to get List. Error $_" 24 | } 25 | if($Null -ne $FieldMap){ 26 | $UseFields = @() 27 | Foreach ($f in $Fields){ 28 | $UseFields += $f 29 | } 30 | $Items.value.fields | Select-Object $UseFields 31 | } else { 32 | $Items.value.fields 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/New-pwListItemJsonBody.ps1: -------------------------------------------------------------------------------- 1 | function New-pwListItemJsonBody { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | $Properties 6 | ) 7 | $Body = @{ 8 | fields = @{} 9 | } 10 | if ($Properties -is 'HashTable') { 11 | foreach($PropertyName in $Properties.Keys) { 12 | $Body.fields.Add("$PropertyName","$($Properties.$PropertyName)") 13 | } 14 | } 15 | if ($Properties -is 'PSCustomObject'){ 16 | foreach($PropertyName in ($Properties | Get-Member -MemberType NoteProperty).Name) { 17 | $Body.fields.Add("$PropertyName","$($Properties.$PropertyName)") 18 | } 19 | } 20 | $JsonBody = $Body | ConvertTo-Json -Compress 21 | return $JsonBody 22 | } 23 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Remove-pwSharePointListItem.ps1: -------------------------------------------------------------------------------- 1 | function Remove-pwSharePointListItem { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | [hashtable] 6 | $AuthHeader, 7 | [Parameter(Mandatory=$true)] 8 | [guid] 9 | $SiteId, 10 | [Parameter(Mandatory=$true)] 11 | [string] 12 | $ListTitle, 13 | [Parameter(Mandatory=$true)] 14 | [int] 15 | $ItemId 16 | ) 17 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 18 | $GraphDeleteItem = "https://graph.microsoft.com/v1.0/sites/$SiteID/lists/$ListTitle/items/$ItemId" 19 | try { 20 | Invoke-RestMethod -Uri $GraphDeleteItem -Method 'DELETE' -Headers $AuthHeader -ContentType "application/json" -ErrorAction Stop 21 | } 22 | catch { 23 | throw "Failed to remove item from list. Error: $_" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Set-pwPropertyMap.ps1: -------------------------------------------------------------------------------- 1 | function Set-pwPropertyMap { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | [hashtable] 6 | $Mapping, 7 | [Parameter(Mandatory=$true)] 8 | [psobject] 9 | $SourceItem 10 | ) 11 | $MappedProperties = @{} 12 | $Properties = ($SourceItem | Get-Member -Type Property).Name 13 | foreach ($Property in $Properties) { 14 | If ($Property -in $Mapping.Values) { 15 | Foreach ($Key in ($Mapping.GetEnumerator() | Where-Object { $_.Value -eq "$Property" })) 16 | { 17 | Write-Verbose "Adding [$($Key.name)] with value [$($SourceItem.($Property))]" 18 | $MappedProperties.Add($Key.name, ($SourceItem.($Property)).ToString()) 19 | } 20 | } else { 21 | Write-Verbose "Property [$Property] not found in mapping. Excluding." 22 | } 23 | } 24 | Return $MappedProperties 25 | } 26 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/Commands/Test-pwSharePointListConnection.ps1: -------------------------------------------------------------------------------- 1 | function Test-pwSharePointListConnection { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | [hashtable] 6 | $AuthHeader, 7 | [Parameter(Mandatory=$true)] 8 | [guid] 9 | $SiteId, 10 | [Parameter(Mandatory=$true)] 11 | [string] 12 | $ListTitle 13 | ) 14 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 15 | $GraphGetItems = "https://graph.microsoft.com/v1.0/sites/$SiteID/lists/$ListTitle" 16 | $List = Invoke-RestMethod -Uri $GraphGetItems -Method 'GET' -Headers $AuthHeader -ContentType "application/json" -ErrorAction SilentlyContinue 17 | if($Null -eq $List){ 18 | return $false 19 | } else { 20 | if ($List.webUrl -like "*$ListTitle*"){ 21 | return $true 22 | } else { 23 | return $false 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/PWSharePoint.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulwetter/WettersSource/6487c04bb6097baaabb0cc362b4b4a8ad57560dc/Graph/SharePoint/PWSharePoint/PWSharePoint.psd1 -------------------------------------------------------------------------------- /Graph/SharePoint/PWSharePoint/PWSharePoint.psm1: -------------------------------------------------------------------------------- 1 | $CommandFiles = Get-ChildItem -Path "$PSScriptRoot\Commands" -Filter '*.ps1' 2 | foreach($file in $CommandFiles){ 3 | . $file.FullName 4 | } 5 | 6 | $PrivateCommandFiles = Get-ChildItem -Path "$PSScriptRoot\PrivateCommands" -Filter '*.ps1' 7 | foreach($file in $PrivateCommandFiles){ 8 | . $file.FullName 9 | } 10 | -------------------------------------------------------------------------------- /Graph/SharePoint/SampleSharePointListUpdate.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulwetter/WettersSource/6487c04bb6097baaabb0cc362b4b4a8ad57560dc/Graph/SharePoint/SampleSharePointListUpdate.ps1 -------------------------------------------------------------------------------- /IntuneGraph/AppAssignment.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(DefaultParameterSetName="GroupID")] 2 | param ( 3 | [Parameter(ParameterSetName="GroupID", Mandatory=$true)] 4 | [guid] 5 | $GroupID, 6 | [Parameter(ParameterSetName="AllDevice", Mandatory=$false)] 7 | [switch] 8 | $AllDevice, 9 | [Parameter(ParameterSetName="AllDevice", Mandatory=$false)] 10 | [Parameter(ParameterSetName="GroupID", Mandatory=$false)] 11 | [ValidateSet("showReboot","hideAll","showAll")] 12 | [string] 13 | $Notification = "showReboot", 14 | [Parameter(ParameterSetName="AllDevice", Mandatory=$false)] 15 | [Parameter(ParameterSetName="GroupID", Mandatory=$false)] 16 | [string] 17 | $AppFilter 18 | ) 19 | 20 | 21 | function New-PWRequiredAppAssignment { 22 | [CmdletBinding()] 23 | param ( 24 | [guid] 25 | $GroupID, 26 | [ValidateSet("showReboot","hideAll","showAll")] 27 | [string] 28 | $Notification = "showReboot", 29 | [switch] 30 | $AllDevice, 31 | $ExistingAssignments 32 | ) 33 | $assignment = [pscustomobject]@{ 34 | "mobileAppAssignments" = [System.Collections.Generic.List[pscustomobject]]( 35 | [pscustomobject]@{ 36 | '@odata.type' = '#microsoft.graph.mobileAppAssignment' 37 | intent = 'Required' 38 | target = [pscustomobject]@{ 39 | '@odata.type' = '#microsoft.graph.groupAssignmentTarget' 40 | 'groupId' = $GroupID 41 | } 42 | settings = [pscustomobject]@{ 43 | '@odata.type' = '#microsoft.graph.win32LobAppAssignmentSettings' 44 | 'notifications' = $Notification 45 | 'restartSettings' = $null 46 | 'installTimeSettings' = $null 47 | 'deliveryOptimizationPriority' = 'notConfigured' 48 | } 49 | } 50 | ) 51 | } 52 | if ($AllDevice){ 53 | $assignment.mobileAppAssignments[0].target = [pscustomobject]@{ 54 | '@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget' 55 | } 56 | } 57 | if ($ExistingAssignments) { 58 | foreach ($OldAssignment in $ExistingAssignments){ 59 | $assignment.mobileAppAssignments.Add( 60 | [pscustomobject]@{ 61 | '@odata.type' = '#microsoft.graph.mobileAppAssignment' 62 | intent = $OldAssignment.intent 63 | target = $OldAssignment.target 64 | settings = $OldAssignment.settings 65 | } 66 | ) 67 | } 68 | } 69 | return $assignment|ConvertTo-Json -Depth 99 -Compress 70 | } 71 | 72 | function New-PWLobAppAssignment { 73 | [CmdletBinding()] 74 | param ( 75 | [guid] 76 | $AppID, 77 | [string] 78 | $Assignment 79 | ) 80 | 81 | $AssignSplat = @{ 82 | 'HttpMethod' = "POST" 83 | 'Url' = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$AppID/assign" 84 | 'Content' = "$Assignment" 85 | } 86 | Invoke-MSGraphRequest @AssignSplat 87 | } 88 | 89 | function Get-PWLobApps { 90 | [CmdletBinding()] 91 | param ( 92 | [string] 93 | $AppNameFilter 94 | ) 95 | $LOBApps = (Invoke-MSGraphRequest -HttpMethod GET -Url https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/).value|Where {$_.'@odata.type' -like '#microsoft.graph.win32LobApp'} 96 | If (!([string]::IsNullOrEmpty($AppNameFilter))){ 97 | $Apps = $LOBApps | Where-Object {$_.displayName -like "*$AppNameFilter*"} 98 | } else { 99 | $Apps = $LOBApps 100 | } 101 | return $Apps 102 | } 103 | 104 | 105 | function Get-PWLobAppAssignment { 106 | [CmdletBinding()] 107 | param ( 108 | [guid] 109 | $AppID 110 | ) 111 | 112 | $AssignSplat = @{ 113 | 'HttpMethod' = "GET" 114 | 'Url' = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$AppID/assignments" 115 | ErrorAction = 'Stop' 116 | } 117 | $assignments = Invoke-MSGraphRequest @AssignSplat 118 | $assignments.value 119 | } 120 | 121 | Write-Host "Connecting to Graph.." 122 | try {Connect-MSGraph -ErrorAction Stop} 123 | Catch {throw "Failed to connect to gragh. error: $_"} 124 | Write-Host "Getting all LOB apps..." 125 | try { 126 | if ($AppFilter){ 127 | $Apps = @(Get-PWLobApps -AppNameFilter $AppFilter -ErrorAction Stop) 128 | } else { 129 | $Apps = @(Get-PWLobApps -ErrorAction Stop) 130 | } 131 | } 132 | Catch {throw "Failed to get apps. error: $_"} 133 | write-host "Found [$($Apps.count)] apps." 134 | $SelectedApps = @($Apps|Out-GridView -Title "Select Apps to Deploy to" -PassThru) 135 | write-host "Select [$($SelectedApps.count)] apps for deployment." 136 | foreach ($SelectedApp in $SelectedApps) { 137 | Write-Host "Creating deployment for app [$($SelectedApp.displayName)]" 138 | $ExistingAssignments = Get-PWLobAppAssignment -AppID $SelectedApp.id 139 | $AddAssignmentSplat = @{} 140 | if ($ExistingAssignments){ 141 | $AddAssignmentSplat.Add('ExistingAssignments', $ExistingAssignments) 142 | $AddAssignmentSplat.Add('ErrorAction',"Stop") 143 | } 144 | if ($GroupID){ 145 | $AddAssignmentSplat.Add('GroupID',"$GroupID") 146 | } 147 | if ($AllDevice){ 148 | $AddAssignmentSplat.Add('AllDevice', $true) 149 | } 150 | try { 151 | $assignments = New-PWRequiredAppAssignment @AddAssignmentSplat 152 | } 153 | catch { 154 | Write-Warning "failed to create assignment json for app [$($SelectedApp.displayName)]" 155 | continue 156 | } 157 | try { 158 | New-PWLobAppAssignment -AppID $SelectedApp.id -Assignment $assignments -ErrorAction Stop 159 | Write-Host "Assigned Group to application successfully." 160 | } 161 | catch { 162 | Write-Warning "Failed to assign group [$groupid] to application [$($SelectedApp.displayName)]" 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /IntuneGraph/Get-IntuneDeviceNotes.ps1: -------------------------------------------------------------------------------- 1 | Function Get-IntuneDeviceNotes{ 2 | <# 3 | .SYNOPSIS 4 | Gets the notes of a device in intune. 5 | 6 | .DESCRIPTION 7 | Gets the notes property on a device in intune using the beta Graph api 8 | 9 | .PARAMETER DeviceName 10 | The name of the device that you want to get the notes field from as it appears in intune. 11 | 12 | .EXAMPLE 13 | Get-IntuneDeviceNotes -DeviceName TestDevice01 14 | 15 | .NOTES 16 | Must connect to the graph api first with Connect-MSGraph. 17 | #> 18 | [CmdletBinding()] 19 | param ( 20 | [Parameter(Mandatory=$true)] 21 | [String] 22 | $DeviceName 23 | ) 24 | Try { 25 | $DeviceID = (Get-IntuneManagedDevice -filter "deviceName eq '$DeviceName'" -ErrorAction Stop).id 26 | } 27 | Catch { 28 | Write-Error $_.Exception.Message 29 | break 30 | } 31 | $deviceId = (Get-IntuneManagedDevice -Filter "deviceName eq 'BeesKnees'").id 32 | $Resource = "deviceManagement/managedDevices('$deviceId')" 33 | $properties = 'notes' 34 | $uri = "https://graph.microsoft.com/beta/$($Resource)?select=$properties" 35 | Try{ 36 | (Invoke-MSGraphRequest -HttpMethod GET -Url $uri -ErrorAction Stop).notes 37 | } 38 | Catch{ 39 | Write-Error $_.Exception.Message 40 | break 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /IntuneGraph/Set-IntuneDeviceNotes.ps1: -------------------------------------------------------------------------------- 1 | Function Set-IntuneDeviceNotes{ 2 | <# 3 | .SYNOPSIS 4 | Sets the notes on a device in intune. 5 | 6 | .DESCRIPTION 7 | Sets the notes property on a device in intune using the beta Graph api 8 | 9 | .PARAMETER DeviceName 10 | The name of the device as it appears in intune. 11 | 12 | .PARAMETER Notes 13 | A string of the notes that you would like recorded in the notes field in intune. 14 | 15 | .EXAMPLE 16 | Set-IntuneDeviceNotes -DeviceName TestDevice01 -Notes "This is a note on the stuff and things for this device." 17 | 18 | .NOTES 19 | Must connect to the graph api first with Connect-MSGraph. 20 | #> 21 | [CmdletBinding()] 22 | param ( 23 | [Parameter(Mandatory=$true)] 24 | [String] 25 | $DeviceName, 26 | [Parameter(Mandatory=$false)] 27 | [String] 28 | $Notes 29 | ) 30 | Try { 31 | $DeviceID = (Get-IntuneManagedDevice -filter "deviceName eq '$DeviceName'" -ErrorAction Stop).id 32 | } 33 | Catch{ 34 | Write-Error $_.Exception.Message 35 | break 36 | } 37 | If (![string]::IsNullOrEmpty($DeviceID)){ 38 | $Resource = "deviceManagement/managedDevices('$DeviceID')" 39 | $GraphApiVersion = "Beta" 40 | $URI = "https://graph.microsoft.com/$graphApiVersion/$($resource)" 41 | $JSONPayload = @" 42 | { 43 | notes:"$Notes" 44 | } 45 | "@ 46 | Try{ 47 | Write-Verbose "$URI" 48 | Write-Verbose "$JSONPayload" 49 | Invoke-MSGraphRequest -HttpMethod PATCH -Url $uri -Content $JSONPayload -Verbose -ErrorAction Stop 50 | } 51 | Catch{ 52 | Write-Error $_.Exception.Message 53 | break 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /InvokeToastAsUser/SampleToast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulwetter/WettersSource/6487c04bb6097baaabb0cc362b4b4a8ad57560dc/InvokeToastAsUser/SampleToast.jpg -------------------------------------------------------------------------------- /InvokeToastAsUser/ToastHeroImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulwetter/WettersSource/6487c04bb6097baaabb0cc362b4b4a8ad57560dc/InvokeToastAsUser/ToastHeroImage.jpg -------------------------------------------------------------------------------- /InvokeToastAsUser/ToastLogoImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulwetter/WettersSource/6487c04bb6097baaabb0cc362b4b4a8ad57560dc/InvokeToastAsUser/ToastLogoImage.jpg -------------------------------------------------------------------------------- /NumberTaskSequenceSteps/Invoke-ToolInstallation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Installs or uninstalls the Task Sequence Step Number console extension for ConfigMgr. 4 | 5 | .DESCRIPTION 6 | Installs or uninstalls the Task Sequence Step Number console extension for ConfigMgr. 7 | 8 | .PARAMETER SiteServer 9 | Specifies the name of the Site Server where the SMS Provider is installed. 10 | 11 | .PARAMETER Method 12 | Runs the script in either 'Install' or 'Uninstall' mode. 13 | 14 | .PARAMETER Path 15 | Sets the path where the Create Software Update Group Tool script file will be stored. This path must 16 | already exist, the script will not create the path if it is not found. 17 | 18 | .EXAMPLE 19 | .\Invoke-ToolInstallation.ps1 -SiteServer CM01.contoso.com -Method Install -Path C:\Scripts -Verbose 20 | 21 | .NOTES 22 | Borrowed and tweaked this installer script from: https://www.scconfigmgr.com/2016/05/10/create-software-update-group-tool-console-extension-for-configmgr/ 23 | #> 24 | [CmdletBinding(SupportsShouldProcess=$true)] 25 | param( 26 | [parameter(Mandatory=$true, HelpMessage="Site server where the SMS Provider is installed.")] 27 | [ValidateNotNullOrEmpty()] 28 | [string]$SiteServer, 29 | 30 | [parameter(Mandatory=$true, HelpMessage="Specify installation method.")] 31 | [ValidateNotNullOrEmpty()] 32 | [ValidateSet("Install","Uninstall")] 33 | [string]$Method, 34 | 35 | [parameter(Mandatory=$true, HelpMessage="Specify a valid path to where the Task Sequence Step Number script file will be stored.")] 36 | [ValidateNotNullOrEmpty()] 37 | [ValidatePattern("^[A-Za-z]{1}:\\\w+")] 38 | [ValidateScript({ 39 | # Check if path contains any invalid characters 40 | if ((Split-Path -Path $_ -Leaf).IndexOfAny([IO.Path]::GetInvalidFileNameChars()) -ge 0) { 41 | throw "$(Split-Path -Path $_ -Leaf) contains invalid characters" 42 | } 43 | else { 44 | # Check if the whole path exists 45 | if (Test-Path -Path $_ -PathType Container) { 46 | return $true 47 | } 48 | else { 49 | throw "Unable to locate part of or the whole specified path, specify a valid path" 50 | } 51 | } 52 | })] 53 | [string]$Path 54 | ) 55 | Begin { 56 | # Validate that the script is being executed elevated 57 | try { 58 | $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() 59 | $WindowsPrincipal = New-Object Security.Principal.WindowsPrincipal -ArgumentList $CurrentIdentity 60 | if (-not($WindowsPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))) { 61 | Write-Warning -Message "Script was not executed elevated, please re-launch." ; break 62 | } 63 | } 64 | catch { 65 | Write-Warning -Message $_.Exception.Message ; break 66 | } 67 | 68 | # Determine PSScriptRoot 69 | $ScriptRoot = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent 70 | 71 | # Validate ConfigMgr console presence 72 | if ($env:SMS_ADMIN_UI_PATH -ne $null) { 73 | try { 74 | if (Test-Path -Path $env:SMS_ADMIN_UI_PATH -PathType Container -ErrorAction Stop) { 75 | Write-Verbose -Message "ConfigMgr console environment variable detected: $($env:SMS_ADMIN_UI_PATH)" 76 | } 77 | } 78 | catch [Exception] { 79 | Write-Warning -Message $_.Exception.Message ; break 80 | } 81 | } 82 | else { 83 | Write-Warning -Message "ConfigMgr console environment variable was not detected" ; break 84 | } 85 | 86 | # Define installation file variables 87 | $XMLFile = "NumberTsSteps.xml" 88 | $ScriptFile = "Set-TSStepNumbers.ps1" 89 | 90 | # Define node folders 91 | $Node = "f2c07bfb-d83d-4e0b-969b-5da6321c28c2" 92 | 93 | # Validate if required files are present in the script root directory 94 | if (-not(Test-Path -Path (Join-Path -Path $ScriptRoot -ChildPath $XMLFile) -PathType Leaf -ErrorAction SilentlyContinue)) { 95 | Write-Warning -Message "Unable to determine location for '$($XMLFile)'. Make sure it's present in '$($ScriptRoot)'." ; break 96 | } 97 | if (-not(Test-Path -Path (Join-Path -Path $ScriptRoot -ChildPath $ScriptFile) -PathType Leaf -ErrorAction SilentlyContinue)) { 98 | Write-Warning -Message "Unable to determine location for '$($ScriptFile)'. Make sure it's present in '$($ScriptRoot)'." ; break 99 | } 100 | 101 | # Determine Admin console root 102 | $AdminConsoleRoot = ($env:SMS_ADMIN_UI_PATH).Substring(0,$env:SMS_ADMIN_UI_PATH.Length-9) 103 | 104 | # Create Action folders if not exists 105 | $FolderList = New-Object -TypeName System.Collections.ArrayList 106 | $FolderList.AddRange(@( 107 | (Join-Path -Path $AdminConsoleRoot -ChildPath "XmlStorage\Extensions\Actions\$($Node)") 108 | )) | Out-Null 109 | foreach ($CurrentNode in $FolderList) { 110 | if (-not(Test-Path -Path $CurrentNode -PathType Container)) { 111 | Write-Verbose -Message "Creating folder: '$($CurrentNode)'" 112 | New-Item -Path $CurrentNode -ItemType Directory -Force | Out-Null 113 | } 114 | else { 115 | Write-Verbose -Message "Found folder: '$($CurrentNode)'" 116 | } 117 | } 118 | } 119 | Process { 120 | switch ($Method) { 121 | "Install" { 122 | # Edit XML files to contain correct path to script file 123 | if (Test-Path -Path (Join-Path -Path $ScriptRoot -ChildPath $XMLFile) -PathType Leaf -ErrorAction SilentlyContinue) { 124 | Write-Verbose -Message "Editing '$($XMLFile)' to contain the correct path to script file" 125 | $XMLDataFilePath = Join-Path -Path $ScriptRoot -ChildPath $XMLFile 126 | [xml]$XMLDataFile = Get-Content -Path $XMLDataFilePath 127 | $XMLDataFile.ActionDescription.ActionGroups.ActionDescription.Executable | Where-Object { $_.FilePath -like "*powershell.exe*" } | ForEach-Object { 128 | $_.Parameters = $_.Parameters.Replace("#PATH#","'$($Path)\$($ScriptFile)'").Replace("'",'"').Replace("#SERVER#","$($SiteServer)") 129 | } 130 | $XMLDataFile.Save($XMLDataFilePath) 131 | } 132 | else { 133 | Write-Warning -Message "Unable to load '$($XMLFile)' from '$($Path)'. Make sure the file is located in the same folder as the installation script." ; break 134 | } 135 | 136 | # Copy XML to Software Update Groups node 137 | Write-Verbose -Message "Copying '$($XMLFile)' to Task Sequence node action folder" 138 | $XMLStorageSUGArgs = @{ 139 | Path = Join-Path -Path $ScriptRoot -ChildPath $XMLFile 140 | Destination = Join-Path -Path $AdminConsoleRoot -ChildPath "XmlStorage\Extensions\Actions\$($Node)\$($XMLFile)" 141 | Force = $true 142 | } 143 | Copy-Item @XMLStorageSUGArgs 144 | 145 | # Copy script file to specified path 146 | Write-Verbose -Message "Copying '$($ScriptFile)' to: '$($Path)'" 147 | $ScriptFileArgs = @{ 148 | Path = Join-Path -Path $ScriptRoot -ChildPath $ScriptFile 149 | Destination = Join-Path -Path $Path -ChildPath $ScriptFile 150 | Force = $true 151 | } 152 | Copy-Item @ScriptFileArgs 153 | } 154 | "Uninstall" { 155 | # Remove XML file from Software Update Groups node 156 | Write-Verbose -Message "Removing '$($XMLFile)' from Task Sequence node action folder" 157 | $XMLStorageSUGArgs = @{ 158 | Path = Join-Path -Path $AdminConsoleRoot -ChildPath "XmlStorage\Extensions\Actions\$($Node)\$($XMLFile)" 159 | Force = $true 160 | ErrorAction = "SilentlyContinue" 161 | } 162 | if (Test-Path -Path (Join-Path -Path $AdminConsoleRoot -ChildPath "XmlStorage\Extensions\Actions\$($Node)\$($XMLFile)")) { 163 | Remove-Item @XMLStorageSUGArgs 164 | } 165 | else { 166 | Write-Warning -Message "Unable to locate '$(Join-Path -Path $AdminConsoleRoot -ChildPath "XmlStorage\Extensions\Actions\$($Node)\$($XMLFile)")'" 167 | } 168 | 169 | # Remove script file from specified path 170 | Write-Verbose -Message "Removing '$($ScriptFile)' from '$($Path)'" 171 | $ScriptFileArgs = @{ 172 | Path = Join-Path -Path $Path -ChildPath $ScriptFile 173 | Force = $true 174 | } 175 | if (Test-Path -Path (Join-Path -Path $Path -ChildPath $ScriptFile)) { 176 | Remove-Item @ScriptFileArgs 177 | } 178 | else { 179 | Write-Warning -Message "Unable to locate '$(Join-Path -Path $Path -ChildPath $ScriptFile)'" 180 | } 181 | } 182 | } 183 | } 184 | 185 | -------------------------------------------------------------------------------- /NumberTaskSequenceSteps/NumberTsSteps.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AdminUI.UIResources.dll 6 | Microsoft.ConfigurationManagement.AdminConsole.UIResources.Properties.Resources.resources 7 | 8 | MigrationJobs 9 | 10 | 11 | ContextMenu 12 | DefaultHomeTab 13 | 14 | 15 | 16 | 17 | 18 | AdminUI.UIResources.dll 19 | Microsoft.ConfigurationManagement.AdminConsole.UIResources.Properties.Resources.resources 20 | 21 | VendorSpecific 22 | 23 | 24 | ContextMenu 25 | DefaultHomeTab 26 | 27 | 28 | "powershell.exe" 29 | -WindowStyle Minimized -NoLogo -NoProfile -ExecutionPolicy Bypass -file #PATH# -PackageID ##SUB:PackageID## -PrimarySiteServer #SERVER# 30 | 31 | 32 | 33 | 34 | 35 | AdminUI.UIResources.dll 36 | Microsoft.ConfigurationManagement.AdminConsole.UIResources.Properties.Resources.resources 37 | 38 | Delete 39 | 40 | 41 | ContextMenu 42 | DefaultHomeTab 43 | 44 | 45 | "powershell.exe" 46 | -WindowStyle Minimized -NoLogo -NoProfile -ExecutionPolicy Bypass -file #PATH# -PackageID ##SUB:PackageID## -PrimarySiteServer #SERVER# -ClearStepNumbers 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /NumberTaskSequenceSteps/Set-TSStepNumbers.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [Parameter(Mandatory = $true)] 4 | [ValidateNotNullOrEmpty()] 5 | [string]$PackageID, 6 | [Parameter(Mandatory = $false)] 7 | [ValidateNotNullOrEmpty()] 8 | [string]$SiteCode = (New-Object -ComObject Microsoft.SMS.Client -Strict).GetAssignedSite(), 9 | [Parameter(Mandatory = $false)] 10 | [ValidateNotNullOrEmpty()] 11 | [string]$PrimarySiteServer='localhost', 12 | [Parameter(Mandatory = $false)] 13 | [switch]$ClearStepNumbers 14 | ) 15 | 16 | Function Set-PWSequenceStepNumbers { 17 | [CmdletBinding()] 18 | param ($Sequence, $GroupName, [int]$StepCounter = 0) 19 | Write-Verbose "Starting Run: $StepCounter" 20 | foreach ($node in $Sequence.ChildNodes) { 21 | switch ($node.localname) { 22 | 'step' { 23 | $StepCounter++ 24 | if (($StepCounter -eq 1) -and ($FirstStep -ne 0)){$StepCounter = 0;$FirstStep = 0} 25 | Write-Verbose "$StepCounter --- STEP --- $($Node.Name)" 26 | if ($GroupName) { 27 | $newname = "$($Node.name)" -replace '[0-9]*\. ', '' 28 | If ($ClearStepNumbers) { 29 | $Node.name = $newname 30 | } else { 31 | $Node.name = Set-MaxLength -Str "$($StepCounter). $newname" -Length 50 32 | } 33 | $TSStep = New-Object -TypeName psobject -Property @{'StepNumber' = $StepCounter; 'GroupName' = "$GroupName"; 'StepName' = "$($node.Name)" } 34 | } 35 | else { 36 | $newname = "$($Node.name)" -replace '[0-9]*\. ', '' 37 | If ($ClearStepNumbers) { 38 | $Node.name = $newname 39 | } else { 40 | $Node.name = Set-MaxLength -Str "$($StepCounter). $newname" -Length 50 41 | } 42 | $TSStep = New-Object -TypeName psobject -Property @{'StepNumber' = $StepCounter; 'GroupName' = "N/A"; 'StepName' = "$($node.Name)" } 43 | } 44 | $TSStep 45 | } 46 | 'subtasksequence' { 47 | if ([string]::IsNullOrEmpty($node.disable)) { 48 | if ((Get-IsParentGroupDisabled -TSXml $node) -ne $true) { 49 | $StepCounter++ 50 | if (($StepCounter -eq 1) -and ($FirstStep -ne 0)){$StepCounter = 0;$FirstStep = 0} 51 | Write-Verbose "$StepCounter --- SUBTS --- $($Node.Name)" 52 | $SubTSPackageID = $(foreach ($var in $Node.defaultVarList.variable) { if ($Var.property -like 'TsPackageID') { $var.'#text' } }) 53 | $SubSequence = Get-PWTSXml -TSPackageID "$SubTSPackageID" 54 | $SubTaskSequenceXML = Set-PWSequenceStepNumbers -Sequence $SubSequence 55 | $SubTSFinalStepNumber = ($SubTaskSequenceXML.stepnumber | Measure-Object -Maximum).Maximum 56 | if ($GroupName) { 57 | $newname = "$($Node.name)" -replace '[0-9]*\-[0-9]*\. ', '' 58 | $newname = "$newname" -replace '[0-9]*\. ', '' 59 | If ($ClearStepNumbers) { 60 | $Node.name = $newname 61 | } else { 62 | $Node.name = Set-MaxLength -Str "$($StepCounter)-$($StepCounter + $SubTSFinalStepNumber + 1). $newname" -Length 50 63 | } 64 | $StepCounter = $StepCounter + $SubTSFinalStepNumber + 1 65 | $TSStep = New-Object -TypeName psobject -Property @{'StepNumber' = $StepCounter; 'GroupName' = "$GroupName"; 'StepName' = "$($node.Name)" } 66 | } 67 | else { 68 | $newname = "$($Node.name)" -replace '[0-9]*\-[0-9]*\. ', '' 69 | $newname = "$newname" -replace '[0-9]*\. ', '' 70 | If ($ClearStepNumbers) { 71 | $Node.name = $newname 72 | } else { 73 | $Node.name = Set-MaxLength -Str "$($StepCounter)-$($StepCounter + $SubTSFinalStepNumber + 1). $newname" -Length 50 74 | } 75 | $StepCounter = $StepCounter + $SubTSFinalStepNumber + 1 76 | $TSStep = New-Object -TypeName psobject -Property @{'StepNumber' = $StepCounter; 'GroupName' = "N/A"; 'StepName' = "$($node.Name)" } 77 | } 78 | $TSStep 79 | } 80 | else { 81 | $StepCounter-- 82 | $Node.name = $("$($Node.name)" -replace '[0-9]*\-[0-9]*\. ', '') -replace '[0-9]*\. ', '' 83 | } 84 | } 85 | else { 86 | $StepCounter-- 87 | $Node.name = $("$($Node.name)" -replace '[0-9]*\-[0-9]*\. ', '') -replace '[0-9]*\. ', '' 88 | } 89 | } 90 | 'group' { 91 | $StepCounter++ 92 | if (($StepCounter -eq 1) -and ($FirstStep -ne 0)){$StepCounter = 0;$FirstStep = 0} 93 | Write-Verbose "$StepCounter --- GROUP --- $($Node.Name) --- Start" 94 | $newname = "$($Node.name)" -replace '[0-9]*\-[0-9]*\. ', '' 95 | $newname = "$newname" -replace '[0-9]*\. ', '' 96 | $GroupFirstStep = $StepCounter 97 | $TSStep = New-Object -TypeName psobject -Property @{'StepNumber' = $StepCounter; 'GroupName' = "$($node.Name)"; 'StepName' = "N/A" } 98 | #$TSStep 99 | $NextSteps = Set-PWSequenceStepNumbers -Sequence $node -GroupName "$($node.Name)" -StepCounter $StepCounter 100 | If ($NextSteps) { 101 | foreach ($NextStep in $NextSteps) { Write-Verbose $NextStep.StepNumber } 102 | $StepCounter = ($NextSteps.StepNumber | Measure-Object -Maximum).Maximum 103 | } 104 | Write-Verbose "$StepCounter --- GROUP --- $($Node.Name) --- BeforeINC" 105 | $StepCounter++ 106 | If ($ClearStepNumbers) { 107 | $Node.name = $newname 108 | } else { 109 | $Node.name = Set-MaxLength -Str "$($GroupFirstStep)-$($StepCounter). $newname" -Length 50 110 | } 111 | Write-Verbose "$StepCounter --- GROUP --- $($Node.Name) --- END" 112 | $TSStep = New-Object -TypeName psobject -Property @{'StepNumber' = $StepCounter; 'GroupName' = "$($node.Name)"; 'StepName' = "N/A" } 113 | $TSStep 114 | } 115 | 'sequence' { 116 | if ($StepCounter -ne 0) { $StepCounter++ } 117 | Write-Verbose "$StepCounter --- SEQU --- $($Node.Name)" 118 | $NextSteps = Set-PWSequenceStepNumbers -Sequence $node -GroupName "$($node.Name)" -StepCounter $StepCounter 119 | $NextSteps 120 | if ($StepCounter -ne 0) { 121 | $StepCounter = ($NextSteps.StepNumber | Measure-Object -Maximum).Maximum 122 | $StepCounter++ 123 | } 124 | Write-Verbose "$StepCounter --- SEQU --- $($Node.Name) --- END" 125 | } 126 | default { } 127 | } 128 | } 129 | Write-Verbose "Ending Run: $StepCounter" 130 | } 131 | 132 | function Set-MaxLength { 133 | param ( 134 | [parameter(Mandatory = $True, ValueFromPipeline = $True)] 135 | [string] $Str, 136 | [parameter(Mandatory = $True, Position = 1)] 137 | [int] $Length 138 | ) 139 | $Str[0..($Length - 1)] -join "" 140 | } 141 | 142 | Function Get-PWTSXml { 143 | [CmdletBinding()] 144 | param ( 145 | [Parameter(Mandatory = $true)] 146 | [ValidateNotNullOrEmpty()] 147 | [string]$TSPackageID, 148 | [Parameter(Mandatory = $false)] 149 | [ValidateNotNullOrEmpty()] 150 | [string]$SiteServer = 'localhost', 151 | [Parameter(Mandatory = $false)] 152 | [ValidateNotNullOrEmpty()] 153 | [string]$SiteCode = (New-Object -ComObject Microsoft.SMS.Client -Strict).GetAssignedSite() 154 | ) 155 | 156 | # Get SMS_TaskSequencePackage WMI object 157 | $TaskSequencePackage = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Filter "PackageID like `'$TSPackageID`'" 158 | $TaskSequencePackage.Get() 159 | # Get SMS_TaskSequence WMI object from TaskSequencePackage 160 | $TaskSequence = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "GetSequence" -ArgumentList $TaskSequencePackage 161 | # Convert WMI object to XML 162 | $TaskSequenceResult = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequence -ComputerName $SiteServer -Name "SaveToXml" -ArgumentList $TaskSequence.TaskSequence 163 | $TaskSequenceXML = $TaskSequenceResult.ReturnValue 164 | [xml]$TaskSequenceXML 165 | } 166 | 167 | Function Set-PWTSXml { 168 | [CmdletBinding()] 169 | param ( 170 | [Parameter(Mandatory = $true)] 171 | [ValidateNotNullOrEmpty()] 172 | [string]$TSPackageID, 173 | [Parameter(Mandatory = $true)] 174 | [ValidateNotNullOrEmpty()] 175 | [xml]$TSXml, 176 | [Parameter(Mandatory = $false)] 177 | [ValidateNotNullOrEmpty()] 178 | [string]$SiteServer = 'localhost', 179 | [Parameter(Mandatory = $false)] 180 | [ValidateNotNullOrEmpty()] 181 | [string]$SiteCode = (New-Object -ComObject Microsoft.SMS.Client -Strict).GetAssignedSite() 182 | ) 183 | # Get SMS_TaskSequencePackage WMI object 184 | $TaskSequencePackage = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Filter "PackageID like `'$TSPackageID`'" 185 | $TaskSequencePackage.Get() 186 | # Convert XML back to SMS_TaskSequencePackage WMI object 187 | $XMLString = $TSXml.OuterXml 188 | Write-Verbose "Invoke-WmiMethod -Namespace `"root\SMS\site_$($SiteCode)`" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name `"ImportSequence`" -ArgumentList `"$XMLString`"" 189 | $TaskSequenceResult = Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "ImportSequence" -ArgumentList "$XMLString" 190 | 191 | # Update SMS_TaskSequencePackage WMI object 192 | Write-Verbose "Invoke-WmiMethod -Namespace `"root\SMS\site_$($SiteCode)`" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name `"SetSequence`" -ArgumentList @($($TaskSequenceResult.TaskSequence), $TaskSequencePackage)" 193 | Invoke-WmiMethod -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name "SetSequence" -ArgumentList @($TaskSequenceResult.TaskSequence, $TaskSequencePackage) 194 | } 195 | 196 | Function Get-IsParentGroupDisabled { 197 | [CmdletBinding()] 198 | param ( 199 | [Parameter(Mandatory = $true)] 200 | [ValidateNotNullOrEmpty()] 201 | $TSXml 202 | ) 203 | Write-Verbose "Checking the elements parent: $($tsxml.Name)" 204 | IF ($null -ne $tsxml.ParentNode) { 205 | Write-Verbose "Is disabled: $($tsxml.ParentNode.Disable)" 206 | If ($tsxml.ParentNode.Disable -eq "true") { 207 | Return $true 208 | } 209 | else { 210 | Write-Verbose "Checking into this parent: $($tsxml.ParentNode.Name)" 211 | Get-IsParentGroupDisabled -TSXml $tsxml.ParentNode 212 | } 213 | } 214 | } 215 | 216 | $Sequence = Get-PWTSXml -TSPackageID $PackageID -SiteCode $SiteCode -SiteServer $PrimarySiteServer 217 | $NewTaskSequenceXML = Set-PWSequenceStepNumbers -Sequence $Sequence 218 | Set-PWTSXml -TSPackageID $PackageID -TSXml $Sequence -SiteCode $SiteCode -SiteServer $PrimarySiteServer 219 | -------------------------------------------------------------------------------- /NumberTaskSequenceSteps/readme.md: -------------------------------------------------------------------------------- 1 | Please refer to the blog post here for details on the scripts in this directory: 2 | * https://wetterssource.com/number-your-task-sequence-steps 3 | 4 | What it does: 5 | ![Numbers Task sequence Steps](https://wetterssource.com/sites/default/files/inline-images/NumberTSActionShot.gif "Numbers Task Sequence Steps") 6 | -------------------------------------------------------------------------------- /Pestering/Invoke-PwPester.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PwPester { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true)] 5 | [Alias("Path")] 6 | [string] 7 | $Script, 8 | [Parameter(Mandatory=$false)] 9 | [string] 10 | $CodeCoverage, 11 | [Parameter(Mandatory=$false)] 12 | [int] 13 | $Version = 5 14 | ) 15 | try { 16 | [System.IO.FileInfo]$Script = Convert-Path $Script -ErrorAction Stop 17 | } 18 | catch { 19 | throw "Unable to resolve test script path $Script" 20 | } 21 | $InvokePesterSplat = @{} 22 | $PesterConfig = New-PesterConfiguration 23 | $PesterConfig.Run.Path = $Script.FullName 24 | $PesterConfig.Output.Verbosity = 'Detailed' 25 | if ($PSBoundParameters.ContainsKey('CodeCoverage')) { 26 | $PesterConfig.CodeCoverage.Enabled = $true 27 | try { 28 | [System.IO.FileInfo]$CodeCoverage = Convert-Path $CodeCoverage -ErrorAction Stop 29 | } 30 | catch { 31 | Write-Warning "Unable to resolve script path for code coverage $CodeCoverage" 32 | $PesterConfig.CodeCoverage.Enabled = $false 33 | } 34 | $PesterConfig.CodeCoverage.CoveragePercentTarget = 80 35 | $PesterConfig.CodeCoverage.RecursePaths = $false 36 | $PesterConfig.CodeCoverage.OutputPath = "$($Env:TEMP)\coverage.xml" 37 | $PesterConfig.CodeCoverage.Path = $CodeCoverage.FullName 38 | } 39 | $InvokePesterSplat.Add('Configuration', $PesterConfig) 40 | Invoke-Pester @InvokePesterSplat 41 | } -------------------------------------------------------------------------------- /Pestering/MakeBreakfast.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [Parameter(Mandatory=$true)] 4 | [string[]] 5 | $Meat, 6 | [Parameter(Mandatory=$true)] 7 | [string] 8 | $Beverage, 9 | [Parameter(Mandatory=$false)] 10 | [string[]] 11 | $Eggs, 12 | [Parameter(Mandatory=$false)] 13 | [string] 14 | $Toast, 15 | [Parameter(Mandatory=$false)] 16 | [string] 17 | $Pastry, 18 | [Parameter(Mandatory=$false)] 19 | [int] 20 | $Servings = 1 21 | ) 22 | 23 | Import-Module .\BreakfastModule\Breakfast.psd1 24 | 25 | for ($i = 0; $i -lt $Servings; $i++) { 26 | Add-pwBeverage -Name $Beverage 27 | foreach ($BreakfastMeat in $Meat) { 28 | Add-pwBreakfastMeat -Name $BreakfastMeat 29 | } 30 | if ($PSBoundParameters.ContainsKey('Eggs')) { 31 | foreach ($Egg in $Eggs){ 32 | Add-pwEggs -Name $Egg 33 | } 34 | } 35 | if ($PSBoundParameters.ContainsKey('Toast') -and $PSBoundParameters.ContainsKey('Pastry')) { 36 | Write-Warning "You're overloading on carbs. We're only giving you a pastry as toast is just boring." 37 | Add-pwPastry -Name $Pastry 38 | if ($Pastry -like 'Apple Fritter') { 39 | Write-Output "You wanted toast and an Apple Fritter. I know we said no overloading on carbs, but you do want the worlds greatest pastery. So, how about 2 apple fritters." 40 | Add-pwPastry -Name 'Apple Fritter' 41 | } 42 | } else { 43 | If ($PSBoundParameters.ContainsKey('Pastry')){ 44 | Add-pwPastry -Name $Pastry 45 | } 46 | If ($PSBoundParameters.ContainsKey('Toast')){ 47 | Add-pwToast -Name $Toast 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Pestering/MakeBreakfast.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Mock -CommandName Add-pwBeverage -Verifiable 3 | Mock -CommandName Add-pwBreakfastMeat -Verifiable 4 | Mock -CommandName Add-pwEggs 5 | Mock -CommandName Add-pwPastry 6 | Mock -CommandName Add-pwToast 7 | } 8 | Describe -Name "Checking various breakfast orders with MakeBreakfast.ps1" -Fixture { 9 | Context -Name "When I order Coffee, Sausage and Toast" -Fixture { 10 | BeforeEach -Scriptblock { 11 | {C:\Users\pwett\Documents\GitHub\WettersSource\Pestering\MakeBreakfast.ps1 -Meat Sausage -Beverage Coffee -Toast Wheat} | Should -Not -Throw 12 | } 13 | It -Name "Should Get a beverage" -Test { 14 | Should -Invoke Add-pwBeverage -Times 1 -Exactly 15 | } 16 | It -Name "Should add sausages to the meal" -Test { 17 | Should -Invoke Add-pwBreakfastMeat -Times 1 -Exactly 18 | } 19 | It -Name "Should add toast to the meal" -Test { 20 | Should -Invoke Add-pwToast -Times 1 -Exactly 21 | } 22 | } 23 | Context -Name "When I order OJ and Sausage" -Fixture { 24 | BeforeEach -Scriptblock { 25 | {C:\Users\pwett\Documents\GitHub\WettersSource\Pestering\MakeBreakfast.ps1 -Meat Sausage,Bacon -Beverage Coffee -Toast Wheat} | Should -Not -Throw 26 | } 27 | It -Name "Should Get a beverage and meat" -Test { 28 | Should -InvokeVerifiable 29 | } 30 | It -Name "Should Add 2 meats" -Test { 31 | Should -Invoke -CommandName Add-pwBreakfastMeat -Times 2 -Exactly 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Convert-pwBmpToPixels.ps1: -------------------------------------------------------------------------------- 1 | function Convert-pwBmpToPixels { 2 | [OutputType([System.Collections.Hashtable])] 3 | [CmdletBinding(DefaultParameterSetName = 'Bitmap')] 4 | param ( 5 | [Parameter(Mandatory = $true, 6 | ParameterSetName = 'File')] 7 | $File, 8 | [Parameter(Mandatory = $true, 9 | ParameterSetName = 'Bitmap')] 10 | [System.Drawing.Bitmap] 11 | $Bitmap 12 | ) 13 | Write-Verbose "converting bmp to pixels" 14 | if ($PSBoundParameters.Keys.Contains('File')){ 15 | $bmp = Get-pwBitmap -File $File 16 | } else { 17 | $bmp = $Bitmap 18 | } 19 | $width = $bmp.Width 20 | $height = $bmp.Height 21 | $pixels = @{} 22 | for ($x = 0; $x -lt $width; $x++) { 23 | for ($y = 0; $y -lt $height; $y++) { 24 | $pix = $bmp.GetPixel($x,$y) 25 | $pixels.Add("$x,$y","$($pix.R),$($pix.G),$($pix.B)") 26 | } 27 | } 28 | $pixels 29 | } 30 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Convert-pwPixelstoBmp.ps1: -------------------------------------------------------------------------------- 1 | function Convert-pwPixelstoBmp{ 2 | [OutputType([System.Drawing.Bitmap])] 3 | [CmdletBinding()] 4 | param ( 5 | $Pixels 6 | ) 7 | $Dimensions = Get-pwPixelMax -Pixels $Pixels 8 | Write-Verbose "Size is $($Dimensions.xMax) x $($Dimensions.yMax)" 9 | $bmp = New-pwEmptyBitmap -Width $Dimensions.xMax -Height $Dimensions.yMax 10 | Write-Verbose "Creating BMP of $($bmp.Height) x $($bmp.Width)" 11 | $bmp = Set-pwBmpPixels -Pixels $Pixels -Bitmap $bmp 12 | $bmp 13 | } 14 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Copy-pwBitmapImage.ps1: -------------------------------------------------------------------------------- 1 | function Copy-pwBitmapImage { 2 | [OutputType([System.Drawing.Bitmap])] 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory = $true)] 6 | [string] 7 | $File 8 | ) 9 | $ExistingBmp = Get-pwBitmap -File $File 10 | $bmp = [System.Drawing.Bitmap]::new($ExistingBmp) 11 | $ExistingBmp.Dispose() 12 | [system.gc]::Collect() 13 | return $bmp 14 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Get-pwBitmap.ps1: -------------------------------------------------------------------------------- 1 | function Get-pwBitmap { 2 | [OutputType([System.Drawing.Bitmap])] 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory = $true)] 6 | $File 7 | ) 8 | [System.Drawing.Bitmap]::FromFile("$File") 9 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Get-pwPixelMax.ps1: -------------------------------------------------------------------------------- 1 | function Get-pwPixelMax { 2 | [OutputType([System.Collections.Hashtable])] 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory = $true)] 6 | [System.Collections.Hashtable] 7 | $Pixels 8 | ) 9 | [int]$xMax = 0 10 | [int]$yMax = 0 11 | foreach ($pix in $Pixels.GetEnumerator()){ 12 | [int]$x = $pix.Key.split(',')[0] 13 | [int]$y = $pix.Key.split(',')[1] 14 | if ($x -gt $xMax) {$xMax = $x} 15 | if ($y -gt $yMax) {$yMax = $y} 16 | } 17 | $xMax = $xMax + 1 18 | $yMax = $yMax + 1 19 | Write-Verbose "Get-pwPixelMax found size is $xMax x $yMax" 20 | return @{ 21 | xMax = $xMax 22 | yMax = $yMax 23 | } 24 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Move-pwPixels.ps1: -------------------------------------------------------------------------------- 1 | function Move-pwPixels { 2 | [OutputType([System.Collections.Hashtable])] 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory = $true)] 6 | [System.Collections.Hashtable] 7 | $Pixels, 8 | [Parameter(Mandatory = $false)] 9 | [int] 10 | $MoveX, 11 | [Parameter(Mandatory = $false)] 12 | [int] 13 | $MoveY 14 | ) 15 | $newPixels = @{} 16 | foreach ($Pix in $Pixels.GetEnumerator()){ 17 | $xy = $pix.Key 18 | [int]$x = $xy.split(',')[0] 19 | [int]$y = $xy.split(',')[1] 20 | $x = $x + $MoveX 21 | $y = $y + $MoveY 22 | $newPixels.Add("$x,$y", $Pix.Value) 23 | } 24 | return $newPixels 25 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/New-pwBmpFileFromPixels.ps1: -------------------------------------------------------------------------------- 1 | function New-pwBmpFileFromPixels { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | $Pixels, 6 | [Parameter(Mandatory = $true)] 7 | $File 8 | ) 9 | $bmp = Convert-pwPixelstoBmp -Pixels $Pixels 10 | $bmp.Save("$File") 11 | } 12 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/New-pwEmptyBitmap.ps1: -------------------------------------------------------------------------------- 1 | function New-pwEmptyBitmap { 2 | [OutputType([System.Drawing.Bitmap])] 3 | [CmdletBinding()] 4 | param ( 5 | [int] 6 | $Width, 7 | [int] 8 | $Height 9 | ) 10 | [System.Drawing.Bitmap]::new($Width, $Height) 11 | } 12 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Save-pwBitmapFile.ps1: -------------------------------------------------------------------------------- 1 | function Save-pwBitmapFile { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [System.Drawing.Bitmap] 6 | $Bitmap, 7 | [Parameter(Mandatory = $true)] 8 | [string] 9 | $File 10 | ) 11 | $Bitmap.Save("$File") 12 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Set-pwBmpPixels.ps1: -------------------------------------------------------------------------------- 1 | function Set-pwBmpPixels { 2 | [OutputType([System.Drawing.Bitmap])] 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory = $true)] 6 | [System.Collections.Hashtable] 7 | $Pixels, 8 | [Parameter(Mandatory = $true)] 9 | [System.Drawing.Bitmap] 10 | $Bitmap 11 | ) 12 | foreach ($Pix in $Pixels.GetEnumerator()){ 13 | $xy = $pix.Key 14 | [int]$x = $xy.split(',')[0] 15 | [int]$y = $xy.split(',')[1] 16 | $rgb = $pix.Value 17 | [int]$R = $rgb.split(',')[0] 18 | [int]$G = $rgb.split(',')[1] 19 | [int]$B = $rgb.split(',')[2] 20 | $color = [System.Drawing.Color]::FromArgb($R, $G, $B) 21 | Write-debug "Setting Pixel $x, $y" 22 | $Bitmap.SetPixel($x, $y, $color) 23 | } 24 | return $Bitmap 25 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Set-pwBmpRotate.ps1: -------------------------------------------------------------------------------- 1 | function Set-pwBmpRotate { 2 | [OutputType([System.Drawing.Bitmap])] 3 | [CmdletBinding()] 4 | param ( 5 | [System.Drawing.Bitmap] 6 | $Bitmap, 7 | [ValidateSet(90,180,270)] 8 | [int]$Rotate = 90 9 | ) 10 | switch ($Rotate) { 11 | 90 { $Bitmap.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipNone) } 12 | 180 { $Bitmap.RotateFlip([System.Drawing.RotateFlipType]::Rotate180FlipNone) } 13 | 270 { $Bitmap.RotateFlip([System.Drawing.RotateFlipType]::Rotate270FlipNone) } 14 | } 15 | $Bitmap 16 | } 17 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Set-pwPixelRotate.ps1: -------------------------------------------------------------------------------- 1 | function Set-pwPixelRotate { 2 | [OutputType([System.Collections.Hashtable])] 3 | [CmdletBinding()] 4 | param ( 5 | $Pixels, 6 | [ValidateSet(90,180,270)] 7 | [int]$Rotate = 90 8 | ) 9 | $bmp = Convert-pwPixelstoBmp -Pixels $Pixels 10 | $RotatedBmp = Set-pwBmpRotate -Bitmap $bmp -Rotate $Rotate 11 | Convert-pwBmpToPixels -Bitmap $RotatedBmp 12 | } 13 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Commands/Update-pwBmpFileFromPixels.ps1: -------------------------------------------------------------------------------- 1 | function Update-pwBmpFileFromPixels { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [System.Collections.Hashtable] 6 | $NewPixels, 7 | [Parameter(Mandatory = $true)] 8 | [string] 9 | $File, 10 | [Parameter(Mandatory = $false)] 11 | [int] 12 | $Startx, 13 | [Parameter(Mandatory = $false)] 14 | [int] 15 | $Starty 16 | ) 17 | $bmp = Copy-pwBitmapImage -File $File 18 | $MaxPixels = Get-pwPixelMax -Pixels $NewPixels 19 | Write-Verbose "Size is $($MaxPixels.xMax) x $($MaxPixels.yMax)" 20 | $Pixels = Move-pwPixels -Pixels $NewPixels -MoveX $Startx -MoveY $Starty 21 | Write-Verbose "Creating BMP of $($bmp.Height) x $($bmp.Width)" 22 | $bmp = Set-pwBmpPixels -Pixels $Pixels -Bitmap $bmp 23 | Save-pwBitmapFile -Bitmap $bmp -File $File 24 | } 25 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Convert-pwBmpToPixels.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | 4 | $bmp = [System.Drawing.Bitmap]::new(2, 2) 5 | $bmp.SetPixel(0,0,[System.Drawing.Color]::FromArgb(255, 255, 255)) 6 | $bmp.SetPixel(1,1,[System.Drawing.Color]::FromArgb(255, 255, 254)) 7 | 8 | } 9 | Describe -Name 'Convert-pwBmpToPixels' -Fixture { 10 | 11 | BeforeAll -Scriptblock { 12 | InModuleScope -ModuleName 'pwBitmapper' -ScriptBlock { 13 | Mock -CommandName 'Get-pwBitmap' -MockWith { 14 | $bmp = [System.Drawing.Bitmap]::new(2, 2) 15 | $bmp.SetPixel(0,0,[System.Drawing.Color]::FromArgb(255, 255, 255)) 16 | $bmp.SetPixel(1,1,[System.Drawing.Color]::FromArgb(255, 255, 254)) 17 | return $bmp 18 | } -Verifiable 19 | } 20 | } 21 | Context -Name 'When converting a bmp file to pixel hashtable' -Fixture { 22 | It -Name 'Should not Throw' -Test { 23 | {Convert-pwBmpToPixels -File "${ENV:TEMP}\test.bmp"}| Should -Not -Throw 24 | } 25 | It -Name 'Should open a file' -Test { 26 | Convert-pwBmpToPixels -File "${ENV:TEMP}\test.bmp" 27 | Should -Invoke -CommandName 'Get-pwBitmap' -Times 1 -Exactly -ModuleName 'pwBitmapper' 28 | } 29 | It -Name 'Should return a hashtable with correct values' -Test { 30 | $Return = Convert-pwBmpToPixels -File "${ENV:TEMP}\test.bmp" 31 | $Return."0,0" | Should -Be '255,255,255' 32 | $Return."1,1" | Should -Be '255,255,254' 33 | } 34 | } 35 | Context -Name 'When converting a bmp object to pixel hashtable' -Fixture { 36 | It -Name 'Should not Throw' -Test { 37 | {Convert-pwBmpToPixels -Bitmap $bmp} | Should -Not -Throw 38 | } 39 | It -Name 'Should not open a file' -Test { 40 | Convert-pwBmpToPixels -Bitmap $bmp 41 | Should -Invoke -CommandName 'Get-pwBitmap' -Times 0 -Exactly -ModuleName 'pwBitmapper' 42 | } 43 | It -Name 'Should return a hashtable with correct values' -Test { 44 | $Return = Convert-pwBmpToPixels -Bitmap $bmp 45 | $Return."0,0" | Should -Be '255,255,255' 46 | $Return."1,1" | Should -Be '255,255,254' 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Convert-pwPixelstoBmp.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | 4 | $pixels = @{ 5 | '0,0' = '255,255,255' 6 | '0,1' = '255,255,255' 7 | '1,0' = '255,255,255' 8 | '1,1' = '255,255,254' 9 | } 10 | } 11 | Describe -Name 'Convert-pwPixelstoBmp' -Fixture { 12 | BeforeAll -Scriptblock { 13 | InModuleScope -ModuleName 'pwBitmapper' -ScriptBlock { 14 | Mock -CommandName 'Get-pwPixelMax' -MockWith {@{xMax = 2; yMax = 2}} -Verifiable 15 | Mock -CommandName 'New-pwEmptyBitmap' -MockWith { 16 | [System.Drawing.Bitmap]::new(2, 2) 17 | } -Verifiable 18 | Mock -CommandName 'Set-pwBmpPixels' -MockWith { 19 | $bmp = [System.Drawing.Bitmap]::new(2, 2) 20 | $bmp.SetPixel(0,0,[System.Drawing.Color]::FromArgb(255, 255, 255)) 21 | $bmp.SetPixel(1,1,[System.Drawing.Color]::FromArgb(255, 255, 254)) 22 | return $bmp 23 | } -Verifiable 24 | } 25 | } 26 | Context -Name 'When converting a bmp file to pixel hashtable' -Fixture { 27 | It -Name 'Should not Throw' -Test { 28 | {Convert-pwPixelstoBmp -Pixels $pixels}| Should -Not -Throw 29 | } 30 | It -Name 'Should return a Bitmap Object with correct pixels' -Test { 31 | $Return = Convert-pwPixelstoBmp -Pixels $pixels 32 | $Return | Should -BeOfType 'System.Drawing.Bitmap' 33 | $Return.GetPixel(0,0).b | Should -Be '255' 34 | $Return.GetPixel(1,1).b | Should -Be '254' 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Copy-pwBitmapImage.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | } 4 | Describe -Name 'Copy-pwBitmapImage' -Fixture { 5 | BeforeAll -Scriptblock { 6 | InModuleScope -ModuleName 'pwBitmapper' -ScriptBlock { 7 | Mock -CommandName 'Get-pwBitmap' -MockWith { 8 | [System.Drawing.Bitmap]::new(2, 2) 9 | } -Verifiable 10 | } 11 | } 12 | Context -Name 'When copying a bitmap object' -Fixture { 13 | It -Name 'Should not Throw' -Test { 14 | {Copy-pwBitmapImage -File "${ENV:TEMP}\test.bmp"}| Should -Not -Throw 15 | } 16 | It -Name 'Should return a Bitmap Object of same size' -Test { 17 | $Return = Copy-pwBitmapImage -File "${ENV:TEMP}\test.bmp" 18 | $Return | Should -BeOfType 'System.Drawing.Bitmap' 19 | $Return.Height | Should -Be 2 20 | $Return.Width | Should -Be 2 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Get-pwBitmap.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | } 4 | Describe -Name "Get-pwBitmap" -Fixture { 5 | Context -Name "When getting file" -Fixture { 6 | BeforeAll -Scriptblock { 7 | $RandomName = '{0}\{1}.bmp' -f $env:Temp, (New-Guid).Guid 8 | $bmp = [System.Drawing.Bitmap]::new(2, 2) 9 | $bmp.SetPixel(0,0,[System.Drawing.Color]::FromArgb(255, 255, 255)) 10 | $bmp.SetPixel(1,1,[System.Drawing.Color]::FromArgb(255, 255, 254)) 11 | $bmp.Save("$RandomName") 12 | } 13 | It -Name "Should not throw" -Test { 14 | {Get-pwBitmap -File $RandomName} | Should -Not -Throw 15 | } 16 | It -Name "Should return a bitmap object" -Test { 17 | $Return = Get-pwBitmap -File $RandomName 18 | $Return | Should -BeOfType 'System.Drawing.Bitmap' 19 | } 20 | AfterAll -Scriptblock { 21 | Remove-Item -Path $RandomName -Force -ErrorAction SilentlyContinue 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Get-pwPixelMax.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | } 4 | Describe -Name "Get-pwPixelMax" -Fixture { 5 | Context -Name "When looking at a 2 by 2 pixel set" -Fixture { 6 | BeforeAll -Scriptblock { 7 | $Pixels = @{ 8 | '0,0' = '255,255,255' 9 | '0,1' = '255,255,255' 10 | '1,0' = '255,255,255' 11 | '1,1' = '255,255,254' 12 | } 13 | } 14 | It -Name 'Should not throw' -Test { 15 | {Get-pwPixelMax -Pixels $Pixels} | Should -Not -Throw 16 | } 17 | It -Name 'Should have a Max X of 2' -Test { 18 | (Get-pwPixelMax -Pixels $Pixels).xMax |Should -Be 2 19 | } 20 | It -Name 'Should have a Max Y of 2' -Test { 21 | (Get-pwPixelMax -Pixels $Pixels).yMax |Should -Be 2 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Move-pwPixels.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | } 4 | Describe -Name "Move-pwPixels" -Fixture { 5 | BeforeAll -Scriptblock { 6 | $Pixels = @{ 7 | '0,0' = '255,255,255' 8 | '0,1' = '255,255,255' 9 | '1,0' = '255,255,255' 10 | '1,1' = '255,255,254' 11 | } 12 | } 13 | Context -Name "When looking at a 2 by 2 pixel set" -Fixture { 14 | It -Name 'Should not throw' -Test { 15 | { Move-pwPixels -Pixels $Pixels } | Should -Not -Throw 16 | } 17 | } 18 | Context -Name "When moving at a 2 by 2 pixel set 2 pixels in X and Y direction" -Fixture { 19 | It -Name 'Should shift the pixels in the hashset by 2 positions' -Test { 20 | $Result = Move-pwPixels -Pixels $Pixels -MoveX 2 -MoveY 2 21 | $Result.'2,2' | Should -Be '255,255,255' 22 | $Result.'3,3' | Should -Be '255,255,254' 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/New-pwBmpFileFromPixels.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | } 4 | Describe -Name 'New-pwBmpFileFromPixels' -Fixture { 5 | BeforeAll -Scriptblock { 6 | $pixels = @{ 7 | '0,0' = '255,255,255' 8 | '0,1' = '255,255,255' 9 | '1,0' = '255,255,255' 10 | '1,1' = '255,255,255' 11 | } 12 | InModuleScope -ModuleName 'pwBitmapper' -ScriptBlock { 13 | Mock -CommandName 'Convert-pwPixelstoBmp' -MockWith { 14 | return [System.Drawing.Bitmap]::new(2, 2) 15 | } -Verifiable 16 | } 17 | } 18 | Context -Name 'When given an array of pixels and path' -Fixture { 19 | BeforeEach -Scriptblock { 20 | New-pwBmpFileFromPixels -Pixels $Pixels -File "${ENV:TEMP}\test.bmp" 21 | } 22 | It -Name 'Should not throw' -Test { 23 | {New-pwBmpFileFromPixels -Pixels $Pixels -File "${ENV:TEMP}\test.bmp"} | Should -Not -Throw 24 | } 25 | It -Name 'Should create the bitmap file' -Test { 26 | Should -InvokeVerifiable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/New-pwEmptyBitmap.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | } 4 | Describe -Name 'New-pwEmptyBitmap' -Fixture { 5 | Context -Name 'When given dimensions of 10 by 10' -Fixture { 6 | It -Name 'Should not throw' -Test { 7 | {New-pwEmptyBitmap -Width 10 -Height 10} | Should -Not -Throw 8 | } 9 | It -Name 'Should create a 10x10 bitmap object' -Test { 10 | $bitmap = New-pwEmptyBitmap -Width 10 -Height 10 11 | $bitmap | Should -BeOfType 'System.Drawing.Bitmap' 12 | $bitmap.Width | Should -Be 10 13 | $bitmap.Height | Should -Be 10 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Save-pwBitmapFile.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 3 | } 4 | Describe -Name 'Save-pwBitmapFile' -Fixture { 5 | BeforeAll -Scriptblock { 6 | $RandomName = '{0}\{1}.bmp' -f $env:Temp, (New-Guid).Guid 7 | $bmp = [System.Drawing.Bitmap]::new(2, 2) 8 | $bmp.SetPixel(0,0,[System.Drawing.Color]::FromArgb(255, 255, 255)) 9 | $bmp.SetPixel(1,1,[System.Drawing.Color]::FromArgb(255, 255, 254)) 10 | } 11 | Context -Name 'When given a bitmap object to save to file' -Fixture { 12 | It -Name 'Should not throw' -Test { 13 | {Save-pwBitmapFile -Bitmap $bmp -File $RandomName} | Should -Not -Throw 14 | } 15 | It -Name 'Should have saved to a file' -Test { 16 | Test-Path -Path $RandomName | Should -BeTrue 17 | } 18 | } 19 | AfterAll -Scriptblock { 20 | Remove-Item -Path $RandomName -Force 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Set-pwBmpPixels.tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | BeforeAll -Scriptblock { 4 | Import-Module "$PSScriptRoot..\..\pwBitmapper" -Force 5 | } 6 | 7 | Describe -Name 'Set-pwBmpPixels' -Fixture { 8 | BeforeAll -Scriptblock { 9 | $pixels = @{ 10 | '0,0' = '255,255,251' 11 | '0,1' = '255,255,252' 12 | '1,0' = '255,255,253' 13 | '1,1' = '255,255,254' 14 | } 15 | $bmp = [System.Drawing.Bitmap]::new(2, 2) 16 | 17 | } 18 | Context -Name 'When given a 2x2 pixel array' -Fixture { 19 | It -Name 'Should not throw' -Test { 20 | {Set-pwBmpPixels -Pixels $pixels -Bitmap $bmp} | Should -Not -Throw 21 | } 22 | It -Name 'Should return a 2x2 bitmap' -Test { 23 | $result = Set-pwBmpPixels -Pixels $pixels -Bitmap $bmp 24 | $result | Should -BeOfType 'System.Drawing.Bitmap' 25 | $result.Height | Should -Be 2 26 | $result.Width | Should -Be 2 27 | } 28 | It -Name 'Should set colors that match the pixel input' -Test { 29 | $result = Set-pwBmpPixels -Pixels $pixels -Bitmap $bmp 30 | $result.GetPixel(0,0).B | Should -Be 251 31 | $result.GetPixel(0,1).B | Should -Be 252 32 | $result.GetPixel(1,0).B | Should -Be 253 33 | $result.GetPixel(1,1).B | Should -Be 254 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Set-pwBmpRotate.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module ..\..\pwBitmapper -Force 3 | } 4 | Describe -Name 'Set-pwBmpRotate' -Fixture { 5 | BeforeAll -Scriptblock { 6 | #$obj = New-MockObject -Type 'System.Drawing.Image' 7 | $bmp = New-MockObject -InputObject $([System.Drawing.Bitmap]::new(10, 10)) 8 | } 9 | $Tests = @( 10 | @{Rotate = 90} 11 | @{Rotate = 180} 12 | @{Rotate = 270} 13 | ) 14 | Context -Name 'Rotate Image degrees' -ForEach $Tests -Fixture { 15 | It -Name 'Should not throw' -Test { 16 | {Set-pwBmpRotate -Bitmap $bmp -Rotate $Rotate} | Should -Not -Throw 17 | } 18 | It -Name 'Should return a Bitmap' -Test { 19 | $Return = Set-pwBmpRotate -Bitmap $bmp -Rotate $Rotate 20 | $Return | Should -BeOfType 'System.Drawing.Bitmap' 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Set-pwPixelRotate.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module ..\..\pwBitmapper -Force 3 | } 4 | Describe -Name 'Set-pwPixelRotate' -Fixture { 5 | BeforeAll -Scriptblock { 6 | $pixels = @{ 7 | '0,0' = '255,255,255' 8 | '0,1' = '255,255,255' 9 | '1,0' = '255,255,255' 10 | '1,1' = '255,255,255' 11 | } 12 | InModuleScope -ModuleName 'pwBitmapper' -ScriptBlock { 13 | Mock -CommandName 'Convert-pwPixelstoBmp' -MockWith { 14 | return [System.Drawing.Bitmap]::new(2, 2) 15 | } -Verifiable 16 | Mock -CommandName 'Set-pwBmpRotate' -MockWith { 17 | return [System.Drawing.Bitmap]::new(2, 2) 18 | } -Verifiable 19 | Mock -CommandName 'Convert-pwBmpToPixels' -MockWith { 20 | return @{ 21 | '0,0' = '255,255,255' 22 | '0,1' = '255,255,255' 23 | '1,0' = '255,255,255' 24 | '1,1' = '255,255,255' 25 | } 26 | } -Verifiable 27 | } 28 | } 29 | Context -Name 'Rotate Image 90 degrees' -Fixture { 30 | BeforeEach -Scriptblock { 31 | $newPixels = Set-pwPixelRotate -Pixels $Pixels -Rotate 90 32 | } 33 | It -Name 'Should not throw' -Test { 34 | {Set-pwPixelRotate -Pixels $Pixels -Rotate 90} | Should -Not -Throw 35 | } 36 | It -Name 'Should rotate the image' -Test { 37 | Should -InvokeVerifiable 38 | $newPixels.'0,0' | Should -Be '255,255,255' 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/Tests/Update-pwBmpFileFromPixels.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll -Scriptblock { 2 | Import-Module ..\..\pwBitmapper -Force 3 | 4 | $bmp = [System.Drawing.Bitmap]::new(10, 10) 5 | $pixels = @{ 6 | '0,0' = '255,255,255' 7 | '0,1' = '255,255,255' 8 | '1,0' = '255,255,255' 9 | '1,1' = '255,255,255' 10 | } 11 | } 12 | Describe -Name 'Update-pwBmpFileFromPixels' -Fixture { 13 | BeforeAll -Scriptblock { 14 | InModuleScope -ModuleName 'pwBitmapper' -ScriptBlock { 15 | Mock -CommandName 'Copy-pwBitmapImage' -MockWith { 16 | [System.Drawing.Bitmap]::new(10, 10) 17 | } -Verifiable 18 | Mock -CommandName 'Get-pwPixelMax' -MockWith {@{xMax = 10; yMax = 10}} -Verifiable 19 | Mock -CommandName 'Move-pwPixels' -MockWith { 20 | @{ 21 | '0,0' = '255,255,255' 22 | '0,1' = '255,255,255' 23 | '1,0' = '255,255,255' 24 | '1,1' = '255,255,255' 25 | } 26 | } -Verifiable 27 | Mock -CommandName 'Set-pwBmpPixels' -MockWith { 28 | [System.Drawing.Bitmap]::new(10, 10) 29 | } -Verifiable 30 | Mock -CommandName 'Save-pwBitmapFile' -MockWith { } -Verifiable 31 | } 32 | } 33 | Context -Name 'When a set of pixels are passed to update the bitmap' -Fixture { 34 | It 'Should not throw' -Test { 35 | {Update-pwBmpFileFromPixels -NewPixels $pixels -File "${ENV:TEMP}\test.bmp"} | Should -Not -Throw 36 | } 37 | It 'Should update and save the bmp' -Test { 38 | Should -InvokeVerifiable 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/pwBitmapper.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulwetter/WettersSource/6487c04bb6097baaabb0cc362b4b4a8ad57560dc/Pestering/Modules/pwBitmapper/pwBitmapper.psd1 -------------------------------------------------------------------------------- /Pestering/Modules/pwBitmapper/pwBitmapper.psm1: -------------------------------------------------------------------------------- 1 | $CommandFiles = Get-ChildItem -Path "$PSScriptRoot\Commands" -Filter '*.ps1' 2 | foreach($file in $CommandFiles){ 3 | . $file.FullName 4 | } 5 | 6 | $PrivateCommandFiles = Get-ChildItem -Path "$PSScriptRoot\PrivateCommands" -Filter '*.ps1' 7 | foreach($file in $PrivateCommandFiles){ 8 | . $file.FullName 9 | } 10 | 11 | Export-ModuleMember -Function $CommandFiles.BaseName -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/Breakfast.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulwetter/WettersSource/6487c04bb6097baaabb0cc362b4b4a8ad57560dc/Pestering/Modules/pwBreakFast/Breakfast.psd1 -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/Breakfast.psm1: -------------------------------------------------------------------------------- 1 | $CommandFiles = Get-ChildItem -Path "$PSScriptRoot\Commands" -Filter '*.ps1' 2 | foreach($file in $CommandFiles){ 3 | . $file.FullName 4 | } 5 | 6 | $PrivateCommandFiles = Get-ChildItem -Path "$PSScriptRoot\PrivateCommands" -Filter '*.ps1' 7 | foreach($file in $PrivateCommandFiles){ 8 | . $file.FullName 9 | } 10 | 11 | Export-ModuleMember -Function $CommandFiles.BaseName -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/Commands/Add-Eggs.ps1: -------------------------------------------------------------------------------- 1 | function Add-pwEggs { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [string] 6 | $Name 7 | ) 8 | Write-Verbose "Adding some $Name eggs to the order. Yum!" 9 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/Commands/Add-Toast.ps1: -------------------------------------------------------------------------------- 1 | function Add-pwToast { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $false)] 5 | [string] 6 | $Name 7 | ) 8 | if ([string]::IsNullOrEmpty($Name)){ 9 | Write-Verbose "Looks like you wanted toast but didn't tell us what kind. So, we'll add a healthy wheat toast" 10 | } else{ 11 | Write-Verbose "One order of $Name toast coming right up!" 12 | } 13 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/Commands/Add-pwBreakfastMeat.ps1: -------------------------------------------------------------------------------- 1 | function Add-pwBreakfastMeat { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [string[]] 6 | $Name 7 | ) 8 | foreach ($Item in $Name) { 9 | Write-Verbose "Adding a healthy serving of $Item" 10 | } 11 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/Commands/Add-pwPastry.ps1: -------------------------------------------------------------------------------- 1 | function Add-pwPastry { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [string] 6 | $Name 7 | ) 8 | Write-Verbose "Adding a fresh and delicious $Name pastry to this meal!" 9 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/Commands/Get-Beverage.ps1: -------------------------------------------------------------------------------- 1 | function Add-pwBeverage { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [string] 6 | $Name 7 | ) 8 | Write-Verbose "Pouring a fine $Name..." 9 | If ($Name -eq "Coffee") { 10 | return "Mug" 11 | } 12 | else { 13 | return "Glass" 14 | } 15 | } -------------------------------------------------------------------------------- /Pestering/Modules/pwBreakFast/WorkingFile.ps1: -------------------------------------------------------------------------------- 1 | [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 2 | $bmp = New-Object System.Drawing.Bitmap(240, 240) 3 | 4 | for ($i = 0; $i -lt 240; $i++) 5 | { 6 | for ($j = 0; $j -lt 240; $j += 2) 7 | { 8 | $bmp.SetPixel($i, $j, 'Red') 9 | $bmp.SetPixel($i, $j + 1, [System.Drawing.Color]::FromArgb(0, 100, 200)) 10 | } 11 | } 12 | 13 | $bmp.Save("C:\Temp\Test.bmp") 14 | ii f:\Temp\bmp.bmp 15 | 16 | 17 | function Convert-pwBmpToPixels { 18 | [OutputType([System.Collections.Hashtable])] 19 | [CmdletBinding(DefaultParameterSetName = 'Bitmap')] 20 | param ( 21 | [Parameter(Mandatory = $true, 22 | ParameterSetName = 'File')] 23 | $File, 24 | [Parameter(Mandatory = $true, 25 | ParameterSetName = 'Bitmap')] 26 | [System.Drawing.Image] 27 | $Bitmap 28 | ) 29 | if ($PSBoundParameters.Keys.Contains('File')){ 30 | $bmp = [System.Drawing.Bitmap]::FromFile("$File") 31 | } else { 32 | $bmp = $Bitmap 33 | } 34 | $width = $bmp.Width 35 | $height = $bmp.Height 36 | $pixels = @{} 37 | for ($x = 0; $x -lt $width; $x++) { 38 | for ($y = 0; $y -lt $height; $y++) { 39 | $pix = $bmp.GetPixel($x,$y) 40 | $pixels.Add("$x,$y","$($pix.R),$($pix.G),$($pix.B)") 41 | } 42 | } 43 | $pixels 44 | } 45 | 46 | function New-BmpFileFromPixels { 47 | [CmdletBinding()] 48 | param ( 49 | $Pixels, 50 | $File 51 | ) 52 | $bmp = Convert-pwPixelstoBmp -Pixels $Pixels 53 | $bmp.Save("$File") 54 | } 55 | 56 | function Convert-pwPixelstoBmp{ 57 | [OutputType([System.Drawing.Image])] 58 | [CmdletBinding()] 59 | param ( 60 | $Pixels 61 | ) 62 | [int]$xMax = 0 63 | [int]$yMax = 0 64 | foreach ($pix in $Pixels.GetEnumerator()){ 65 | [int]$x = $pix.Key.split(',')[0] 66 | [int]$y = $pix.Key.split(',')[1] 67 | if ($x -gt $xMax) {$xMax = $x} 68 | if ($y -gt $yMax) {$yMax = $y} 69 | } 70 | $xMax++ 71 | $yMax++ 72 | Write-Verbose "Size is $xMax x $yMax" 73 | $bmp = [System.Drawing.Bitmap]::New($xMax,$yMax) 74 | Write-Verbose "Creating BMP of $($bmp.Height) x $($bmp.Width)" 75 | foreach ($pix in $Pixels.GetEnumerator()){ 76 | $xy = $pix.Key 77 | [int]$x = $xy.split(',')[0] 78 | [int]$y = $xy.split(',')[1] 79 | $rgb = $pix.Value 80 | [int]$R = $rgb.split(',')[0] 81 | [int]$G = $rgb.split(',')[1] 82 | [int]$B = $rgb.split(',')[2] 83 | $color = [System.Drawing.Color]::FromArgb($R, $G, $B) 84 | $bmp.SetPixel($x, $y, $color) 85 | } 86 | $bmp 87 | } 88 | 89 | function New-EmptyBitmapFile { 90 | [CmdletBinding()] 91 | param ( 92 | [int] 93 | $Height, 94 | [int] 95 | $Width, 96 | [string] 97 | $File 98 | ) 99 | $bmp = [System.Drawing.Bitmap]::new($Width, $Height) 100 | $bmp.Save("$File") 101 | } 102 | 103 | function Update-BmpFileFromPixels { 104 | [CmdletBinding()] 105 | param ( 106 | $Pixels, 107 | [int]$Startx, 108 | [int]$Starty, 109 | $File 110 | ) 111 | $ExistingBmp = [System.Drawing.Bitmap]::FromFile("$File") 112 | $bmp = [System.Drawing.Bitmap]::new($ExistingBmp) 113 | $ExistingBmp.Dispose() 114 | [system.gc]::Collect() 115 | [int]$xMax = 0 116 | [int]$yMax = 0 117 | foreach ($pix in $Pixels.GetEnumerator()){ 118 | [int]$x = $pix.Key.split(',')[0] 119 | [int]$y = $pix.Key.split(',')[1] 120 | if ($x -gt $xMax) {$xMax = $x} 121 | if ($y -gt $yMax) {$yMax = $y} 122 | } 123 | $xMax = $xMax++ 124 | $yMax = $yMax++ 125 | Write-Verbose "Size is $xMax x $yMax" 126 | Write-Verbose "Creating BMP of $($bmp.Height) x $($bmp.Width)" 127 | foreach ($pix in $Pixels.GetEnumerator()){ 128 | $xy = $pix.Key 129 | [int]$x = $xy.split(',')[0] 130 | [int]$y = $xy.split(',')[1] 131 | $x = $x + $Startx 132 | $y = $y + $Starty 133 | $rgb = $pix.Value 134 | [int]$R = $rgb.split(',')[0] 135 | [int]$G = $rgb.split(',')[1] 136 | [int]$B = $rgb.split(',')[2] 137 | $color = [System.Drawing.Color]::FromArgb($R, $G, $B) 138 | Write-Verbose "Setting Pixel $x, $y" 139 | $bmp.SetPixel($x, $y, $color) 140 | } 141 | $bmp.Save("$File") 142 | } 143 | 144 | function Set-pwBmpRotate { 145 | [OutputType([System.Drawing.Image])] 146 | [CmdletBinding()] 147 | param ( 148 | [System.Drawing.Image] 149 | $Bitmap, 150 | [ValidateSet(90,180,270)] 151 | [int]$Rotate = 90 152 | ) 153 | switch ($Rotate) { 154 | 90 { $Bitmap.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipNone) } 155 | 180 { $Bitmap.RotateFlip([System.Drawing.RotateFlipType]::Rotate180FlipNone) } 156 | 270 { $Bitmap.RotateFlip([System.Drawing.RotateFlipType]::Rotate270FlipNone) } 157 | } 158 | $Bitmap 159 | } 160 | 161 | function Set-pwPixelRotate { 162 | [OutputType([System.Collections.Hashtable])] 163 | [CmdletBinding()] 164 | param ( 165 | $Pixels, 166 | [ValidateSet(90,180,270)] 167 | [int]$Rotate = 90 168 | ) 169 | $bmp = Convert-pwPixelstoBmp -Pixels $Pixels 170 | $RotatedBmp = Set-pwBmpRotate -Bitmap $bmp -Rotate $Rotate 171 | Convert-pwBmpToPixels -Bitmap $RotatedBmp 172 | } 173 | 174 | function Add-ToPlate { 175 | [CmdletBinding()] 176 | param ( 177 | [Parameter(Mandatory=$true)] 178 | $ParameterName 179 | ) 180 | } 181 | 182 | 183 | 184 | 185 | Update-BmpFromPixels -Pixels $pixels -Startx 120 -Starty 120 -File C:\Temp\Funny.bmp 186 | Update-BmpFromPixels -Pixels $pixels -Startx 0 -Starty 120 -File C:\Temp\Funny.bmp 187 | Update-BmpFromPixels -Pixels $pixels -Startx 120 -Starty 0 -File C:\Temp\Funny.bmp 188 | Update-BmpFromPixels -Pixels $pixels -File C:\Temp\Funny.bmp 189 | -------------------------------------------------------------------------------- /PolicyReverse/Set-AllowRun.ps1: -------------------------------------------------------------------------------- 1 | #Version 7 2 | 3 | #These are registry settings that will be updated in the HKCU profiles on all profiles on the computer. 4 | #If you need to add another, just copy one of the entire [PSCustomObject]@{....} 5 | $UserRegistrySettings = @( 6 | [PSCustomObject]@{ 7 | RegPath = '\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer' 8 | RegValue = 'NoRun' 9 | RegData = 0 10 | }, 11 | [PSCustomObject]@{ 12 | RegPath = '\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer' 13 | RegValue = 'NoPinningToTaskbar' 14 | RegData = 0 15 | }, 16 | [PSCustomObject]@{ 17 | RegPath = '\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer' 18 | RegValue = 'NoViewContextMenu' 19 | RegData = 0 20 | }, 21 | [PSCustomObject]@{ 22 | RegPath = '\Software\Microsoft\Windows\CurrentVersion\Policies\System' 23 | RegValue = 'DisableTaskMgr' 24 | RegData = 0 25 | } 26 | ) 27 | 28 | 29 | 30 | 31 | function Write-Log { 32 | [CmdletBinding()] 33 | Param ( 34 | [Parameter(Mandatory = $false)] 35 | $Message, 36 | 37 | [Parameter(Mandatory = $false)] 38 | $ErrorMessage, 39 | 40 | [Parameter(Mandatory = $false)] 41 | $Component, 42 | 43 | [Parameter(Mandatory = $false, HelpMessage = "1 = Normal, 2 = Warning (yellow), 3 = Error (red)")] 44 | [ValidateSet(1, 2, 3)] 45 | [int]$Type, 46 | 47 | [Parameter(Mandatory = $false, HelpMessage = "Size in KB")] 48 | [int]$LogSizeKB = 512, 49 | 50 | [Parameter(Mandatory = $true)] 51 | $LogFile 52 | ) 53 | <# 54 | Type: 1 = Normal, 2 = Warning (yellow), 3 = Error (red) 55 | #> 56 | Write-Verbose -Message $Message 57 | If ($ErrorMessage) { Write-Verbose -Message $ErrorMessage } 58 | Try { 59 | IF (!(Test-Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName)) { 60 | New-Item -ItemType directory -Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName 61 | } 62 | } 63 | Catch { 64 | Throw 'Failed to find/set parent directory path' 65 | } 66 | $LogLength = $LogSizeKB * 1024 67 | try { 68 | $log = Get-Item $LogFile -ErrorAction Stop 69 | If (($log.length) -gt $LogLength) { 70 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 71 | $Date = Get-Date -Format "MM-dd-yyyy" 72 | $LogMessage = "" 73 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 74 | Move-Item -Path "$LogFile" -Destination "$($LogFile.TrimEnd('g'))_" -Force 75 | } 76 | } 77 | catch { Write-Verbose "Nothing to move or move failed." } 78 | 79 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 80 | $Date = Get-Date -Format "MM-dd-yyyy" 81 | 82 | if ($null -ne $ErrorMessage) { $Type = 3 } 83 | if ($null -eq $Component) { $Component = " " } 84 | if ($null -eq $Type) { $Type = 1 } 85 | 86 | $LogMessage = "" 87 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 88 | } 89 | 90 | function Get-ProcessOutput 91 | { 92 | Param ( 93 | [Parameter(Mandatory=$true)] 94 | [string]$FileName, 95 | [Parameter(Mandatory=$false)] 96 | [string]$Arguments 97 | ) 98 | 99 | $process = New-Object System.Diagnostics.Process 100 | $process.StartInfo.UseShellExecute = $false 101 | $process.StartInfo.RedirectStandardOutput = $true 102 | $process.StartInfo.RedirectStandardError = $true 103 | $process.StartInfo.FileName = $FileName 104 | if($Arguments) { $process.StartInfo.Arguments = $Arguments } 105 | $null = $process.Start() 106 | 107 | $StandardError = $process.StandardError.ReadToEnd() 108 | $StandardOutput = $process.StandardOutput.ReadToEnd() 109 | 110 | [PSCustomObject]@{ 111 | StandardOutput = $StandardOutput 112 | StandardError = $StandardError 113 | } 114 | } 115 | 116 | $PSDefaultParameterValues["Write-Log:LogFile"] = "C:\Windows\Logs\Software\Update-RunPolicy.log" 117 | $PSDefaultParameterValues["Write-Log:Verbose"] = $false 118 | 119 | $TempHive = 'TempUser' 120 | Write-Log -Message "******************Beginning Run Policy Update******************" 121 | Write-Log -Message "Checking Temp Hive is Empty..." 122 | If (Test-Path -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$TempHive\SOFTWARE"){ 123 | Write-Log -Message "Temp hive [HKEY_USERS\$TempHive] was not empty. attempting to unload..." 124 | $Output = Get-ProcessOutput -FileName 'REG.EXE' -Arguments "UNLOAD HKU\$TempHive" 125 | if ([string]::IsNullOrEmpty($Output.StandardError)){ 126 | Write-Log -Message "Successfully Unloaded user hive out of [HKEY_USERS\$TempHive]" 127 | } else { 128 | Write-Log -Message "Failed to unload existing temp hive [HKEY_USERS\$TempHive]. Error [$($Output.StandardError)]. Searching for new temp hive..." -Type 2 129 | for ($i = 1; $i -lt 10; $i++){ 130 | $NewTemp = "{0}{1}" -f $TempHive,$i 131 | If (!(Test-Path -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$NewTemp\SOFTWARE")){ 132 | $TempHive = $NewTemp 133 | Write-Log -Message "Successfully found new avialable temp hive for loading at [HKEY_USERS\$TempHive]. Continuing script..." 134 | Break 135 | } 136 | } 137 | } 138 | } 139 | Write-Log -Message "Getting all user profiles on this machine." 140 | $MachineUserProfiles = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' 141 | $RealUserProfiles = $MachineUserProfiles.Where({$_.PSChildName -notin @('S-1-5-18','S-1-5-19','S-1-5-20')}) 142 | Write-Log -Message "Getting all user profile hives already loaded on this machine." 143 | $LoadedHives = Get-ChildItem -Path 'Microsoft.PowerShell.Core\Registry::HKEY_USERS' | Where-Object { $_.PSChildName -notlike "*_classes" } 144 | Write-Log -Message "looping through all of the profile users [$($RealUserProfiles.count)]." 145 | Foreach ($Profile in $RealUserProfiles) { 146 | Write-Log -Message "Updating run policy for user [$($Profile.PSChildName)]." 147 | If ($Profile.PSChildName -in ($LoadedHives.PSChildName)) { 148 | Write-Log "Profile matches that of one of the already loaded registry hive files. No need to load a hive." 149 | foreach ($UserSetting in $UserRegistrySettings) { 150 | $RegPath = "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$($Profile.PSChildName)$($UserSetting.RegPath)" 151 | If (!(Test-Path -Path "$RegPath")){ 152 | New-Item -Path "$RegPath" -Force -ErrorAction Ignore 153 | } 154 | If ((Get-ItemProperty -Path $RegPath -ErrorAction Ignore).$($UserSetting.RegValue) -ne $UserSetting.RegData) { 155 | Write-Log -Message "[$($UserSetting.RegValue)] value not set as expected [$RegPath]" 156 | Try{ 157 | Set-ItemProperty -Path $RegPath -Name "$($UserSetting.RegValue)" -Value $($UserSetting.RegData) -ErrorAction Stop 158 | Write-Log -Message "Successfully set [$($UserSetting.RegValue)] value for [$RegPath]" 159 | } 160 | Catch{ 161 | Write-Log -Message "Failed to set [$($UserSetting.RegValue)] value for [$RegPath]" -Type 3 162 | Write-Log -Message "Exception: $($_.Exception.Message) Reason: $($_.CategoryInfo.Reason)" -Type 3 163 | } 164 | } else { 165 | Write-Log -Message "Already set [$($UserSetting.RegValue)] value for [$RegPath]" 166 | } 167 | } 168 | $DeleteKey = "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$($Profile.PSChildName)\SOFTWARE\Policies\Microsoft\Windows\Explorer" 169 | Write-Log -Message "Checking for [$DeleteKey]" -Type 1 170 | if (Test-Path "$DeleteKey") { 171 | Try { 172 | Remove-Item "$DeleteKey" -Recurse -Force -ErrorAction Stop 173 | Write-Log -Message "Successfully removed [$DeleteKey]" -Type 1 174 | } 175 | Catch { 176 | Write-Log -Message "Failed to removed [$DeleteKey]" -Type 3 177 | Write-Log -Message "Exception: $($_.Exception.Message) Reason: $($_.CategoryInfo.Reason)" -Type 3 178 | } 179 | } else { 180 | Write-Log -Message "Registry key [$DeleteKey] not found on system." -Type 1 181 | } 182 | } 183 | else { 184 | $Output = Get-ProcessOutput -FileName 'REG.EXE' -Arguments "LOAD HKU\$TempHive `"$($Profile.ProfileImagePath)\NTUSER.DAT`"" 185 | if ([string]::IsNullOrEmpty($Output.StandardError)){ 186 | Write-Log "Loaded user hive for [$($Profile.PSChildName)] into [HKEY_USERS\$TempHive] successfully." 187 | 188 | foreach ($UserSetting in $UserRegistrySettings) { 189 | $RegPath = "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$TempHive$($UserSetting.RegPath)" 190 | If (!(Test-Path -Path "$RegPath")){ 191 | New-Item -Path "$RegPath" -Force -ErrorAction Ignore 192 | } 193 | If ((Get-ItemProperty -Path $RegPath -ErrorAction Ignore).$($UserSetting.RegValue) -ne $UserSetting.RegData) { 194 | Write-Log -Message "[$($UserSetting.RegValue)] value not set as expected [$RegPath]" 195 | Try{ 196 | Set-ItemProperty -Path $RegPath -Name "$($UserSetting.RegValue)" -Value $($UserSetting.RegData) -ErrorAction Stop 197 | Write-Log -Message "Successfully set [$($UserSetting.RegValue)] value for [$RegPath]" 198 | } 199 | Catch{ 200 | Write-Log -Message "Failed to set [$($UserSetting.RegValue)] value for [$RegPath]" -Type 3 201 | Write-Log -Message "Exception: $($_.Exception.Message) Reason: $($_.CategoryInfo.Reason)" -Type 3 202 | } 203 | } else { 204 | Write-Log -Message "Already set [$($UserSetting.RegValue)] value for [$RegPath]" 205 | } 206 | } 207 | $DeleteKey = "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$TempHive\SOFTWARE\Policies\Microsoft\Windows\Explorer" 208 | Write-Log -Message "Checking for [$DeleteKey]" -Type 1 209 | if (Test-Path "$DeleteKey") { 210 | Try { 211 | Remove-Item "$DeleteKey" -Recurse -Force -ErrorAction Stop 212 | Write-Log -Message "Successfully removed [$DeleteKey]" -Type 1 213 | } 214 | Catch { 215 | Write-Log -Message "Failed to removed [$DeleteKey]" -Type 3 216 | Write-Log -Message "Exception: $($_.Exception.Message) Reason: $($_.CategoryInfo.Reason)" -Type 3 217 | } 218 | } else { 219 | Write-Log -Message "Registry key [$DeleteKey] not found on system." -Type 1 220 | } 221 | } 222 | else { 223 | Write-Log "Failed to load Hive into temporary space. Error [$($Output.StandardError)]." -Type 3 224 | } 225 | for ($i = 0; $i -lt 10; $i++) { 226 | $Output = Get-ProcessOutput -FileName 'REG.EXE' -Arguments "UNLOAD HKU\$TempHive" 227 | if ([string]::IsNullOrEmpty($Output.StandardError)){ 228 | Write-Log "Successfully Unloaded user hive for [$($Profile.PSChildName)] out of [HKEY_USERS\$TempHive]" 229 | break 230 | } else { 231 | Write-Log "Failed to Unloaded user hive for [$($Profile.PSChildName)] out of [HKEY_USERS\$TempHive]. Will wait a second and try again." -Type 3 232 | Start-Sleep -Seconds 1 233 | } 234 | } 235 | } 236 | } 237 | 238 | 239 | If (Test-Path -Path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$TempHive\Software"){ 240 | Write-Log "Looks like there was still a Temp Registry hive loaded. Attempting to unload it. No status will be displayed" -Type 2 241 | for ($i = 0; $i -lt 10; $i++) { 242 | $Output = Get-ProcessOutput -FileName 'REG.EXE' -Arguments "UNLOAD HKU\$TempHive" 243 | if ([string]::IsNullOrEmpty($Output.StandardError)){ 244 | Write-Log "Successfully Unloaded user hive for [$($Profile.PSChildName)] out of [HKEY_USERS\$TempHive]" 245 | break 246 | } else { 247 | Write-Log "Failed to Unloaded user hive for [$($Profile.PSChildName)] out of [HKEY_USERS\$TempHive]. Will wait a second and try again." -Type 3 248 | Start-Sleep -Seconds 1 249 | } 250 | } 251 | } 252 | 253 | Write-Log "******************Completed Run Policy Update******************" -------------------------------------------------------------------------------- /SQL/Invoke-SqlDataReader.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-SqlDataReader { 2 | 3 | <# 4 | .SYNOPSIS 5 | Runs a select statement query against a SQL Server database. 6 | 7 | .DESCRIPTION 8 | Invoke-SqlDataReader is a PowerShell function that is designed to query 9 | a SQL Server database using a select statement without the need for the SQL 10 | PowerShell module or snap-in being installed. 11 | 12 | .PARAMETER ServerInstance 13 | The name of an instance of the SQL Server database engine. For default instances, 14 | only specify the server name: 'ServerName'. For named instances, use the format 15 | 'ServerName\InstanceName'. 16 | 17 | .PARAMETER Database 18 | The name of the database to query on the specified SQL Server instance. 19 | 20 | .PARAMETER Query 21 | Specifies one Transact-SQL select statement query to be run. 22 | 23 | .PARAMETER SQLPort 24 | Specific port to connect to the sql server with. Default is 1433. 25 | 26 | .PARAMETER QueryTimeout 27 | Specifies how long to wait until the SQL Query times out. default 300 Seconds 28 | 29 | .PARAMETER Credential 30 | SQL Authentication userid and password in the form of a credential object. 31 | 32 | .EXAMPLE 33 | Invoke-SqlDataReader -ServerInstance Server01 -Database Master -Query ' 34 | select name, database_id, compatibility_level, recovery_model_desc from sys.databases' 35 | 36 | .EXAMPLE 37 | 'select name, database_id, compatibility_level, recovery_model_desc from sys.databases' | 38 | Invoke-SqlDataReader -ServerInstance Server01 -Database Master 39 | 40 | .EXAMPLE 41 | 'select name, database_id, compatibility_level, recovery_model_desc from sys.databases' | 42 | Invoke-SqlDataReader -ServerInstance Server01 -Database Master -Credential (Get-Credential) 43 | 44 | .INPUTS 45 | String 46 | 47 | .OUTPUTS 48 | DataRow 49 | 50 | .NOTES 51 | #> 52 | 53 | [CmdletBinding()] 54 | param ( 55 | [Parameter(Mandatory)] 56 | [string]$ServerInstance, 57 | 58 | [Parameter(Mandatory)] 59 | [string]$Database, 60 | 61 | [Parameter(Mandatory, 62 | ValueFromPipeline)] 63 | [string]$Query, 64 | 65 | [Parameter(Mandatory=$false, 66 | ValueFromPipeline=$false)] 67 | [int]$SQLPort = 1433, 68 | 69 | [Parameter(Mandatory=$false, 70 | ValueFromPipeline=$false)] 71 | [int]$QueryTimeout = 300, 72 | 73 | [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty 74 | ) 75 | 76 | BEGIN { 77 | $connection = New-Object -TypeName System.Data.SqlClient.SqlConnection 78 | 79 | if (-not($PSBoundParameters.Credential)) { 80 | $connectionString = "Server=$ServerInstance,$SQLPort;Database=$Database;Integrated Security=True;" 81 | } 82 | else { 83 | $connectionString = "Server=$ServerInstance,$SQLPort;Database=$Database;Integrated Security=False;" 84 | $userid= $Credential.UserName -replace '^.*\\|@.*$' 85 | ($password = $credential.Password).MakeReadOnly() 86 | $sqlCred = New-Object -TypeName System.Data.SqlClient.SqlCredential($userid, $password) 87 | $connection.Credential = $sqlCred 88 | } 89 | 90 | $connection.ConnectionString = $connectionString 91 | $ErrorActionPreference = 'Stop' 92 | 93 | try { 94 | $connection.Open() 95 | Write-Verbose -Message "Connection to the $($connection.Database) database on $($connection.DataSource) has been successfully opened." 96 | } 97 | catch { 98 | Write-Error -Message "An error has occurred. Error details: $($_.Exception.Message)" 99 | } 100 | 101 | $ErrorActionPreference = 'Continue' 102 | $command = $connection.CreateCommand() 103 | $command.CommandTimeout = $QueryTimeout 104 | } 105 | 106 | PROCESS { 107 | $command.CommandText = $Query 108 | $ErrorActionPreference = 'Stop' 109 | 110 | try { 111 | $result = $command.ExecuteReader() 112 | } 113 | catch { 114 | Write-Error -Message "An error has occured. Error Details: $($_.Exception.Message)" 115 | } 116 | 117 | $ErrorActionPreference = 'Continue' 118 | 119 | if ($result) { 120 | $dataTable = New-Object -TypeName System.Data.DataTable 121 | $dataTable.Load($result) 122 | $dataTable 123 | } 124 | } 125 | 126 | END { 127 | $connection.Close() 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /SetupDiagGUI/Invoke-SetupDaig.ps1: -------------------------------------------------------------------------------- 1 | ##Run SetupDiag for diagnostics. 2 | 3 | $PSDefaultParameterValues["Write-Log:LogFile"] = "C:\Windows\Logs\Software\SetupDiag-Diagnostics.log" 4 | $PSDefaultParameterValues["Write-Log:Verbose"] = $false 5 | $SetupDiagExePath = 'C:\Windows\Temp\SetupDiag.exe' 6 | $SetupDiagWorkingDirectory = [io.path]::GetDirectoryName($SetupDiagExePath) 7 | ##Log Function 8 | 9 | function Write-Log { 10 | [CmdletBinding()] 11 | Param ( 12 | [Parameter(Mandatory = $false)] 13 | $Message, 14 | 15 | [Parameter(Mandatory = $false)] 16 | $ErrorMessage, 17 | 18 | [Parameter(Mandatory = $false)] 19 | $Component, 20 | 21 | [Parameter(Mandatory = $false, HelpMessage = "1 = Normal, 2 = Warning (yellow), 3 = Error (red)")] 22 | [ValidateSet(1, 2, 3)] 23 | [int]$Type, 24 | 25 | [Parameter(Mandatory = $false, HelpMessage = "Size in KB")] 26 | [int]$LogSizeKB = 512, 27 | 28 | [Parameter(Mandatory = $true)] 29 | $LogFile 30 | ) 31 | <# 32 | Type: 1 = Normal, 2 = Warning (yellow), 3 = Error (red) 33 | #> 34 | Try{ 35 | IF (!(Test-Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName)){ 36 | New-Item -ItemType directory -Path ([System.IO.DirectoryInfo]$LogFile).Parent.FullName 37 | } 38 | } 39 | Catch{ 40 | Throw 'Failed to find/set parent directory path' 41 | } 42 | $LogLength = $LogSizeKB * 1024 43 | try { 44 | $log = Get-Item $LogFile -ErrorAction Stop 45 | If (($log.length) -gt $LogLength) { 46 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 47 | $Date = Get-Date -Format "MM-dd-yyyy" 48 | $LogMessage = "" 49 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 50 | Move-Item -Path "$LogFile" -Destination "$($LogFile.TrimEnd('g'))_" -Force 51 | } 52 | } 53 | catch {Write-Verbose "Nothing to move or move failed."} 54 | 55 | $Time = Get-Date -Format "HH:mm:ss.ffffff" 56 | $Date = Get-Date -Format "MM-dd-yyyy" 57 | 58 | if ($ErrorMessage -ne $null) {$Type = 3} 59 | if ($Component -eq $null) {$Component = " "} 60 | if ($Type -eq $null) {$Type = 1} 61 | 62 | $LogMessage = "" 63 | $LogMessage | Out-File -Append -Encoding UTF8 -FilePath $LogFile 64 | } 65 | 66 | function Show-WPFWindow { 67 | param 68 | ( 69 | [Parameter(Mandatory)] 70 | [Windows.Window] 71 | $Window 72 | ) 73 | 74 | $result = $null 75 | $null = $window.Dispatcher.InvokeAsync{ 76 | $result = $window.ShowDialog() 77 | Set-Variable -Name result -Value $result -Scope 1 78 | }.Wait() 79 | $result 80 | } 81 | function Convert-XAMLtoWindow{ 82 | param 83 | ( 84 | [Parameter(Mandatory=$true)] 85 | [string] 86 | $XAML 87 | ) 88 | 89 | Add-Type -AssemblyName PresentationFramework 90 | 91 | $reader = [XML.XMLReader]::Create([IO.StringReader]$XAML) 92 | $result = [Windows.Markup.XAMLReader]::Load($reader) 93 | $reader.Close() 94 | $reader = [XML.XMLReader]::Create([IO.StringReader]$XAML) 95 | while ($reader.Read()) 96 | { 97 | $name=$reader.GetAttribute('Name') 98 | if (!$name) { $name=$reader.GetAttribute('x:Name') } 99 | if($name) 100 | {$result | Add-Member NoteProperty -Name $name -Value $result.FindName($name) -Force} 101 | } 102 | $reader.Close() 103 | $result 104 | } 105 | 106 | Function ConvertTo-HashTable { 107 | 108 | <# 109 | .Synopsis 110 | Convert an object into a hashtable. 111 | .Description 112 | This command will take an object and create a hashtable based on its properties. 113 | You can have the hashtable exclude some properties as well as properties that 114 | have no value. 115 | .Parameter Inputobject 116 | A PowerShell object to convert to a hashtable. 117 | .Parameter NoEmpty 118 | Do not include object properties that have no value. 119 | .Parameter Exclude 120 | An array of property names to exclude from the hashtable. 121 | .Example 122 | PS C:\> get-process -id $pid | select name,id,handles,workingset | ConvertTo-HashTable 123 | 124 | Name Value 125 | ---- ----- 126 | WorkingSet 418377728 127 | Name powershell_ise 128 | Id 3456 129 | Handles 958 130 | .Notes 131 | Version: 2.0 132 | Updated: January 17, 2013 133 | Author : Jeffery Hicks (http://jdhitsolutions.com/blog) 134 | .Link 135 | http://jdhitsolutions.com/blog/2013/01/convert-powershell-object-to-hashtable-revised 136 | .Link 137 | About_Hash_Tables 138 | Get-Member 139 | .Inputs 140 | Object 141 | .Outputs 142 | hashtable 143 | #> 144 | 145 | [cmdletbinding()] 146 | 147 | Param( 148 | [Parameter(Position=0,Mandatory=$True, 149 | HelpMessage="Please specify an object",ValueFromPipeline=$True)] 150 | [ValidateNotNullorEmpty()] 151 | [object]$InputObject, 152 | [switch]$NoEmpty, 153 | [string[]]$Exclude 154 | ) 155 | 156 | Process { 157 | #get type using the [Type] class because deserialized objects won't have 158 | #a GetType() method which is what we would normally use. 159 | 160 | $TypeName = [system.type]::GetTypeArray($InputObject).name 161 | Write-Verbose "Converting an object of type $TypeName" 162 | 163 | #get property names using Get-Member 164 | $names = $InputObject | Get-Member -MemberType properties | 165 | Select-Object -ExpandProperty name 166 | 167 | #define an empty hash table 168 | $hash = @{} 169 | 170 | #go through the list of names and add each property and value to the hash table 171 | $names | ForEach-Object { 172 | #only add properties that haven't been excluded 173 | if ($Exclude -notcontains $_) { 174 | #only add if -NoEmpty is not called and property has a value 175 | if ($NoEmpty -AND -Not ($inputobject.$_)) { 176 | Write-Verbose "Skipping $_ as empty" 177 | } 178 | else { 179 | Write-Verbose "Adding property $_" 180 | $hash.Add($_,$inputobject.$_) 181 | } 182 | } #if exclude notcontains 183 | else { 184 | Write-Verbose "Excluding $_" 185 | } 186 | } #foreach 187 | Write-Verbose "Writing the result to the pipeline" 188 | Write-Output $hash 189 | }#close process 190 | 191 | } 192 | 193 | if (Test-Path $SetupDiagExePath){ 194 | Write-Log -Message "Setupdiag already Exists at $SetupDiagExePath. Copying over the file." 195 | Copy-item -Path '.\SetupDiag.exe' -Destination $SetupDiagExePath -Force 196 | } Else { 197 | Write-Log -Message "Copying Setupdaig to: $SetupDiagExePath" 198 | Copy-item -Path '.\SetupDiag.exe' -Destination $SetupDiagExePath 199 | } 200 | 201 | try { 202 | Write-Log -Message "Runnning Setupdiag from $SetupDiagExePath" 203 | Start-Process -FilePath $SetupDiagExePath -ArgumentList '/AddReg' -WorkingDirectory "$SetupDiagWorkingDirectory" -WindowStyle Hidden -Wait -ErrorAction Stop 204 | Write-Log -Message "Completed Running Setupdiag from $SetupDiagExePath" 205 | } 206 | catch { 207 | Write-Log -Message "Error Running SetupDiag from $SetupDiagExePath" -Type 3 208 | Write-Log -Message "Exception: [$($_.Exception.Message)]" -Type 3 209 | Write-Log -Message "Exiting Script with exit code 1" -Type 2 210 | Exit 1 211 | } 212 | 213 | If (Test-Path -Path "HKLM:\SYSTEM\Setup\MoSetup\Volatile\SetupDiag"){ 214 | Write-Log -Message "Looks like SetupDiag wrote something to the registry... Lets take a look!" 215 | Try { 216 | $DiagnosticProperties = Get-ItemProperty "HKLM:\SYSTEM\Setup\MoSetup\Volatile\SetupDiag" -ErrorAction Stop | Select-Object -Property * -ExcludeProperty PS* 217 | } 218 | Catch { 219 | Write-Log -Message "Failed to collect diagnostic data from the registry." -Type 2 220 | Write-Log -Message "Exiting Script with exit code 1" -Type 2 221 | Exit 1 222 | } 223 | $UDHashTable = ConvertTo-HashTable $DiagnosticProperties 224 | If ($DiagnosticProperties.Remediation -like "null"){ 225 | $Recommendation = "None Available" 226 | } else { 227 | $Recommendation = "$($DiagnosticProperties.Remediation)" 228 | } 229 | 230 | $xaml = @" 231 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | Below is a summary of the output from the SetupDiag.exe diagnostic tool. Use this information to help troubleshoot failed upgrades: 252 | Execution Times: 253 | Upgrade Start Time: $($DiagnosticProperties.UpgradeStartTime) 254 | Upgrade End Time: $($DiagnosticProperties.UpgradeEndTime) 255 | Total Elapsed Time: $($DiagnosticProperties.UpgradeElapsedTime) 256 | 257 | Recommendation: 258 | $Recommendation 259 | 260 |