├── .prettierignore
├── .vscode
└── launch.json
├── Bulk Change PrinterDriverAttributes for non Package-Aware printer drivers
├── ChangePrinterDriverAttributes.ps1
├── do_you_trust_this_printer.png
└── readme.md
├── Change Lock Screen and Desktop Background in Windows 10 Pro
├── Set-LockScreen.ps1
└── readme.md
├── Defrag Windows Search Database
├── Defrag-WinSearchDB.ps1
├── readme.md
└── screenshot.png
├── Delete Exchange Online Emails Older Than x Months
└── Remove-ExchangeOnlineEmails.ps1
├── Delete Windows 10 Preinstalled Apps
└── readme.md
├── Delete reappearing printers that keeps comming back
├── DeletePrintersRDS.cmd
├── printers.png
└── readme.md
├── Email Report of File Permissions on HTML and CSV
├── Get-FolderPermissions.ps1
├── readme.md
└── screenshot.png
├── File Server Access Audit Report with PowerShell
├── 1.PNG
├── 2.PNG
├── 5.PNG
├── 6.PNG
├── 7.png
├── Get-AuditReport.ps1
└── readme.md
├── GUI Password Reset Tool for Active Directory
├── GUI.PNG
├── Set-ADPassword.ps1
└── readme.md
├── Install JRE
├── Download-JRE.ps1
├── Install-JRE.ps1
└── readme.md
├── Install LibreOffice
├── Install-LibreOffice.ps1
└── readme.md
├── Install MSI
├── Install-MSI.ps1
└── readme.md
├── Install Print Drivers Remotely
├── 1.PNG
├── 2.PNG
├── 3.png
├── Install-PrinterDriversRemotely.ps1
└── readme.md
├── Install Software Remotely
├── Install-SoftwareRemotely.ps1
├── Screenshot.png
├── Screenshot2.png
└── readme.md
├── Install Software
├── Install-Software.ps1
├── locally.png
├── psexec.png
└── readme.md
├── LICENSE
├── Optimize and cleanup of WSUS on Windows Server 2012 R2 and 2016
├── Optimize-WSUS.ps1
├── WsusDBMaintenance.sql
└── readme.md
├── Optimize drives
├── Invoke-DiskDefrag.ps1
├── Invoke-DiskOptimize.ps1
└── readme.md
├── Password Encryption for PowerShell scripts
├── New-StringDecryption.ps1
├── New-StringEncription.ps1
└── readme.md
├── RZGet launcher for Intune
└── Install-Software.ps1
├── Redirect Folder
└── Redirect-Folder.ps1
├── Remote Computer Update
├── Launch-Remote.ps1
├── LaunchRemote.cmd
├── Update-Computer.ps1
├── readme.md
└── screenshot.png
├── SQL Server Backup
├── BackupSQL.ps1
└── readme.md
├── System Center DPM 2012 (R2) HTML Report
├── DPMReport.ps1
├── readme.md
└── report_screenshot.PNG
├── Take ownership of folder
└── Take-Own.ps1
├── Windows Mainteinance
├── Win-Mnt.ps1
├── clamav-0.104.0.win.x64.zip
└── readme.md
├── Windows Server Backup Email Report of Several Servers
├── Get-WSBReport.ps1
├── Servers.txt
├── readme.md
└── wsb.PNG
└── readme.md
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore all md files:
2 | *.md
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch Remove-ExchangeOnlineEmails Script",
6 | "type": "PowerShell",
7 | "request": "launch",
8 | "script": "${workspaceFolder}\\Delete Exchange Online Emails Older Than x Months\\Remove-ExchangeOnlineEmails.ps1",
9 | "cwd": "${workspaceFolder}",
10 | "args": [
11 | "-target \"soporte@3digits.es\" -months 13 -deleteComplianceSearch"
12 | ]
13 | },
14 | {
15 | "name": "Launch Install-Software Script",
16 | "type": "PowerShell",
17 | "request": "launch",
18 | "script": "${workspaceFolder}\\Install Software\\Install-Software.ps1",
19 | "cwd": "${workspaceFolder}",
20 | "args": ["-software '7-Zip','Notepad++','Edge','Teams','Postman'"]
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/Bulk Change PrinterDriverAttributes for non Package-Aware printer drivers/ChangePrinterDriverAttributes.ps1:
--------------------------------------------------------------------------------
1 | Function Set-PrinterDriverAttributes([string]$RegistryPath){
2 | $Changes = 0
3 | Write-Host "Checking $($RegistryPath)"
4 | $Printers = Get-ChildItem -Path "$RegistryPath" -Recurse
5 | ForEach ($Printer in $Printers){
6 | $PrinterDriverAttributes = $Printer.GetValue("PrinterDriverAttributes")
7 | If($PrinterDriverAttributes % 2 -eq 0){
8 | Write-Host "Printer driver $($Printer) has PrinterDriverAttributes value of $($PrinterDriverAttributes)" -ForegroundColor Yellow
9 | Write-Host "Changing PrinterDriverAttributes to $($PrinterDriverAttributes + 1)" -ForegroundColor Yellow
10 | try{
11 | New-ItemProperty -Path $Printer.PSPath -Name PrinterDriverAttributes -PropertyType DWord -Value $($PrinterDriverAttributes + 1) -Force -ErrorAction Continue | Out-Null
12 | $Changes++
13 | } catch {
14 | Write-Host "Error changing registry key" -ForegroundColor Red
15 | Write-Host "Excepcion: $($_.Exception.GetType().FullName)" -ForegroundColor Red
16 | Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
17 | }
18 | }
19 | Else{
20 | Write-Host "Printer driver $($Printer) has PrinterDriverAttributes value of $($PrinterDriverAttributes)" -ForegroundColor DarkCyan
21 | }
22 | }
23 | Return $Changes
24 | }
25 |
26 | $ErrorActionPreference = "Stop"
27 |
28 | If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
29 | [Security.Principal.WindowsBuiltInRole] "Administrator"))
30 | {
31 | Write-Error "You need to run script as administrador"
32 | Exit(1)
33 | }
34 | $DriversChanged = Set-PrinterDriverAttributes "HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Drivers\Version-3"
35 | $DriversChanged += Set-PrinterDriverAttributes "HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows x64\Drivers\Version-3"
36 | If ($DriversChanged){
37 | Write-Host "$($DriversChanged) registry keys changed." -ForegroundColor Green
38 | Write-Host "Restarting Spooler." -ForegroundColor Yellow
39 | Restart-Service Spooler
40 | }
41 | Else{
42 | Write-Host "All PrinterDriverAttributes registry keys OK." -ForegroundColor Green
43 | }
--------------------------------------------------------------------------------
/Bulk Change PrinterDriverAttributes for non Package-Aware printer drivers/do_you_trust_this_printer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Bulk Change PrinterDriverAttributes for non Package-Aware printer drivers/do_you_trust_this_printer.png
--------------------------------------------------------------------------------
/Bulk Change PrinterDriverAttributes for non Package-Aware printer drivers/readme.md:
--------------------------------------------------------------------------------
1 | # Bulk Change PrinterDriverAttributes for non Package-Aware printer drivers
2 |
3 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Bulk%20Change%20PrinterDriverAttributes%20for%20non%20Package-Aware%20printer%20drivers/ChangePrinterDriverAttributes.ps1)
4 |
5 |
6 |
7 |
8 |
9 | Since [KB3170455](https://support.microsoft.com/en-us/topic/ms16-087-security-update-for-windows-print-spooler-components-july-12-2016-afceb380-914b-500f-5aa2-904fe6d13817), deployed Printers via Group Policy does not install non Package-Aware printer drivers automatically. Users are prompted with a message saying "Do yo trust this printer?"
10 |
11 | More info and manual fix: [Group Policy Printer Issue – Print and Point Restrictions – KB3170455](https://www.richardwalz.com/group-policy-printer-issue-print-and-point-restrictions-kb3170455/)
12 |
13 | You have to install printer driver manually in each computer. But, for many old drivers, you can force GPO driver installation changing PrinterDriverAttributes registry value to a odd number in print server.
14 |
15 | This script, scan registry and changes all even PrinterDriverAttributes to allow driver installation by GPO. You have to restart print server spooler after running it.
16 |
17 |
--------------------------------------------------------------------------------
/Change Lock Screen and Desktop Background in Windows 10 Pro/Set-LockScreen.ps1:
--------------------------------------------------------------------------------
1 | <#PSScriptInfo
2 |
3 | .VERSION 1.0
4 |
5 | .GUID 9320af4f-61f1-4e65-9579-62e6e14e3d4f
6 |
7 | .AUTHOR Juan Granados
8 |
9 | .COPYRIGHT 2021 Juan Granados
10 |
11 | .TAGS Lock Screen Windows 10 Pro GPO
12 |
13 | .LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
14 |
15 | .PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/Change%20Lock%20Screen%20and%20Desktop%20Background%20in%20Windows%2010%20Pro
16 |
17 | .RELEASENOTES
18 | Change Lock Screen and Desktop Background in Windows 10 Pro.
19 | #>
20 |
21 | <#
22 | .SYNOPSIS
23 | Change Lock Screen and Desktop Background in Windows 10 Pro.
24 | .DESCRIPTION
25 | This script allows you to change logon screen and desktop background in Windows 10 Professional using GPO startup script.
26 | .PARAMETER LockScreenSource (Optional)
27 | Path to the Lock Screen image to copy locally in computer.
28 | Example: "\\SERVER-FS01\LockScreen.jpg"
29 | .PARAMETER BackgroundSource (Optional)
30 | Path to the Desktop Background image to copy locally in computer.
31 | Example: "\\SERVER-FS01\BackgroundScreen.jpg"
32 | .PARAMETER LogPath (Optional)
33 | Path where save log file. If it's not specified no log is recorded.
34 | .EXAMPLE
35 | Set Lock Screen and Desktop Wallpaper with logs:
36 | Set-LockScreen -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
37 | .EXAMPLE
38 | Set Lock Screen and Desktop Wallpaper without logs:
39 | Set-LockScreen -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg"
40 | .EXAMPLE
41 | Set Lock Screen only:
42 | Set-LockScreen -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
43 | .EXAMPLE
44 | Set Desktop Wallpaper only:
45 | Set-LockScreen -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
46 | .NOTES
47 | Author: Juan Granados
48 | #>
49 | Param(
50 | [Parameter(Mandatory=$false,Position=0)]
51 | [ValidateNotNullOrEmpty()]
52 | [string]$LockScreenSource,
53 | [Parameter(Mandatory=$false,Position=1)]
54 | [ValidateNotNullOrEmpty()]
55 | [string]$BackgroundSource,
56 | [Parameter(Mandatory=$false,Position=2)]
57 | [ValidateNotNullOrEmpty()]
58 | [string]$LogPath
59 | )
60 |
61 | #Requires -RunAsAdministrator
62 |
63 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) {
64 | Start-Transcript -Path "$($LogPath)\$($env:COMPUTERNAME).log" | Out-Null
65 | }
66 |
67 | $ErrorActionPreference = "Stop"
68 |
69 | $RegKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP"
70 |
71 | $DesktopPath = "DesktopImagePath"
72 | $DesktopStatus = "DesktopImageStatus"
73 | $DesktopUrl = "DesktopImageUrl"
74 | $LockScreenPath = "LockScreenImagePath"
75 | $LockScreenStatus = "LockScreenImageStatus"
76 | $LockScreenUrl = "LockScreenImageUrl"
77 |
78 | $StatusValue = "1"
79 | $DesktopImageValue = "C:\Windows\System32\Desktop.jpg"
80 | $LockScreenImageValue = "C:\Windows\System32\LockScreen.jpg"
81 |
82 | if (!$LockScreenSource -and !$BackgroundSource)
83 | {
84 | Write-Host "Either LockScreenSource or BackgroundSource must has a value."
85 | }
86 | else
87 | {
88 | if(!(Test-Path $RegKeyPath)) {
89 | Write-Host "Creating registry path $($RegKeyPath)."
90 | New-Item -Path $RegKeyPath -Force | Out-Null
91 | }
92 | if ($LockScreenSource) {
93 | Write-Host "Copy Lock Screen image from $($LockScreenSource) to $($LockScreenImageValue)."
94 | Copy-Item $LockScreenSource $LockScreenImageValue -Force
95 | Write-Host "Creating registry entries for Lock Screen"
96 | New-ItemProperty -Path $RegKeyPath -Name $LockScreenStatus -Value $StatusValue -PropertyType DWORD -Force | Out-Null
97 | New-ItemProperty -Path $RegKeyPath -Name $LockScreenPath -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
98 | New-ItemProperty -Path $RegKeyPath -Name $LockScreenUrl -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
99 | }
100 | if ($BackgroundSource) {
101 | Write-Host "Copy Desktop Background image from $($BackgroundSource) to $($DesktopImageValue)."
102 | Copy-Item $BackgroundSource $DesktopImageValue -Force
103 | Write-Host "Creating registry entries for Desktop Background"
104 | New-ItemProperty -Path $RegKeyPath -Name $DesktopStatus -Value $StatusValue -PropertyType DWORD -Force | Out-Null
105 | New-ItemProperty -Path $RegKeyPath -Name $DesktopPath -Value $DesktopImageValue -PropertyType STRING -Force | Out-Null
106 | New-ItemProperty -Path $RegKeyPath -Name $DesktopUrl -Value $DesktopImageValue -PropertyType STRING -Force | Out-Null
107 | }
108 | }
109 |
110 | if (-not [string]::IsNullOrWhiteSpace($LogPath)){Stop-Transcript}
111 |
--------------------------------------------------------------------------------
/Change Lock Screen and Desktop Background in Windows 10 Pro/readme.md:
--------------------------------------------------------------------------------
1 | # **Change Lock Screen and Desktop Background in Windows 10 Pro**
2 |
3 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Change%20Lock%20Screen%20and%20Desktop%20Background%20in%20Windows%2010%20Pro/Set-LockScreen.ps1)
4 |
5 | This script allows you to change login screen and desktop background in Windows 10 Professional using GPO startup script.
6 |
7 | By default, lock screen can not be changed by GPO in Windows 10 Professional, with this script you can change it to comply with corporate image.
8 |
9 | Create a GPO to run this PowerShell script.
10 |
11 | Examples:
12 |
13 | Set Lock Screen and Desktop Wallpaper with logs:
14 |
15 | ```powershell
16 | .\Set-LockScreen.ps1 -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
17 | ```
18 |
19 | Set Lock Screen and Desktop Wallpaper without logs:
20 |
21 | ```powershell
22 | .\Set-LockScreen.ps1 -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg"
23 | ```
24 |
25 | Set Lock Screen only:
26 |
27 | ```powershell
28 | .\Set-LockScreen.ps1 -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
29 | ```
30 |
31 | Set Desktop Wallpaper only:
32 |
33 | ```powershell
34 | .\Set-LockScreen.ps1 -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
35 | ```
36 |
37 | ```powershell
38 | <#
39 | .SYNOPSIS
40 | Change Lock Screen and Desktop Background in Windows 10 Pro.
41 | .DESCRIPTION
42 | This script allows you to change logon screen and desktop background in Windows 10 Professional using GPO startup script.
43 | .PARAMETER LockScreenSource (Optional)
44 | Path to the Lock Screen image to copy locally in computer.
45 | Example: "\\SERVER-FS01\LockScreen.jpg"
46 | .PARAMETER BackgroundSource (Optional)
47 | Path to the Desktop Background image to copy locally in computer.
48 | Example: "\\SERVER-FS01\BackgroundScreen.jpg"
49 | .PARAMETER LogPath (Optional)
50 | Path where save log file. If it's not specified no log is recorded.
51 | .EXAMPLE
52 | Set Lock Screen and Desktop Wallpaper with logs:
53 | Set-Loca Screen -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
54 | .EXAMPLE
55 | Set Lock Screen and Desktop Wallpaper without logs:
56 | Set-LockScreen -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg"
57 | .EXAMPLE
58 | Set Lock Screen only:
59 | .\Set-Screen.ps1 -LockScreenSource "\\SERVER-FS01\LockScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
60 | .EXAMPLE
61 | Set Desktop Wallpaper only:
62 | .\Set-LockScreen.ps1 -BackgroundSource "\\SERVER-FS01\BackgroundScreen.jpg" -LogPath "\\SERVER-FS01\Logs"
63 | .NOTES
64 | Author: Juan Granados
65 | #>
66 | ```
67 |
--------------------------------------------------------------------------------
/Defrag Windows Search Database/Defrag-WinSearchDB.ps1:
--------------------------------------------------------------------------------
1 | <#PSScriptInfo
2 |
3 | .VERSION 1.0
4 |
5 | .GUID c478611d-d1c2-4063-8f40-fa67874a3711
6 |
7 | .AUTHOR Juan Granados
8 |
9 | .COPYRIGHT 2021 Juan Granados
10 |
11 | .TAGS WSUS Windows Update Remote Software
12 |
13 | .LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
14 |
15 | .PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/Defrag%20Windows%20Search%20Database
16 |
17 | .RELEASENOTES Initial release
18 |
19 | #>
20 |
21 | <#
22 | .SYNOPSIS
23 | Defrag Windows Search Database.
24 | .DESCRIPTION
25 | Defrag Windows Search Database and optionally deletes it after error.
26 | .PARAMETER DataBase
27 | Windows Search Database path.
28 | Default C:\ProgramData\Microsoft\Search\Data\Applications\Windows\Windows.edb
29 | .PARAMETER TempPath
30 | Temporary folder to perform defrag. Using a different physical drive is recommended.
31 | Default: C:\ProgramData\Microsoft\Search\Data\Applications\Windows
32 | .PARAMETER LogPath
33 | Log file path.
34 | Default "Documents"
35 | .PARAMETER DeleteOnError
36 | Deletes Windows Search Database and modify registry to rebuild it at next Search Service startup.
37 | Default false.
38 | .EXAMPLE
39 | Defrag-WinSearchDB -LogPath "\\ES-CPD-BCK02\Log" -TempPath \\ES-CPD-BCK02\Temp
40 | .EXAMPLE
41 | Defrag-WinSearchDB -TempPath "D:\Temp"
42 | .LINK
43 | https://github.com/juangranados/powershell-scripts/tree/main/Defrag%20Windows%20Search%20Database
44 | .NOTES
45 | Author: Juan Granados
46 | #>
47 | Param(
48 | [Parameter()]
49 | [ValidateNotNullOrEmpty()]
50 | [string]$DataBase = "$($env:ProgramData)\Microsoft\Search\Data\Applications\Windows\Windows.edb",
51 | [Parameter()]
52 | [ValidateNotNullOrEmpty()]
53 | [string]$LogPath = [Environment]::GetFolderPath("MyDocuments"),
54 | [Parameter()]
55 | [ValidateNotNullOrEmpty()]
56 | [string]$TempPath = "$($env:ProgramData)\Microsoft\Search\Data\Applications\Windows",
57 | [Parameter()]
58 | [switch]$DeleteOnError
59 | )
60 | ## ------------------------------------------------------------------
61 | # function Invoke-Process
62 | # https://stackoverflow.com/a/66700583
63 | ## ------------------------------------------------------------------
64 | function Invoke-Process {
65 | param
66 | (
67 | [Parameter(Mandatory)]
68 | [ValidateNotNullOrEmpty()]
69 | [string]$FilePath,
70 |
71 | [Parameter()]
72 | [ValidateNotNullOrEmpty()]
73 | [string]$ArgumentList,
74 |
75 | [ValidateSet("Full", "StdOut", "StdErr", "ExitCode", "None")]
76 | [string]$DisplayLevel
77 | )
78 |
79 | $ErrorActionPreference = 'Stop'
80 |
81 | try {
82 | $pinfo = New-Object System.Diagnostics.ProcessStartInfo
83 | $pinfo.FileName = $FilePath
84 | $pinfo.RedirectStandardError = $true
85 | $pinfo.RedirectStandardOutput = $true
86 | $pinfo.UseShellExecute = $false
87 | $pinfo.WindowStyle = 'Hidden'
88 | $pinfo.CreateNoWindow = $true
89 | $pinfo.Arguments = $ArgumentList
90 | $p = New-Object System.Diagnostics.Process
91 | $p.StartInfo = $pinfo
92 | $p.Start() | Out-Null
93 | $result = [pscustomobject]@{
94 | Title = ($MyInvocation.MyCommand).Name
95 | Command = $FilePath
96 | Arguments = $ArgumentList
97 | StdOut = $p.StandardOutput.ReadToEnd()
98 | StdErr = $p.StandardError.ReadToEnd()
99 | ExitCode = $p.ExitCode
100 | }
101 | $p.WaitForExit()
102 |
103 | if (-not([string]::IsNullOrEmpty($DisplayLevel))) {
104 | switch ($DisplayLevel) {
105 | "Full" { return $result; break }
106 | "StdOut" { return $result.StdOut; break }
107 | "StdErr" { return $result.StdErr; break }
108 | "ExitCode" { return $result.ExitCode; break }
109 | }
110 | }
111 | }
112 | catch {
113 | Write-Host "An error has ocurred"
114 | }
115 | }
116 | $ErrorActionPreference = "SilentlyContinue"
117 | Stop-Transcript | out-null
118 | $ErrorActionPreference = "Continue"
119 |
120 | $LogPath = $LogPath.TrimEnd('\')
121 | if (-not (Test-Path $LogPath)) {
122 | Write-Host "Log path $($LogPath) not found"
123 | Exit (1)
124 | }
125 |
126 | Start-Transcript -path "$($LogPath)\$(get-date -Format yyyy_MM_dd)_$($env:COMPUTERNAME).txt"
127 |
128 | if (-not (Test-Path $DataBase) -or (($($DataBase.Substring($DataBase.Length - 4)) -ne ".edb"))) {
129 | Write-Host "Windows Search Database not found on $($DataBase)"
130 | Stop-Transcript
131 | Exit (1)
132 | }
133 |
134 | $TempPath = $TempPath.TrimEnd('\')
135 | if (-not (Test-Path $TempPath)) {
136 | Write-Host "Temp path $($TempPath) not found"
137 | Exit (1)
138 | }
139 |
140 | $TempDataBase = $TempPath + "\tempdfrg_$(Get-Random).edb"
141 |
142 | Write-Host "Disabling Windows Search"
143 | Set-Service -Name 'wsearch' -StartupType 'Disabled'
144 | Stop-Service wsearch -Force
145 | Get-Service wsearch
146 | Write-Host "Perform defrag on $($DataBase)"
147 | $defragResult = Invoke-Process -FilePath "$([System.Environment]::SystemDirectory)\esentutl.exe" -ArgumentList "/d $($DataBase) /t $($TempDataBase)" -DisplayLevel Full
148 | if ($defragResult.ExitCode -ne 0) {
149 | Write-Host "An error has ocurred: $($defragResult.ExitCode)"
150 | $defragResult.StdOut
151 | if ($DeleteOnError) {
152 | Write-Host "Deleting database $($DataBase)"
153 | Remove-Item $DataBase -Force
154 | New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Search" -Name 'SetupCompletedSuccessfully' -Value 0 -PropertyType "DWord" -Force
155 | }
156 | }
157 | else {
158 | $defragResult.StdOut
159 | }
160 | Write-Host "Enabling Windows Search"
161 | Invoke-Process -FilePath "$([System.Environment]::SystemDirectory)\sc.exe" -ArgumentLis "config wsearch start=delayed-auto" -DisplayLevel "StdOut"
162 | Start-Service wsearch
163 | Get-Service wsearch
164 | Stop-Transcript
--------------------------------------------------------------------------------
/Defrag Windows Search Database/readme.md:
--------------------------------------------------------------------------------
1 | # Defrag Windows Search Database
2 |
3 | [Right click here and select "Save link as" to download](https://github.com/juangranados/powershell-scripts/tree/main/Defrag%20Windows%20Search%20Database/DefragWinSearchDB.ps1)
4 |
5 | Script to Defrag Windows Search Database and optionally deletes it after error.
6 |
7 | 
8 |
9 | ## Parameters
10 |
11 | ### DataBase
12 |
13 | Windows Search Database path.
14 |
15 | Default C:\ProgramData\Microsoft\Search\Data\Applications\Windows\Windows.edb
16 |
17 | ### TempPath
18 |
19 | Temporary folder to perform defrag. Using a different physical drive is recommended.
20 |
21 | Default: C:\ProgramData\Microsoft\Search\Data\Applications\Windows
22 |
23 | ### LogPath
24 |
25 | Log file path.
26 |
27 | Default: Documents.
28 |
29 | Example: "\\ES-CPD-BCK02\SearchDefrag\Log"
30 |
31 | ### DeleteOnError
32 |
33 | Deletes Windows Search Database and modify registry to rebuild it at next Search Service startup.
34 |
35 | Default: false
36 |
37 | ## Example
38 | ```powershell
39 | Defrag-WinSearchDB -LogPath "\\ES-CPD-BCK02\DefragWindDB\Log" -TempPath "\\ES-CPD-BCK02\DefragWindDB\Temp"
40 | ```
41 |
42 | ```powershell
43 | Defrag-WinSearchDB -TempPath "D:\Temp"
44 | ```
--------------------------------------------------------------------------------
/Defrag Windows Search Database/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Defrag Windows Search Database/screenshot.png
--------------------------------------------------------------------------------
/Delete Exchange Online Emails Older Than x Months/Remove-ExchangeOnlineEmails.ps1:
--------------------------------------------------------------------------------
1 | <#PSScriptInfo
2 |
3 | .VERSION 1.0
4 |
5 | .GUID 5d639597-a35a-4a95-8010-042fb21c343f
6 |
7 | .AUTHOR Juan Granados
8 |
9 | .COPYRIGHT 2022 Juan Granados
10 |
11 | .TAGS Exchange Online Remove Messages
12 |
13 | .LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
14 |
15 | .PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/Delete%20Exchange%20Online%20Emails%20Older%20Than%20x%20Months
16 |
17 | .RELEASENOTES Initial release
18 |
19 | #>
20 |
21 | <#
22 | .SYNOPSIS
23 | Delete Exchange Online emails older than x months from a mailbox.
24 | .DESCRIPTION
25 | Delete Exchange Online emails older than x months from a mailbox.
26 | Running user has to be member of eDiscovery Manager group: https://docs.microsoft.com/en-us/microsoft-365/compliance/assign-ediscovery-permissions?view=o365-worldwide
27 | in order to run New-ComplianceSearchAction.
28 | Thanks to: https://answers.microsoft.com/en-us/msoffice/forum/all/delete-more-than-10-items-from-a-mailbox-using/f28efa60-3766-4f50-af2d-e1f9be588931
29 | .PARAMETER target
30 | Mailboxto delete emails from.
31 | .PARAMETER months
32 | Emails older than this number of months will be deleted
33 | .PARAMETER logPath
34 | Path where save log file.
35 | Default: Temp folder
36 | .PARAMETER preview
37 | Only shows matching emails. It will not delete anything.
38 | .PARAMETER deleteComplianceSearch
39 | Deletes Compliance Search object after using it.
40 | .EXAMPLE
41 | Remove-ExchangeOnlineEmails.ps1 -LogPath "C:\temp\Log" -target esmith@contoso.com -months 12 -preview
42 | .EXAMPLE
43 | Remove-ExchangeOnlineEmails.ps1 -target finance@contoso.com -months 24 -deleteComplianceSearch
44 | .LINK
45 | https://github.com/juangranados/powershell-scripts/tree/main/Delete%20Exchange%20Online%20Emails%20Older%20Than%20x%20Months
46 | .NOTES
47 | Author: Juan Granados
48 | #>
49 | Param(
50 | [Parameter(Mandatory = $true)]
51 | [ValidateNotNullOrEmpty()]
52 | [string]$target,
53 | [Parameter(Mandatory = $true)]
54 | [ValidateNotNullOrEmpty()]
55 | [ValidateRange(0, 100)]
56 | [int]$months,
57 | [Parameter(Mandatory = $false)]
58 | [ValidateNotNullOrEmpty()]
59 | [string]$logPath = $env:temp,
60 | [Parameter()]
61 | [switch]$preview,
62 | [Parameter()]
63 | [switch]$deleteComplianceSearch
64 | )
65 |
66 | $ErrorActionPreference = "SilentlyContinue"
67 | Stop-Transcript | out-null
68 | $ErrorActionPreference = "Stop"
69 |
70 | function Get-StringBetweenTwoStrings([string]$firstString, [string]$secondString, [string]$string) {
71 | $pattern = "$firstString(.*?)$secondString"
72 | $result = [regex]::Match($string, $pattern).Groups[1].Value
73 | return $result
74 | }
75 | function Get-ParsedLog([string]$log) {
76 | $log = $log -replace '{' -replace '}', ','
77 | $table = New-Object system.Data.DataTable "DetailedMessageStats"
78 | $table.columns.add($(New-Object system.Data.DataColumn Location, ([string])))
79 | $table.columns.add($(New-Object system.Data.DataColumn Sender, ([string])))
80 | $table.columns.add($(New-Object system.Data.DataColumn Subject, ([string])))
81 | $table.columns.add($(New-Object system.Data.DataColumn Type, ([string])))
82 | $table.columns.add($(New-Object system.Data.DataColumn Size, ([int])))
83 | $table.columns.add($(New-Object system.Data.DataColumn ReceivedTime, ([Datetime])))
84 | $table.columns.add($(New-Object system.Data.DataColumn DataLink, ([string])))
85 | ForEach ($line in $($log -split "`r`n")) {
86 | $row = $table.NewRow()
87 | $row.Location = Get-StringBetweenTwoStrings "Location: " "; Sender:" $line
88 | $row.Sender = Get-StringBetweenTwoStrings "Sender: " "; Subject:" $line
89 | $row.Subject = Get-StringBetweenTwoStrings "Subject: " "; Type:" $line
90 | $row.Type = Get-StringBetweenTwoStrings "Type: " "; Size:" $line
91 | $row.Size = Get-StringBetweenTwoStrings "Size: " "; Received Time:" $line
92 | $row.ReceivedTime = Get-StringBetweenTwoStrings "Received Time: " "; Data Link:" $line
93 | $row.DataLink = Get-StringBetweenTwoStrings "Data Link: " "," $line
94 | $table.Rows.Add($row)
95 | }
96 | return $table
97 | }
98 |
99 | $logPath = $logPath.TrimEnd('\')
100 | if (-not (Test-Path $logPath)) {
101 | Write-Host "Log path $($logPath) not found"
102 | Exit (1)
103 | }
104 |
105 | Start-Transcript -path "$($logPath)\$(get-date -Format yyyy_MM_dd)_Remove-ExchangeOnlineEmails.txt"
106 | if (-not (Get-InstalledModule -Name ExchangeOnlineManagement)) {
107 | Write-Host "ExchangeOnlineManagement module not found! Run: Install-Module -Name ExchangeOnlineManagement"
108 | Exit
109 | }
110 | Write-Host "Checking Exchange Online and Compliance connection"
111 | $sessions = Get-PSSession | Select-Object -Property State, Name, ComputerName
112 | $exchangeOnlineConnection = (@($sessions) -like '@{State=Opened; Name=ExchangeOnlineInternalSession*; ComputerName=outlook.office365.com*').Count -gt 0
113 | $complianceConnection = (@($sessions) -like '@{State=Opened; Name=ExchangeOnlineInternalSession*; ComputerName=*compliance.protection.outlook.com*').Count -gt 0
114 |
115 | if (!$exchangeOnlineConnection) {
116 | Write-Host "Connecting Exchange Online"
117 | Connect-ExchangeOnline -ErrorAction Stop
118 | }
119 |
120 | if (-not $complianceConnection) {
121 | Write-Host "Connecting Compliance"
122 | Connect-IPPSSession -ErrorAction Stop
123 | }
124 |
125 | Write-Host "Checking target"
126 | $folderQueries = @()
127 | $folderStatistics = Get-MailboxFolderStatistics $target | where-object { ($_.FolderPath -eq "/Recoverable Items") -or ($_.FolderPath -eq "/Purges") -or ($_.FolderPath -eq "/Versions") -or ($_.FolderPath -eq "/DiscoveryHolds") }
128 | if ($folderStatistics) {
129 | foreach ($folderStatistic in $folderStatistics) {
130 | $folderId = $folderStatistic.FolderId;
131 | $folderPath = $folderStatistic.FolderPath;
132 | $encoding = [System.Text.Encoding]::GetEncoding("us-ascii")
133 | $nibbler = $encoding.GetBytes("0123456789ABCDEF");
134 | $folderIdBytes = [Convert]::FromBase64String($folderId);
135 | $indexIdBytes = New-Object byte[] 48;
136 | $indexIdIdx = 0;
137 | $folderIdBytes | Select-Object -skip 23 -First 24 | ForEach-Object { $indexIdBytes[$indexIdIdx++] = $nibbler[$_ -shr 4]; $indexIdBytes[$indexIdIdx++] = $nibbler[$_ -band 0xF] }
138 | $folderQuery = "folderid:$($encoding.GetString($indexIdBytes))";
139 | $folderStat = New-Object PSObject
140 | Add-Member -InputObject $folderStat -MemberType NoteProperty -Name FolderPath -Value $folderPath
141 | Add-Member -InputObject $folderStat -MemberType NoteProperty -Name FolderQuery -Value $folderQuery
142 | $folderQueries += $folderStat
143 | }
144 | }
145 | else {
146 | Write-Host "$target statistics not found. Exiting" -ForegroundColor Red
147 | Exit
148 | }
149 |
150 | $RecoverableItemsFolder = $folderQueries.folderquery[0]
151 | $PurgesFolder = $folderQueries.folderquery[1]
152 | $VersionsFolder = $folderQueries.folderquery[2]
153 | $DiscoveryHoldsFolder = $folderQueries.folderquery[3]
154 | $searchName = "$($target)_emails_older_than_$($months)_months"
155 | try {
156 | if (Get-ComplianceSearch -Identity $searchName -ErrorAction SilentlyContinue) {
157 | Write-Host "Compliance Search $searchName exists. Changing properties"
158 | Set-ComplianceSearch -Identity $searchName -ExchangeLocation $target -ContentMatchQuery "(Received <= $((get-date).AddMonths(-$months).ToString("MM/dd/yyy"))) AND (kind:email) AND (NOT (($RecoverableItemsFolder) OR ($PurgesFolder) OR ($VersionsFolder) OR ($DiscoveryHoldsFolder)))" -ErrorAction "Stop"
159 | }
160 | else {
161 | Write-Host "Creating Compliance Search $searchName"
162 | New-ComplianceSearch -Name $searchName -ExchangeLocation $target -ContentMatchQuery "(Received <= $((get-date).AddMonths(-$months).ToString("MM/dd/yyy"))) AND (kind:email) AND (NOT (($RecoverableItemsFolder) OR ($PurgesFolder) OR ($VersionsFolder) OR ($DiscoveryHoldsFolder)))" -ErrorAction "Stop"
163 | }
164 | Write-Host "Running Compliance Search $searchName"
165 | Start-ComplianceSearch -Identity $searchName -ErrorAction "Stop"
166 |
167 | While ((Get-ComplianceSearch -Identity $searchName -ErrorAction "Stop").status -ne "Completed") {
168 | Write-Host "." -NoNewLine
169 | Start-Sleep 5
170 | }
171 | Write-Host "."
172 | $complianceSearchResults = Get-ComplianceSearch -Identity $searchName -ErrorAction "Stop"
173 | if ($complianceSearchResults.Items -le 0) {
174 | Write-Host "Compliance Search returned 0 items"
175 | }
176 | else {
177 | if ($complianceSearchResults.ExchangeLocation.count -ne 1) {
178 | Write-Host "You have selected a Compliance Search scoped for more than 1 mailbox, please restart and select a search scoped for a single mailbox."
179 | if ($deleteComplianceSearch) {
180 | Write-Host "Deleting object $searchName"
181 | Remove-ComplianceSearch -Identity $searchName -Confirm:$false -ErrorAction "Stop"
182 | }
183 | Exit
184 | }
185 | Write-Host "Compliance Search returned $($complianceSearchResults.Items) items"
186 | if ($preview) {
187 | $searchActionName = "$($searchName)_preview"
188 | if (Get-ComplianceSearchAction -Identity $searchActionName -ErrorAction SilentlyContinue) {
189 | Write-Host "Compliance Search Action $searchActionName exists. Deleting"
190 | Remove-ComplianceSearchAction -Identity $searchActionName -Confirm:$false -ErrorAction "Stop"
191 | }
192 | Write-Host "Creating Compliance Search Action for Preview $searchActionName"
193 | New-ComplianceSearchAction -SearchName $searchName -Preview -ErrorAction "Stop" | Out-Null
194 |
195 | Write-Host "Waiting for Compliance Search Action to finish"
196 | While ((Get-ComplianceSearchAction -Identity $searchActionName -ErrorAction "Stop").status -ne "Completed") {
197 | Write-Host "." -NoNewline
198 | Start-Sleep 5
199 | }
200 | Write-Host "."
201 | $complianceSearchActionResult = Get-ParsedLog (Get-ComplianceSearchAction $searchActionName -Details).Results
202 | $complianceSearchActionResult | Out-GridView -Title "Compliance Search Preview"
203 | }
204 | else {
205 | $searchActionName = "$($searchName)_purge"
206 | [int]$batches = [math]::floor($complianceSearchResults.Items / 10)
207 | if (Get-ComplianceSearchAction -Identity $searchActionName -ErrorAction SilentlyContinue) {
208 | Write-Host "Compliance Search Action $searchActionName exists. Deleting"
209 | Remove-ComplianceSearchAction -Identity $searchActionName -Confirm:$false -ErrorAction "Stop" | Out-Null
210 | }
211 | for ($batch = 1; $batch -le $batches; $batch++) {
212 | Write-Host "Batch $batch of $batches" -ForegroundColor Cyan
213 | Write-Host "Creating Compliance Search Action for Deletion $searchActionName"
214 | $repeat = $true
215 | $i = 1
216 | while ($repeat) {
217 | try {
218 | New-ComplianceSearchAction -SearchName $searchName -Purge -PurgeType HardDelete -Confirm:$false -ErrorAction "Stop" | Out-Null
219 | $repeat = $false
220 | }
221 | catch {
222 | Write-Host "Error trying to create Compliance Search Action. Waiting 5 seconds until next try. Try $i of 5"
223 | Start-Sleep -Seconds 5
224 | Remove-ComplianceSearchAction -Identity $searchActionName -Confirm:$false -ErrorAction "SilentlyContinue" | Out-Null
225 | if ($i -lt 6) {
226 | $i++
227 | }
228 | else {
229 | Write-Host "Cannot create new Compliance Search Action" -ForegroundColor Red
230 | if ($deleteComplianceSearch) {
231 | Write-Host "Deleting object $searchName"
232 | Remove-ComplianceSearch -Identity $searchName -Confirm:$false -ErrorAction "Stop"
233 | }
234 | Exit
235 | }
236 | }
237 | }
238 | $complianceSearchActionStatus = (Get-ComplianceSearchAction -Identity $searchActionName).status
239 | Write-Host "Waiting for Compliance Search Action to finish"
240 | do {
241 | Write-Host "." -NoNewline
242 | Start-Sleep 5
243 | $complianceSearchActionStatus = (Get-ComplianceSearchAction -Identity $searchActionName).status
244 | } while ($complianceSearchActionStatus -ne "Completed")
245 | Write-Host "."
246 | Write-Host "10 items deleted. $($complianceSearchResults.Items - (10 * $batch)) remaining" -ForegroundColor Green
247 | Write-Host "Deleting Compliance Search Action $searchActionName"
248 | Remove-ComplianceSearchAction -Identity $searchActionName -Confirm:$false | Out-Null
249 | }
250 | }
251 | }
252 |
253 | if ($deleteComplianceSearch) {
254 | Write-Host "Deleting object $searchName"
255 | Remove-ComplianceSearch -Identity $searchName -Confirm:$false -ErrorAction "Stop"
256 | }
257 | }
258 | catch {
259 | Write-Error "An error occurred: $_"
260 | }
261 | Stop-Transcript
262 |
--------------------------------------------------------------------------------
/Delete Windows 10 Preinstalled Apps/readme.md:
--------------------------------------------------------------------------------
1 | # Delete Windows 10 Preinstalled Apps
2 |
3 | Windows 10 includes a variety of universal apps like Candy Crush, Facebook, ect. All of these can be uninstalled with a single PowerShell Command.
4 |
5 | This command uninstalls all built-in apps except Windows Store, Calculator and Photos.
6 |
7 | ## Deletes current user apps
8 |
9 | ```powershell
10 | Get-AppxPackage | where-object {$_.name -notlike "*Microsoft.WindowsStore*"} | where-object {$_.name -notlike "*Microsoft.WindowsCalculator*"} | where-object {$_.name -notlike "*Microsoft.Windows.Photos*"} | Remove-AppxPackage
11 | ```
12 | ## Deletes all users apps (current and new).
13 |
14 | Run PowerShell console as administrator.
15 |
16 | ```powershell
17 | Get-AppxPackage -AllUsers | where-object {$_.name -notlike "*Microsoft.WindowsStore*"} | where-object {$_.name -notlike "*Microsoft.WindowsCalculator*"} | where-object {$_.name -notlike "*Microsoft.Windows.Photos*"} | Remove-AppxPackage
18 |
19 | Get-AppxProvisionedPackage -online | where-object {$_.name -notlike "*Microsoft.WindowsStore*"} | where-object {$_.name -notlike "*Microsoft.WindowsCalculator*"} | where-object {$_.name -notlike "*Microsoft.Windows.Photos*"} | Remove-AppxProvisionedPackage -online
20 | ```
--------------------------------------------------------------------------------
/Delete reappearing printers that keeps comming back/DeletePrintersRDS.cmd:
--------------------------------------------------------------------------------
1 | Psexec.exe -s -i -accepteula powershell.exe -Command (Remove-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Enum\SWD\PRINTENUM\*' -Recurse -Force)
2 | Psexec.exe -s -i -accepteula powershell.exe -Command (Remove-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceClasses\{0ecef634-6ef0-472a-8085-5ad023ecbccd}\*' -Recurse -Force)
3 | Psexec.exe -s -i -accepteula powershell.exe -Command (Remove-Item -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Providers\Client Side Rendering Print Provider\*' -Recurse -Force)
4 | Psexec.exe -s -i -accepteula powershell.exe -Command (Remove-Item -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\*' -Recurse -Force)
5 | Psexec.exe -s -i -accepteula powershell.exe -Command (Remove-Item -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\V4 Connections\*' -Recurse -Force)
6 | @echo off
7 | set /p r= Reboot computer? [y/n]
8 | if %r% == y goto reboot
9 | exit
10 | :reboot
11 | shutdown -r -f -t 0
--------------------------------------------------------------------------------
/Delete reappearing printers that keeps comming back/printers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Delete reappearing printers that keeps comming back/printers.png
--------------------------------------------------------------------------------
/Delete reappearing printers that keeps comming back/readme.md:
--------------------------------------------------------------------------------
1 | # Windows Server Backup Email Report of Several Servers
2 |
3 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Delete%20reappearing%20printers%20that%20keeps%20comming%20back/DeletePrintersRDS.cmd)
4 |
5 | Script to delete ghost printers that keeps coming back after deletion.
6 |
7 | **Warning: this script will delete all printers.**
8 |
9 | You need to add psexec to system path. [Download PsTools](https://docs.microsoft.com/en-us/sysinternals/downloads/pstools) and extract it to C:\Windows\System32. Please, take a registry backup first!
10 |
11 | *Example of undeletable printers on a RDS server*
12 |
13 | 
14 |
15 |
--------------------------------------------------------------------------------
/Email Report of File Permissions on HTML and CSV/Get-FolderPermissions.ps1:
--------------------------------------------------------------------------------
1 | <#PSScriptInfo
2 |
3 | .VERSION 1.0
4 |
5 | .GUID 1069276e-50b4-414a-ae8c-b8801445ae7e
6 |
7 | .AUTHOR Juan Granados
8 |
9 | .COPYRIGHT 2021 Juan Granados
10 |
11 | .TAGS Folder Permission Report HTML CSV email mail
12 |
13 | .LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
14 |
15 | .PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/Email%20Report%20of%20File%20Permissions%20on%20HTML%20and%20CSV
16 |
17 | .RELEASENOTES
18 | Initial release
19 | #>
20 |
21 | <#
22 | .SYNOPSIS
23 | Generate a folders permissions report.
24 | .DESCRIPTION
25 | Starting with a root folder, it generates a folders permissions report. Number of subfolders examined depends on FolderDeep parameter.
26 | Report is generated in CSV format and can be send attached via mail with a html report in the body.
27 | .PARAMETER OutFile
28 | Path to store CSV file.
29 | Default .\Permissions.csv
30 | .PARAMETER RootPath
31 | Folder to start checking permissions.
32 | .PARAMETER FolderDeep
33 | Number of subfolders levels to check.
34 | Default 99.
35 | .PARAMETER ObjectsIgnored
36 | Users or groups to ignore in report.
37 | Default NT AUTHORITY\SYSTEM,BUILTIN\Administrator
38 | .PARAMETER InspectGroups
39 | List only users in report.
40 | Default $False
41 | .PARAMETER SMTPServer
42 | Sets smtp server in order to sent an email with backup result. If leave blank, no email will be send.
43 | .PARAMETER SMTPRecipient
44 | List of emails addresses which will receive the backup result separated by commas.
45 | .PARAMETER SMTPSender
46 | Email address which will send the backup result.
47 | .PARAMETER SMTPUser
48 | Username in case of smtp server requires authentication.
49 | .PARAMETER SMTPPassword
50 | Password in case of smtp server requires authentication.
51 | .PARAMETER SMTPSSL
52 | Use of SSL in case of smtp server requires SSL.
53 | Default: $False
54 | .PARAMETER SMTPPort
55 | Port to connect to smtp server.
56 | Default: 25
57 | .EXAMPLE
58 | Get-FoldersPermissions -RootPath "D:\Data\Departments" -FolderDeep 2 -SMTPServer "mail.server.com" -SMTPRecipient "megaboss@server.com","support@server.com" -SMTPSender "reports@server.com"
59 | .LINK
60 | https://github.com/juangranados/powershell-scripts/tree/main/Email%20Report%20of%20File%20Permissions%20on%20HTML%20and%20CSV
61 | .NOTES
62 | Author: Juan Granados
63 | #>
64 | [cmdletbinding()]
65 |
66 | param(
67 | [parameter(Position=0,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Path to store CSV file')][string]$OutFile = ".\$(Get-Date -format "yyyyMMdd_hhmmss")-Permissions.csv",
68 | [parameter(Position=1,Mandatory=$true,ValueFromPipeline=$false,HelpMessage='Folder to start checking permissions')][string]$RootPath,
69 | [parameter(Position=2,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Number of subfolders levels to check')][string]$FolderDeep = 99,
70 | [parameter(Position=3,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Users or groups to ignore in report')][string[]]$ObjectsIgnored = @("NT AUTHORITY\SYSTEM","BUILTIN\Administrator"),
71 | [parameter(Position=4,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Inspect users in groups ($True/$False)')][bool]$InspectGroups=$false,
72 | [parameter(Position=5,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Mail From')][string]$SMTPSender,
73 | [parameter(Position=6,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Mail To')]$SMTPRecipient,
74 | [parameter(Position=7,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Mail Server')][string]$SMTPServer,
75 | [parameter(Position=8,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Mail User')][string]$SMTPUser,
76 | [parameter(Position=9,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Mail Password')][string]$SMTPPassword,
77 | [parameter(Position=10,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Mail Port')][string]$SMTPPort=25,
78 | [parameter(Position=11,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Use SSL in mail sending ($True/$False)')][bool]$SMTPSSL=$False,
79 | [parameter(Position=12,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Mail Subject')][string]$SMTPSubject="Permission report on server $($env:computername) on directory $($RootPath) with $($FolderDeep) level deep"
80 | )
81 | Function Get-FolderPermissions($Folder,[int]$Deep = 0){
82 | # Write current folder name
83 | Write-Host "Examining folder $($Folder.FullName)"
84 |
85 | # Get folder ACLs
86 | $ACLs = get-acl $Folder.fullname | ForEach-Object {$_.Access}
87 | # Examining folder ACLs
88 | Foreach ($ACL in $ACLs){
89 | # If current ACL contains one of object ignored list, skip ACL
90 | if (-not ($ObjectsIgnored.Contains($ACL.IdentityReference.value.toString()))){
91 | # Delete commas in folder name and ACL InheritanceFlags, FileSystemRights
92 | $FolderName = $($Folder.Fullname -replace ',','.')
93 | $ACLInheritanceFlags = $($ACL.InheritanceFlags -replace ',',' &')
94 | $ACLFileSystemRights = $($ACL.FileSystemRights -replace ',',' &')
95 | # If inspect groups is true, list group users.
96 | if ($InspectGroups){
97 | # Check if ACL identity reference is a group
98 | if (-not ($ACL.IdentityReference.value -like "BUILTIN\*") -and ($ACL.IdentityReference.Value.LastIndexOf('\') -ne -1)){
99 | # Get group users
100 | $ADGroup = Get-ADGroup -LDAPFilter "(SAMAccountName=$($ACL.IdentityReference.Value.Substring($ACL.IdentityReference.Value.LastIndexOf('\')+1)))" -ErrorAction SilentlyContinue
101 | # If group has users
102 | if ($ADGroup){
103 | # Get group users
104 | $users = Get-ADGroupMember -identity $ADGroup -Recursive | Get-ADUser -Property DisplayName
105 | # Store users ACL information
106 | ForEach($User in $users){
107 | # Store user info in csv file
108 | $OutInfo = $FolderName + "," + $User.UserPrincipalName + "," + $ACL.AccessControlType + "," + $ACLFileSystemRights + "," + $ACL.IsInherited + "," + $ACLInheritanceFlags + "," + $ACL.PropagationFlags
109 | Add-Content -Value $OutInfo -Path $OutFile
110 | # Store user info in table
111 | $Rows+="
" + $Folder.Fullname + "
" + "
" + $User.UserPrincipalName + "
" + "
" + $ACL.AccessControlType + "
" + "
" + $ACL.FileSystemRights + "
" + "
" + $ACL.IsInherited + "
" + "
" + $ACL.InheritanceFlags + "
" + "
" + $ACL.PropagationFlags + "
`r"
112 | }
113 | # Next loop
114 | continue
115 | }
116 | }
117 | }
118 | # Inspect groups is false or ACL identity reference is a group but users could not be retrieved
119 | # Store object info in csv file
120 | $OutInfo = $FolderName + "," + $ACL.IdentityReference + "," + $ACL.AccessControlType + "," + $ACLFileSystemRights + "," + $ACL.IsInherited + "," + $ACLInheritanceFlags + "," + $ACL.PropagationFlags
121 | Add-Content -Value $OutInfo -Path $OutFile
122 | # Store object info in table
123 | $Rows+="
" + $Folder.Fullname + "
" + "
" + $ACL.IdentityReference + "
" + "
" + $ACL.AccessControlType + "
" + "
" + $ACL.FileSystemRights + "
" + "
" + $ACL.IsInherited + "
" + "
" + $ACL.InheritanceFlags + "
" + "
" + $ACL.PropagationFlags + "
`r"
124 |
125 | }
126 | }
127 | # Get subfolders
128 | $Folders = Get-ChildItem $Folder.FullName -dir -ErrorAction SilentlyContinue
129 | # If there any folders and we want to inspect more folders
130 | If ($Folders -and $Deep){
131 | # Examining new folders
132 | ForEach ($F in $Folders){
133 | # Add results to table
134 | $Rows += $(Get-FolderPermissions $F $($Deep - 1) )
135 | }
136 | }
137 | # Return table
138 | return $Rows
139 | }
140 |
141 | # Variable initialization
142 | # Set csv header
143 | $Header = "Folder Path,Identity Reference,Access Control Type,File System Rights,Is Inherited,Inheritance Flags,Propagation Flags"
144 | # Delete csv file
145 | Del $OutFile -ErrorAction "SilentlyContinue"
146 | # Add header to csv
147 | Add-Content -Value $Header -Path $OutFile
148 | # Clear html table variable
149 | if ([boolean](get-variable "Rows" -ErrorAction SilentlyContinue))
150 | {Clear-Variable -Name "Rows" -Scope Global}
151 |
152 | #Generate table and csv
153 | $Table = Get-FolderPermissions $(Get-Item $RootPath) $FolderDeep
154 |
155 | #Create HTML body
156 | $HTMLFile = @"
157 |
158 |
159 |
160 |
161 |
165 |
166 |
167 |
$SMTPSubject
168 |
169 |
170 |
Folder Path
171 |
Identity Reference
172 |
Access Control Type
173 |
File System Rights
174 |
Is Inherited
175 |
Inheritance Flags
176 |
Propagation Flags
177 |
178 | $Table
179 |
180 |
181 |
182 | "@
183 | #Send mail
184 | If ($SMTPServer)
185 | {
186 | # Set smpt password
187 | $SecureSMTPPassword = ConvertTo-SecureString $SMTPPassword -AsPlainText -Force
188 | $SMTPCredential = New-Object System.Management.Automation.PSCredential($SMTPUser,$SecureSMTPPassword)
189 | Write-Host "Sending Email"
190 | if ($SMTPSSL){
191 | Send-MailMessage -To $SMTPRecipient -From $SMTPSender -Attachments $OutFile -SmtpServer $SMTPServer -Subject $SMTPSubject -UseSsl -Port $SMTPPort -Credential $SMTPCredential -BodyAsHtml -Body $HTMLFile -Encoding ([System.Text.Encoding]::utf8)
192 | }
193 | else{
194 | Send-MailMessage -To $SMTPRecipient -From $SMTPSender -Attachments $OutFile -SmtpServer $SMTPServer -Subject $SMTPSubject -Port $SMTPPort -Credential $SMTPCredential -BodyAsHtml -Body $HTMLFile -Encoding ([System.Text.Encoding]::utf8)
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/Email Report of File Permissions on HTML and CSV/readme.md:
--------------------------------------------------------------------------------
1 | # Email Report of File Permissions on HTML and CSV
2 |
3 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Email%20Report%20of%20File%20Permissions%20on%20HTML%20and%20CSV/Get-FolderPermissions.ps1)
4 |
5 | Starting with a root folder, it generates a folders permissions report. Number of sub folders examined depends on FolderDeep parameter.
6 |
7 | Report is generated in CSV format and can be send attached via mail with a HTML report in the body.
8 |
9 | 
10 |
11 | ```powershell
12 | <#
13 | .SYNOPSIS
14 | Generate a folders permissions report.
15 | .DESCRIPTION
16 | Starting with a root folder, it generates a folders permissions report. Number of subfolders examined depends on FolderDeep parameter.
17 | Report is generated in CSV format and can be send attached via mail with a html report in the body.
18 | .PARAMETER OutFile
19 | Path to store CSV file.
20 | Default .\Permissions.csv
21 | .PARAMETER RootPath
22 | Folder to start checking permissions.
23 | .PARAMETER FolderDeep
24 | Number of subfolders levels to check.
25 | Default 99.
26 | .PARAMETER ObjectsIgnored
27 | Users or groups to ignore in report.
28 | Default NT AUTHORITY\SYSTEM,BUILTIN\Administrator
29 | .PARAMETER InspectGroups
30 | List only users in report.
31 | Default $False
32 | .PARAMETER SMTPServer
33 | Sets smtp server in order to sent an email with backup result. If leave blank, no email will be send.
34 | .PARAMETER SMTPRecipient
35 | List of emails addresses which will receive the backup result separated by commas.
36 | .PARAMETER SMTPSender
37 | Email address which will send the backup result.
38 | .PARAMETER SMTPUser
39 | Username in case of smtp server requires authentication.
40 | .PARAMETER SMTPPassword
41 | Password in case of smtp server requires authentication.
42 | .PARAMETER SMTPSSL
43 | Use of SSL in case of smtp server requires SSL.
44 | Default: $False
45 | .PARAMETER SMTPPort
46 | Port to connect to smtp server.
47 | Default: 25
48 | .EXAMPLE
49 | Get-FoldersPermissions -RootPath "D:\Data\Departments" -FolderDeep 2 -SMTPServer "mail.server.com" -SMTPRecipient "megaboss@server.com","support@server.com" -SMTPSender "reports@server.com"
50 | .NOTES
51 | Author: Juan Granados
52 | #>
53 | ```
54 |
55 |
--------------------------------------------------------------------------------
/Email Report of File Permissions on HTML and CSV/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Email Report of File Permissions on HTML and CSV/screenshot.png
--------------------------------------------------------------------------------
/File Server Access Audit Report with PowerShell/1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/File Server Access Audit Report with PowerShell/1.PNG
--------------------------------------------------------------------------------
/File Server Access Audit Report with PowerShell/2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/File Server Access Audit Report with PowerShell/2.PNG
--------------------------------------------------------------------------------
/File Server Access Audit Report with PowerShell/5.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/File Server Access Audit Report with PowerShell/5.PNG
--------------------------------------------------------------------------------
/File Server Access Audit Report with PowerShell/6.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/File Server Access Audit Report with PowerShell/6.PNG
--------------------------------------------------------------------------------
/File Server Access Audit Report with PowerShell/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/File Server Access Audit Report with PowerShell/7.png
--------------------------------------------------------------------------------
/File Server Access Audit Report with PowerShell/readme.md:
--------------------------------------------------------------------------------
1 | # File Server Access Audit Report with PowerShell
2 |
3 | Right click here and select "Save link as" to download
4 |
5 | This PowerShell script allows to audit several file servers and send a report in CSV and HTML by mail.
6 |
7 | CSV file can be import on Excel to generate a File Audit Report.
8 |
9 | HTML report can filter and sorting rows by server, time, user, file or operation (read, delete or write).
10 |
11 | 
12 |
13 | ## How to configure auditing in Windows Server
14 |
15 | ### Enable audit in Windows Server
16 |
17 | From local policy or group policy, navigate to Computer Configuration → Policies → Windows Settings → Security Settings → Local Policies → Audit Policy → Open Audit object access and select Success and Failure
18 |
19 | 
20 |
21 | ### Enable audit in folder and subfolders
22 |
23 | Right click on folder →Properties → Security → Advanced → Auditing → Add
24 |
25 | 
26 |
27 | Select domain users or user group and mark:
28 |
29 | - Type: All.
30 |
31 | - Basic Permissions: Read & execute, List folder contents, Read.
32 |
33 | 
34 |
35 | ### Increase Security Log size
36 |
37 | From local policy or group policy, browse to Computer Configuration → Policies → Administrative Templates → Windows Components → Event Log Service → Security → Specify the maximum log file size
38 | Set the maximum log file size setting, for example 4194240 KB (4 GB).
39 |
40 | 
41 |
--------------------------------------------------------------------------------
/GUI Password Reset Tool for Active Directory/GUI.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/GUI Password Reset Tool for Active Directory/GUI.PNG
--------------------------------------------------------------------------------
/GUI Password Reset Tool for Active Directory/Set-ADPassword.ps1:
--------------------------------------------------------------------------------
1 | <#PSScriptInfo
2 |
3 | .VERSION 1.0
4 |
5 | .GUID 4111a7cc-46b4-4648-8b8b-e7743bd61b86
6 |
7 | .AUTHOR Juan Granados
8 |
9 | .COPYRIGHT 2021 Juan Granados
10 |
11 | .TAGS AD Active Directory Reset Password GUI
12 |
13 | .LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
14 |
15 | .PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/GUI%20Password%20Reset%20Tool%20for%20Active%20Directory
16 |
17 | .RELEASENOTES
18 | Initial release
19 |
20 | #>
21 |
22 | <#
23 | .DESCRIPTION
24 | Powershell script for IT support team which allow to reset AD users password in a GUI interface.
25 | Requirements:
26 | - PowerShell 4 (Windows Management Framework 4) o highter.
27 | - Support users must be able to reset AD users password:
28 | https://community.spiceworks.com/how_to/1464-how-to-delegate-password-reset-permissions-for-your-it-staff
29 | - Support computers must install RSAT (Remote Server Administration Tools):
30 | dism /Add-Capability /CapabilityName:$((Get-WindowsCapability -Online -Name "*Rsat.ActiveDirectory*").Name) /Online
31 | .NOTES
32 | Author: Juan Granados
33 | Thanks to: https://foxdeploy.com/2015/04/10/part-i-creating-powershell-guis-in-minutes-using-visual-studio-a-new-hope/
34 | #>
35 | $inputXML = @"
36 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
59 |
69 |
70 |
71 |
72 | "@
73 |
74 | $inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^Right click here and select "Save link as" to download
4 |
5 | Powershell script for IT support team which allow to reset AD users password in a GUI interface.
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Install JRE/Download-JRE.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Download Java Runtime Environment (jre) latest version for Windows for later installation.
4 | .DESCRIPTION
5 | This script Download Java Runtime Environment 32 and 64 bit (jre) latest version for Windows for later installation.
6 | .PARAMETER DownloadPath
7 | Path to download both JRE installers: 32 and 64 bit.
8 | Example: D:\Software\JRE
9 | .PARAMETER LogPath
10 | Log path (optional).
11 | Example: D:\Software\JRE\Log
12 | .NOTES
13 | Author: Juan Granados
14 | Date: April 2022
15 | #>
16 | Param(
17 | [Parameter(Mandatory = $true, Position = 0)]
18 | [ValidateNotNullOrEmpty()]
19 | [string]$downloadPath,
20 | [Parameter(Mandatory = $false, Position = 1)]
21 | [ValidateNotNullOrEmpty()]
22 | [string]$logPath
23 | )
24 | #Requires -RunAsAdministrator
25 | if (-not [string]::IsNullOrWhiteSpace($logPath) -and $logPath.Chars($logPath.Length - 1) -eq '\') {
26 | $logPath = ($logPath.TrimEnd('\'))
27 | }
28 | if ($downloadPath.Chars($downloadPath.Length - 1) -eq '\') {
29 | $downloadPath = ($downloadPath.TrimEnd('\'))
30 | }
31 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) {
32 | Start-Transcript -Path "$($logPath)\$($env:COMPUTERNAME)_downloadJRE.log" | Out-Null
33 | }
34 | Write-Host "Surfing https://www.java.com/en/download/manual.jsp"
35 |
36 | $URL = "https://www.java.com/en/download/manual.jsp"
37 | $global:ie = New-Object -com "InternetExplorer.Application"
38 | $global:ie.visible = $false
39 | $global:ie.Navigate($URL)
40 |
41 | do {
42 | Start-Sleep -s 1
43 | } until(!($global:ie.Busy))
44 | $global:doc = $global:ie.Document
45 |
46 | $links = @($global:doc.links)
47 | $ProgressPreference = 'SilentlyContinue'
48 | $link = $links | Where-Object { $_.href -like "http*" } | Where-Object { $_.title -like "Download Java software for Windows (64-bit)" }
49 | Write-Host "Downloading JRE x64"
50 | Invoke-WebRequest $link[0].href -OutFile $downloadPath\jrex64.exe
51 | $link = $links | Where-Object { $_.href -like "http*" } | Where-Object { $_.title -like "Download Java software for Windows Offline" }
52 | Write-Host "Downloading JRE x32"
53 | Invoke-WebRequest $link[0].href -OutFile $downloadPath\jrex32.exe
54 |
55 | if (-not [string]::IsNullOrWhiteSpace($logPath)) {
56 | Stop-Transcript
57 | }
--------------------------------------------------------------------------------
/Install JRE/Install-JRE.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Install Java Runtime Environment (jre) for Windows if previous or no version detected and uninstall previous versions.
4 | .DESCRIPTION
5 | This script install Java Runtime Environment only if a previous version or no version was detected.
6 | You can avoid uninstall old versions with -NoUninstall switch.
7 | .PARAMETER InstallPath
8 | Java Runtime Environment full installer path. It could be download from https://www.java.com/en/download/manual.jsp.
9 | Authenticated users must have read permissions over shared folder.
10 | Example: \\FILESERVER-01\Skype\jre-10.0.2_windows-x64_bin.exe
11 | .PARAMETER LogPath
12 | Log path (optional). ComputerName.log file will be created.
13 | Authenticated users must have write permissions over log shared folder.
14 | Example: \\FILESERVER-01\JRE\Logs (Log will be saved to \\FILESERVER-01\JRE\computername.log)
15 | .PARAMETER NoUninstall
16 | Default: false.
17 | Avoid uninstall previous versions before install Java Runtime Environment.
18 | .PARAMETER x64
19 | Default: false.
20 | 64 bit version is selected for installing: InstallPath contains a 64 Bit JRE Installer.
21 | .EXAMPLE
22 | Install 64 bit Java Runtime Environment from network share, saving log in Log folder of network share and uninstall previous versions.
23 | Note: network share must have read permissions on "\\FILESERVER-01\JRE\" and write on "\\FILESERVER-01\JRE\Logs" for "Authenticated Users" group.
24 | InstallJRE.ps1 "\\FILESERVER-01\JRE\jre-10.0.2_windows-x64_bin.exe" "\\FILESERVER-01\JRE\Logs" -x64
25 | .EXAMPLE
26 | Install 32 bit Java Runtime Environment from network share, saving log in Log folder of network share and uninstall previous versions.
27 | Note: network share must have read permissions on "\\FILESERVER-01\JRE\" and write on "\\FILESERVER-01\JRE\Logs" for "Authenticated Users" group.
28 | InstallJRE.ps1 "\\FILESERVER-01\JRE\jre-8u181-windows-i586.exe" "\\FILESERVER-01\JRE\Logs"
29 | .NOTES
30 | Author: Juan Granados
31 | Date: April 2022
32 | #>
33 | Param(
34 | [Parameter(Mandatory = $true, Position = 0)]
35 | [ValidateNotNullOrEmpty()]
36 | [string]$InstallPath,
37 | [Parameter(Mandatory = $false, Position = 1)]
38 | [ValidateNotNullOrEmpty()]
39 | [string]$LogPath,
40 | [Parameter(Mandatory = $false, Position = 2)]
41 | [ValidateNotNullOrEmpty()]
42 | [switch]$NoUninstall,
43 | [Parameter(Mandatory = $false, Position = 3)]
44 | [ValidateNotNullOrEmpty()]
45 | [switch]$x64
46 | )
47 | #Requires -RunAsAdministrator
48 | function Get-InstalledApps {
49 | if ([IntPtr]::Size -eq 4) {
50 | $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
51 | }
52 | else {
53 | $regpath = @(
54 | 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
55 | 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
56 | )
57 | }
58 | Get-ItemProperty $regpath | . { process { if ($_.DisplayName -and $_.UninstallString) { $_ } } } |
59 | Select-Object DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |
60 | Sort-Object DisplayVersion
61 | }
62 |
63 | function StringVersionToFloat($version) {
64 | while (($version.ToCharArray() | Where-Object { $_ -eq '.' } | Measure-Object).Count -gt 1) {
65 | $aux = $version.Substring($version.LastIndexOf('.') + 1)
66 | $version = $version.Substring(0, $version.LastIndexOf('.')) + $aux
67 | }
68 | return [float]$version
69 | }
70 |
71 | if (-not [string]::IsNullOrWhiteSpace($LogPath) -and $logPath.Chars($logPath.Length - 1) -eq '\') {
72 | $logPath = ($logPath.TrimEnd('\'))
73 | }
74 |
75 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) {
76 | if ($x64) {
77 | Start-Transcript -Path "$($LogPath)\$($env:COMPUTERNAME)_x64.log" | Out-Null
78 | }
79 | else {
80 | Start-Transcript -Path "$($LogPath)\$($env:COMPUTERNAME).log" | Out-Null
81 | }
82 | }
83 |
84 | if (-not [Environment]::Is64BitOperatingSystem -and $x64) {
85 | Write-Error "Error: 64 Bit version can not be installed on 32 Bit Operating System."
86 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) { Stop-Transcript }
87 | Exit 1
88 | }
89 |
90 | $ErrorActionPreference = "Stop"
91 | $Install = $true
92 |
93 | try {
94 | $jreExeFile = Get-Item -Path $InstallPath
95 | }
96 | catch {
97 | Write-Error "Error accessing $($InstallPath)."
98 | Write-Error "$($Error[0])"
99 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) { Stop-Transcript }
100 | Exit 1
101 | }
102 |
103 | $jreExeVersion = StringVersionToFloat $jreExeFile.VersionInfo.ProductVersion
104 |
105 | Write-Host "JRE $($jreExeVersion) selected for installation."
106 |
107 | if ($x64) {
108 | $jreInstalledVersion = Get-InstalledApps | Where-Object { $_.DisplayName -like '*Java*(64-bit)' }
109 | }
110 | else {
111 | $jreInstalledVersion = Get-InstalledApps | Where-Object { $_.DisplayName -like '*Java*' -and $_.DisplayName -notlike '*Java*(64-bit)' }
112 | }
113 |
114 | if ($jreInstalledVersion) {
115 |
116 | foreach ($installation in $jreInstalledVersion) {
117 |
118 | $version = StringVersionToFloat $installation.DisplayVersion
119 |
120 | if (($version -lt $jreExeVersion)) {
121 | Write-Host "JRE $($version) detected."
122 | if (-not $NoUninstall) {
123 | Write-Host "Unnistalling JRE $($version)."
124 | if ($installation.UninstallString -like '*msiexec*') {
125 | Start-Process -FilePath cmd.exe -ArgumentList '/c', $installation.UninstallString, '/qn /norestart' -Wait
126 | }
127 | else {
128 | Start-Process -FilePath cmd.exe -ArgumentList '/c', $installation.UninstallString, '/verysilent' -Wait
129 | }
130 | }
131 | }
132 | else {
133 | Write-Host "JRE $($version) or greater already installed."
134 | $Install = $false
135 | }
136 | }
137 | }
138 | else {
139 | Write-Host "No JRE version detected."
140 | }
141 |
142 | if ($Install) {
143 | Write-Host "Starting installation of JRE $($jreExeVersion) and exiting."
144 | Start-Process $InstallPath -ArgumentList "/s SPONSORS=0" -Wait -PassThru | Wait-Process -Timeout 200
145 | }
146 |
147 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) { Stop-Transcript }
--------------------------------------------------------------------------------
/Install JRE/readme.md:
--------------------------------------------------------------------------------
1 | # Install or update Java Runtime Environment 32 or 64 bits on domain computers using GPO and uninstall old versions.
2 |
3 | Install Java Runtime Environment (JRE) if previous or no version was detected and uninstall previous versions for security reasons.
4 |
5 | This script install Java only if a previous version or no version was detected.
6 |
7 | You can skip uninstallation of old versions with -NoUninstall switch.
8 |
9 | [Install-JRE](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Install%20JRE/Install-JRE.ps1): to install latest JRE on Windows Computers with GPO.
10 |
11 | [Download-JRE](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Install%20JRE/Download-JRE.ps1): to run scheduled task on server in order to keep JRE updated in file share.
12 |
13 | ## Instructions
14 |
15 | ### Download and update daily
16 |
17 | To download and keep Java Runtime Environment updated
18 |
19 | Create a scheduled task on server that nightly executes 'Download-JRE.ps1' to your server path.
20 |
21 | ```powershell
22 | Download-JRE.ps1 -downloadPath "D:\Software\JRE" -logPath "C:\Logs"
23 | ```
24 | Will download jrex64.exe and jrex32.exe to D:\Software\JRE
25 |
26 | ### Instructions for 64 bit installation
27 |
28 | 1. Create shared folder to deploy Java Runtime Environment: ```\\FILESERVER-01\JRE```
29 | 2. Grant 'Authenticated users' read access to ```\\FILESERVER-01\JRE```
30 | 3. Copy ```Install-JRE.ps1``` to ```\\FILESERVER-01\JRE\Install-JRE.ps1```
31 | 4. Create shared folder for logs: ```\\FILESERVER-01\JRE\logs```
32 | 5. Grant 'Authenticated users' write access to ```\\FILESERVER-01\JRE\logs```
33 | 6. Create a computer GPO that runs PowerShell Script:
34 | ```
35 | Name: \\FILESERVER-01\JRE\Install-JRE.ps1
36 | Parameters: \\FILESERVER-01\JRE\jrex64.exe \\FILESERVER-01\JRE\logs -x64
37 | ```
38 |
39 | ### Instructions for 32 bit installation
40 |
41 | 1. Create shared folder to deploy Java Runtime Environment: ```\\FILESERVER-01\JRE```
42 | 2. Grant 'Authenticated users' read access to ```\\FILESERVER-01\JRE```
43 | 3. Copy ```Install-JRE.ps1``` to ```\\FILESERVER-01\JRE\Install-JRE.ps1```
44 | 4. Create shared folder for logs: ```\\FILESERVER-01\JRE\logs```
45 | 5. Grant 'Authenticated users' ```write access to \\FILESERVER-01\JRE\logs```
46 | 6. Create a computer GPO that runs PowerShell Script:
47 | ```
48 | Name: \\FILESERVER-01\JRE\Install-JRE.ps1
49 | Parameters: \\FILESERVER-01\JRE\jrex32.exe \\FILESERVER-01\JRE\logs
50 | ```
51 |
52 | ## Parameters
53 |
54 | ### Install-JRE
55 |
56 | **InstallPath:** JRE installer path.
57 |
58 | **LogPath:** Path where save log file.
59 |
60 | **NoUninstall:** Will not uninstall JRE previous versions.
61 |
62 | **x64:** Install JRE 64 bits version. By default 32 bits version is installed.
63 |
64 | ```powershell
65 | # Install JRE 32 bit
66 | Install-JRE.ps1 -InstallPath \\FS01\JRE\jrex32.exe -LogPath \\FS01\JRE\Logs
67 | ```
68 | ```powershell
69 | # Install JRE 32 bit without uninstall previous versions
70 | Install-JRE.ps1 -InstallPath \\FS01\JRE\jrex32.exe -LogPath \\FS01\JRE\Logs -NoUninstall
71 | ```
72 | ```powershell
73 | # Install JRE 64 bit
74 | Install-JRE.ps1 -InstallPath \\FS01\JRE\jrex32.exe -LogPath \\FS01\JRE\Logs -x64
75 | ```
76 | ```powershell
77 | # Install JRE 64 bit without uninstall previous versions
78 | Install-JRE.ps1 -InstallPath \\FS01\JRE\jrex32.exe -LogPath \\FS01\JRE\Logs -x64 -NoUninstall
79 | ```
80 |
81 | ### Download-JRE
82 |
83 | **DownloadPath:** Path where save jrex32.exe and jrex64.exe.
84 |
85 | **LogPath:** Path where save log file.
86 |
87 | ```powershell
88 | # Download JRE 32 and 64 bit to D:\Software\JRE and save log in C:\Logs
89 | Download-JRE.ps1 -downloadPath "D:\Software\JRE" -logPath "C:\Logs"
90 | ```
91 |
92 |
--------------------------------------------------------------------------------
/Install LibreOffice/Install-LibreOffice.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Install LibreOffice for Windows if previous or no version detected and uninstall previous versions.
4 | .DESCRIPTION
5 | This script LibreOffice only if a previous version or no version was detected.
6 | You can avoid uninstall old versions with -NoUninstall switch.
7 | .PARAMETER InstallPath
8 | LibreOffice full installer path. It could be download from https://www.libreoffice.org/download/download-libreoffice/
9 | Authenticated users must have read permissions over shared folder.
10 | Example: \\FILESERVER-01\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi
11 | .PARAMETER LogPath
12 | Log path (optional). ComputerName.log file will be created.
13 | Authenticated users must have write permissions over log shared folder.
14 | Example: \\FILESERVER-01\LibreOffice\Logs (Log will be saved to \\FILESERVER-01\JRE\computername.log)
15 | .PARAMETER MSIArguments
16 | Parameters of LibreOffice installation options (optional).
17 | Default "/qn /norestart ALLUSERS=1 CREATEDESKTOPLINK=0 REGISTER_ALL_MSO_TYPES=0 REGISTER_NO_MSO_TYPES=1 ISCHECKFORPRODUCTUPDATES=0 QUICKSTART=0 ADDLOCAL=ALL UI_LANGS=en_US"
18 | .PARAMETER NoUninstall
19 | Default: false.
20 | Avoid uninstall previous versions before install.
21 | .EXAMPLE
22 | Install LibreOffice from network share, saving log in Log folder of network share.
23 | Note: network share must have read permissions on "\\FILESERVER-01\LibreOffice\" and write on "\\FILESERVER-01\LibreOffice\Logs" for "Authenticated Users" group.
24 | InstallLibreOffice.ps1 "\\FILESERVER-01\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi" "\\FILESERVER-01\LibreOffice\Logs"
25 | .NOTES
26 | Author: Juan Granados
27 | Date: November 2023
28 | #>
29 | Param(
30 | [Parameter(Mandatory = $true, Position = 0)]
31 | [ValidateNotNullOrEmpty()]
32 | [string]$InstallPath,
33 | [Parameter(Mandatory = $false, Position = 1)]
34 | [ValidateNotNullOrEmpty()]
35 | [string]$LogPath,
36 | [Parameter(Mandatory = $false, Position = 2)]
37 | [ValidateNotNullOrEmpty()]
38 | [string]$MSIArguments= "/qn /norestart ALLUSERS=1 CREATEDESKTOPLINK=0 REGISTER_ALL_MSO_TYPES=0 REGISTER_NO_MSO_TYPES=1 ISCHECKFORPRODUCTUPDATES=0 QUICKSTART=0 ADDLOCAL=ALL UI_LANGS=en_US"
39 | )
40 | #Requires -RunAsAdministrator
41 | function Get-InstalledApps {
42 | if ([IntPtr]::Size -eq 4) {
43 | $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
44 | }
45 | else {
46 | $regpath = @(
47 | 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
48 | 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
49 | )
50 | }
51 | Get-ItemProperty $regpath | . { process { if ($_.DisplayName -and $_.UninstallString) { $_ } } } |
52 | Select-Object DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |
53 | Sort-Object DisplayVersion
54 | }
55 |
56 | function StringVersionToFloat($version) {
57 | while (($version.ToCharArray() | Where-Object { $_ -eq '.' } | Measure-Object).Count -gt 1) {
58 | $aux = $version.Substring($version.LastIndexOf('.') + 1)
59 | $version = $version.Substring(0, $version.LastIndexOf('.')) + $aux
60 | }
61 | return [float]$version
62 | }
63 |
64 | function Get-MSIFileVersion ([System.IO.FileInfo]$msiFilePath) {
65 | try {
66 | $WindowsInstaller = New-Object -com WindowsInstaller.Installer
67 | $Database = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $WindowsInstaller, @($msiFilePath.FullName, 0))
68 | $Query = "SELECT Value FROM Property WHERE Property = 'ProductVersion'"
69 | $View = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $Database, ($Query))
70 | $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null) | Out-Null
71 | $Record = $View.GetType().InvokeMember( "Fetch", "InvokeMethod", $Null, $View, $Null )
72 | $Version = $Record.GetType().InvokeMember( "StringData", "GetProperty", $Null, $Record, 1 )
73 | return $Version
74 | } catch {
75 | throw "Failed to get MSI file version: {0}." -f $_
76 | }
77 | }
78 |
79 | if (-not [string]::IsNullOrWhiteSpace($LogPath) -and $logPath.Chars($logPath.Length - 1) -eq '\') {
80 | $logPath = ($logPath.TrimEnd('\'))
81 | }
82 | try{Stop-Transcript} catch{}
83 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) {
84 | Start-Transcript -Path "$($LogPath)\$($env:COMPUTERNAME).txt" | Out-Null
85 | }
86 |
87 | $ErrorActionPreference = "Stop"
88 | $Install = $true
89 |
90 | try {
91 | $loExeFile = Get-Item -Path $InstallPath
92 | }
93 | catch {
94 | Write-Error "Error accessing $($InstallPath)."
95 | Write-Error "$($Error[0])"
96 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) { Stop-Transcript }
97 | Exit 1
98 | }
99 |
100 | $loExeVersion = StringVersionToFloat $(Get-MSIFileversion $loExeFile)
101 |
102 | Write-Host "LibreOffice $($loExeVersion) selected for installation."
103 |
104 | $loInstalledVersion = Get-InstalledApps | Where-Object { $_.DisplayName -like '*LibreOffice*' }
105 |
106 | if ($loInstalledVersion) {
107 |
108 | foreach ($installation in $loInstalledVersion) {
109 |
110 | $version = StringVersionToFloat $installation.DisplayVersion
111 |
112 | if (($version -lt $loExeVersion)) {
113 | Write-Host "LibreOffice $($version) installed."
114 | }
115 | else {
116 | Write-Host "LibreOffice $($version) or greater already installed."
117 | $Install = $false
118 | }
119 | }
120 | }
121 | else {
122 | Write-Host "No LibreOffice version detected."
123 | }
124 |
125 | if ($Install) {
126 | Write-Host "Starting installation of LibreOffice $($loExeVersion) and exiting."
127 | $MSIArgs = "/i $($InstallPath) " + $MSIArguments + " /l $($env:LOCALAPPDATA)\InstallLibreOfficeInstallation.txt"
128 | $exitCode = (Start-Process "msiexec.exe" -ArgumentList $MSIArgs -Wait -NoNewWindow -PassThru).ExitCode
129 | get-content "$($env:LOCALAPPDATA)\InstallLibreOfficeInstallation.txt"
130 | if ($exitCode -eq 0) {
131 | Write-Host "LibreOffice installed sucessfully"
132 | }
133 | else {
134 | Write-Host "Error $exitCode installing LibreOffice"
135 | }
136 | }
137 |
138 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) { Stop-Transcript }
139 |
--------------------------------------------------------------------------------
/Install LibreOffice/readme.md:
--------------------------------------------------------------------------------
1 | # Install or update LibreOffice on domain computers using GPO and PowerShell.
2 |
3 | Install LibreOffice if previous or no version was detected.
4 |
5 | This script install LibreOffice only if a previous version or no version was detected.
6 |
7 | [Install-LibreOffice](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Install%20LibreOffice/Install-LibreOffice.ps1): script to install LibreOffice with GPO.
8 |
9 | ## Instructions
10 |
11 | 1. Create shared folder to deploy LibreOffice: ```\\FILESERVER-01\LibreOffice```
12 | 2. Grant 'Authenticated users' read access to ```\\FILESERVER-01\LibreOffice```
13 | 3. Copy ```Install-LibreOffice.ps1``` to ```\\FILESERVER-01\LibreOffice\Install-LibreOffice.ps1```
14 | 4. Create shared folder for logs: ```\\FILESERVER-01\LibreOffice\logs```
15 | 5. Grant 'Authenticated users' write access to ```\\FILESERVER-01\LibreOffice\logs```
16 | 6. Create a computer GPO that runs PowerShell Script:
17 | ```
18 | Name: \\FILESERVER-01\JRE\Install-LibreOffice.ps1
19 | Parameters: -InstallPath "\\INFSRV003\Software$\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi" -LogPath"\\INFSRV003\Software$\LibreOffice\Logs"
20 | ```
21 | ## Parameters
22 |
23 | **InstallPath:** LibreOffice installer path.
24 |
25 | **LogPath:** Path where save log file.
26 |
27 | **MSIArguments:** LibreOffice installation options (optional).
28 |
29 | ```powershell
30 | # Install LibreOffice
31 | \\INFSRV003\Software$\LibreOffice\Install-LibreOffice.ps1 -InstallPath "\\INFSRV003\Software$\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi" -LogPath"\\INFSRV003\Software$\LibreOffice\Logs"
32 | ```
33 |
--------------------------------------------------------------------------------
/Install MSI/Install-MSI.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Install any MSI if previous or no version detected
4 | Allows to update installed software and check the result of the installation using a log file.
5 | Is a more complete alternative to the msi installation via gpo.
6 | .DESCRIPTION
7 | This script any MSI only if a previous version or no version was detected.
8 | Allows to update installed software and check the result of the installation using a log file.
9 | Is a more complete alternative to the msi installation via gpo.
10 | .PARAMETER InstallPath
11 | MSI full installer path
12 | Example: \\FILESERVER-01\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi
13 | .PARAMETER SearchName
14 | Name of the application to search for it in the registry in order to get the version installed.
15 | It does not need to be the exact name, but search by this name must return only one item or nothing.
16 | You can simulate the search using the command:
17 | Get-WmiObject Win32_Product | Where-Object {$_.Name -like '*Office*'}
18 | .PARAMETER LogPath
19 | Log path (optional). ComputerName.log file will be created.
20 | Example: \\FILESERVER-01\LibreOffice\Logs (Log will be saved to \\FILESERVER-01\JRE\computername.log)
21 | .PARAMETER MSIArguments
22 | Parameters of MSI file.
23 | Warning! There seems to be a maximum number of 256 characters that can be used in the Script Parameters setting in a GPO Startup/Shutdown/Logon/Logoff PowerShell script.
24 | Sometimes scripts do not run even with fewer characters, so you can create a script that calls this script with all its parameters and run it via GPO.
25 | Optional, /qn is already applied.
26 | .EXAMPLE
27 | Install LibreOffice from network share, saving log in Log folder of network share.
28 | Note: network share must have read permissions on "\\FILESERVER-01\LibreOffice\" and write on "\\FILESERVER-01\LibreOffice\Logs" for "Authenticated Users" group in order to run it with GPO.
29 | Install-MSI.ps1 -InstallPath "\\FILESERVER-01\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi" -SearchName "LibreOffice" -LogPath "\\FILESERVER-01\LibreOffice\Logs" -MSIArguments "UI_LANGS=en_US,es"
30 | .NOTES
31 | Author: Juan Granados
32 | Date: November 2023
33 | #>
34 | Param(
35 | [Parameter(Mandatory = $true, Position = 0)]
36 | [ValidateNotNullOrEmpty()]
37 | [string]$InstallPath,
38 | [Parameter(Mandatory = $true, Position = 1)]
39 | [ValidateNotNullOrEmpty()]
40 | [string]$SearchName,
41 | [Parameter(Mandatory = $false, Position = 2)]
42 | [ValidateNotNullOrEmpty()]
43 | [string]$LogPath,
44 | [Parameter(Mandatory = $false, Position = 3)]
45 | [ValidateNotNullOrEmpty()]
46 | [string]$MSIArguments
47 | )
48 | #Requires -RunAsAdministrator
49 | function Get-InstalledApps {
50 | if ([IntPtr]::Size -eq 4) {
51 | $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
52 | }
53 | else {
54 | $regpath = @(
55 | 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
56 | 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
57 | )
58 | }
59 | Get-ItemProperty $regpath | . { process { if ($_.DisplayName -and $_.UninstallString) { $_ } } } |
60 | Select-Object DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |
61 | Sort-Object DisplayVersion
62 | }
63 |
64 | function StringVersionToFloat($version) {
65 | while (($version.ToCharArray() | Where-Object { $_ -eq '.' } | Measure-Object).Count -gt 1) {
66 | $aux = $version.Substring($version.LastIndexOf('.') + 1)
67 | $version = $version.Substring(0, $version.LastIndexOf('.')) + $aux
68 | }
69 | return [float]$version
70 | }
71 |
72 | function Get-MSIFileVersion ([System.IO.FileInfo]$msiFilePath) {
73 | try {
74 | $WindowsInstaller = New-Object -com WindowsInstaller.Installer
75 | $Database = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $WindowsInstaller, @($msiFilePath.FullName, 0))
76 | $Query = "SELECT Value FROM Property WHERE Property = 'ProductVersion'"
77 | $View = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $Database, ($Query))
78 | $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null) | Out-Null
79 | $Record = $View.GetType().InvokeMember( "Fetch", "InvokeMethod", $Null, $View, $Null )
80 | $Version = $Record.GetType().InvokeMember( "StringData", "GetProperty", $Null, $Record, 1 )
81 | return $Version
82 | }
83 | catch {
84 | Write-Host "Failed to get MSI file version: {0}." -f $_
85 | Stop-Transcript
86 | Exit
87 | }
88 | }
89 | function Get-MSIFileName ([System.IO.FileInfo]$msiFilePath) {
90 | try {
91 | $WindowsInstaller = New-Object -com WindowsInstaller.Installer
92 | $Database = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $WindowsInstaller, @($msiFilePath.FullName, 0))
93 | $Query = "SELECT Value FROM Property WHERE Property = 'ProductName'"
94 | $View = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $Database, ($Query))
95 | $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null) | Out-Null
96 | $Record = $View.GetType().InvokeMember( "Fetch", "InvokeMethod", $Null, $View, $Null )
97 | $ProductName = $Record.GetType().InvokeMember( "StringData", "GetProperty", $Null, $Record, 1 )
98 | return $ProductName
99 | }
100 | catch {
101 | Write-Host "Failed to get MSI file ProductName: {0}." -f $_
102 | Stop-Transcript
103 | Exit
104 | }
105 | }
106 |
107 | if (-not [string]::IsNullOrWhiteSpace($LogPath) -and $logPath.Chars($logPath.Length - 1) -eq '\') {
108 | $logPath = ($logPath.TrimEnd('\'))
109 | }
110 | if (Test-Path -Path $logPath) {
111 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) {
112 | try { Stop-Transcript | Out-Null } catch {}
113 | $logFilename = "$($env:COMPUTERNAME)_$($SearchName).txt"
114 | $logFilename.Split([IO.Path]::GetInvalidFileNameChars()) -join '_' | Out-Null
115 | Start-Transcript -Path "$($LogPath)\$($logFilename)" | Out-Null
116 | }
117 | }
118 | else {
119 | Write-Host "Log path does not exists"
120 | Exit
121 | }
122 |
123 | $ErrorActionPreference = "Stop"
124 | $Install = $true
125 |
126 | try {
127 | $MSIFile = Get-Item -Path $InstallPath
128 | }
129 | catch {
130 | Write-Error "Error accessing $($InstallPath)."
131 | Write-Error "$($Error[0])"
132 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) { Stop-Transcript }
133 | Exit 1
134 | }
135 |
136 | $MSIVersion = StringVersionToFloat $(Get-MSIFileversion $MSIFile)
137 | $MSIName = $(Get-MSIFileName $MSIFile).ToString()
138 |
139 | Write-Host "$($MSIName) version $($MSIVersion) selected for installation."
140 |
141 | $widcardSearch = "*$($SearchName)*"
142 | $installedVersion = Get-InstalledApps | Where-Object { $_.DisplayName -like $widcardSearch }
143 |
144 | if ($installedVersion) {
145 |
146 | foreach ($installation in $installedVersion) {
147 |
148 | $version = StringVersionToFloat $installation.DisplayVersion
149 |
150 | if (($version -lt $MSIVersion)) {
151 | Write-Host "Version $($version) installed."
152 | }
153 | else {
154 | Write-Host "Version $($version) or greater already installed."
155 | $Install = $false
156 | }
157 | }
158 | }
159 | else {
160 | Write-Host "No $($MSIName) detected."
161 | }
162 |
163 | if ($Install) {
164 | $msiLog = "$($env:LOCALAPPDATA)\msi_Installation.txt"
165 | if (Test-Path $msiLog) {
166 | Remove-Item $msiLog -Force
167 | }
168 | if (-not ([string]::IsNullOrEmpty($MSIArguments))) {
169 | $MSIArgs = "/i `"$($InstallPath)`" /qn $($MSIArguments) /l $($msiLog)"
170 | } else {
171 | $MSIArgs = "/i `"$($InstallPath)`" /qn /l $($msiLog)"
172 | }
173 | Write-Host "Starting installation of $($MSIName) version $($MSIVersion) and exiting."
174 | Write-Host "Running: msiexec.exe $($MSIArgs)"
175 | $exitCode = (Start-Process "msiexec.exe" -ArgumentList $MSIArgs -Wait -NoNewWindow -PassThru).ExitCode
176 | get-content $msiLog
177 | if ($exitCode -eq 0) {
178 | Write-Host "$($MSIName) installed sucessfully"
179 | }
180 | else {
181 | Write-Host "Error $exitCode installing $($MSIName)"
182 | }
183 | }
184 |
185 | if (-not [string]::IsNullOrWhiteSpace($LogPath)) { Stop-Transcript }
186 |
--------------------------------------------------------------------------------
/Install MSI/readme.md:
--------------------------------------------------------------------------------
1 | # Install or update any MSI on domain computers using GPO and PowerShell.
2 |
3 | Install any MSI if previous or no version detected
4 |
5 | Allows to update installed software and check the result of the installation using a log file.
6 |
7 | Is a more complete alternative to the msi installation via gpo.
8 |
9 | [Install-MSI](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Install%20MSI/Install-MSI.ps1): script to install MSI with GPO.
10 |
11 | ## Parameters
12 |
13 | **InstallPath**
14 | MSI full installer path
15 | Example: \\FILESERVER-01\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi
16 |
17 | **SearchName**
18 | Name of the application to search for it in the registry in order to get the version installed.
19 | It does not need to be the exact name, but search by this name must return only one item or nothing.
20 | You can simulate the search using the command:
21 | Get-WmiObject Win32_Product | Where-Object {$_.Name -like '*Office*'}
22 |
23 | **LogPath**
24 | Log path (optional). ComputerName.log file will be created.
25 | Example: \\FILESERVER-01\LibreOffice\Logs (Log will be saved to \\FILESERVER-01\JRE\computername.log)
26 |
27 | **MSIArguments**
28 | Parameters of MSI file.
29 | Warning! There seems to be a maximum number of 256 characters that can be used in the Script Parameters setting in a GPO Startup/Shutdown/Logon/Logoff PowerShell script.
30 | Sometimes scripts do not run even with fewer characters, so you can create a script that calls this script with all its parameters and run it via GPO.
31 | Optional, /qn is already applied.
32 |
33 | ## Instructions for deploying LibreOffice MSI
34 |
35 | 1. Create shared folder to deploy LibreOffice: ```\\FILESERVER-01\LibreOffice```
36 | 2. Grant 'Authenticated users' read access to ```\\FILESERVER-01\LibreOffice```
37 | 3. Copy ```Install-MSI.ps1``` to your script folder ```\\FILESERVER-01\Scripts$\Install-MSI.ps1```
38 | 4. Create shared folder for logs: ```\\FILESERVER-01\LibreOffice\logs```
39 | 5. Grant 'Authenticated users' write access to ```\\FILESERVER-01\LibreOffice\logs```
40 | 6. Create a powershell script named _Install-LibreOffice.ps1_ to avoid GPO parameter limit and run the installation on background:
41 | ```
42 | Start-Job -ScriptBlock {\\FILESERVER-01\Scripts$\Install-MSI.ps1 -InstallPath "\\FILESERVER-01\Software$\LibreOffice\LibreOffice_7.5.8_Win_x86-64.msi" -LogPath "\\FILESERVER-01\Software$\LibreOffice\Logs" -SearchName "LibreOffice" -MSIArguments "/norestart ALLUSERS=1 CREATEDESKTOPLINK=0 REGISTER_ALL_MSO_TYPES=0 REGISTER_NO_MSO_TYPES=1 ISCHECKFORPRODUCTUPDATES=0 QUICKSTART=0 ADDLOCAL=ALL UI_LANGS=en_US,ca,es"}
43 | ```
44 | 7. Create a GPO that runs _Install-LibreOffice.ps1_:
45 |
--------------------------------------------------------------------------------
/Install Print Drivers Remotely/1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Install Print Drivers Remotely/1.PNG
--------------------------------------------------------------------------------
/Install Print Drivers Remotely/2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Install Print Drivers Remotely/2.PNG
--------------------------------------------------------------------------------
/Install Print Drivers Remotely/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Install Print Drivers Remotely/3.png
--------------------------------------------------------------------------------
/Install Print Drivers Remotely/Install-PrinterDriversRemotely.ps1:
--------------------------------------------------------------------------------
1 | <#PSScriptInfo
2 |
3 | .VERSION 1.0
4 |
5 | .GUID ede5f6c5-50d3-42a9-b958-daff4b31972d
6 |
7 | .AUTHOR Juan Granados
8 |
9 | .COPYRIGHT 2021 Juan Granados
10 |
11 | .TAGS Install Printer Drivers Remote Remotely PrintNightmare PrinterExport
12 |
13 | .LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
14 |
15 | .PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/Install%20Print%20Drivers%20Remotely
16 |
17 | .RELEASENOTES
18 | Initial release
19 | #>
20 |
21 | <#
22 | .SYNOPSIS
23 | Install printer drivers export file (*.printerExport) in a list of computers.
24 | .DESCRIPTION
25 | Install printer drivers export file (*.printerExport) in a list of computers (plain computer list, OU or CSV file) using PSExec.
26 | To generate a printer export file with all print drivers, run PrintbrmUI.exe from the computer or server you want to export them.
27 | Be carefully because this tool exports all printers and ports too. I recommend install all drivers in a test computer without printers and export them using PrintbrmUI.exe.
28 | Alternatively, you can export all from print server, import in a test computer, delete printers and ports and export again to obtain a printerExport file with only drivers.
29 | If PSExec is not found on computer, script asks to the user for download it and extract in system folder.
30 | I recommend use PSExec latest version because v2.2 does not launch printbrm.exe properly.
31 | .PARAMETER printerExportFile
32 | Path to the printerExport file
33 | To generate a printer export file with all print drivers, run PrintbrmUI.exe from the computer or server you want to export them.
34 | Be carefully because this tool exports all printers and ports too. I recommend install all drivers in a test computer without printers and export them using PrintbrmUI.exe.
35 | Alternatively, you can export all from print server, import in a test computer, delete printers and ports and export again to obtain a printerExport file with only drivers.
36 | .PARAMETER ComputerList
37 | List of computers in install printer drivers. You can only use one source of target computers: ComputerList, OU or CSV.
38 | Example: SRV-RDSH-001,SRV-RDSH-002,SRV-RDSH-003 (Without quotation marks)
39 | .PARAMETER OU
40 | OU containing computers in which install printer drivers.
41 | RSAT for AD module for PowerShell must be installed in order to query AD.
42 | - Install on Windows 10: Get-WindowsCapability -Online |? {$_.Name -like "*RSAT.ActiveDirectory*" -and $_.State -eq "NotPresent"} | Add-WindowsCapability -Online
43 | - Install on server: Install-WindowsFeature RSAT-AD-PowerShell
44 | Restart console after installation.
45 | If you run script from a Domain Controller, AD module for PowerShell is already enabled.
46 | You can only use one source of target computers: ComputerList, OU or CSV.
47 | Example: 'OU=Test,OU=Computers,DC=CONTOSO,DC=COM'
48 | .PARAMETER CSV
49 | CSV file containing computers in which install printer drivers. You can only use one source of target computers: ComputerList, OU or CSV.
50 | Example: 'C:\Scripts\Computers.csv'
51 | CSV Format:
52 | Name
53 | Computer001
54 | Computer002
55 | Computer003
56 | .PARAMETER LogPath
57 | Path where save log file.
58 | Default: My Documents
59 | Example: C:\Logs
60 | .PARAMETER Credential
61 | Script will ask for an account to perform remote installation.
62 | .EXAMPLE
63 | Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -OU "OU=RDS,OU=Datacenter,DC=CONTOSO,DC=COM"
64 | .EXAMPLE
65 | Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -ComputerList SRVRSH-001,SRVRSH-002,SRVRSH-003 -Credential -LogPath C:\Temp\Logs
66 | .EXAMPLE
67 | Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -CSV "C:\scripts\computers.csv"
68 | .LINK
69 | https://github.com/juangranados/powershell-scripts/tree/main/Install%20Print%20Drivers%20Remotely
70 | .NOTES
71 | Author: Juan Granados
72 | #>
73 |
74 | Param(
75 | [Parameter(Mandatory=$true,Position=0)]
76 | [ValidateNotNullOrEmpty()]
77 | [string]$printerExportFile,
78 | [Parameter(Mandatory=$false,Position=1)]
79 | [ValidateNotNullOrEmpty()]
80 | [string]$LocalPath="C:\temp",
81 | [Parameter(Mandatory=$false,Position=2)]
82 | [ValidateNotNullOrEmpty()]
83 | [string[]]$ComputerList,
84 | [Parameter(Mandatory=$false,Position=3)]
85 | [ValidateNotNullOrEmpty()]
86 | [string]$OU,
87 | [Parameter(Mandatory=$false,Position=4)]
88 | [ValidateNotNullOrEmpty()]
89 | [string]$CSV,
90 | [Parameter(Mandatory=$false,Position=5)]
91 | [ValidateNotNullOrEmpty()]
92 | [string]$LogPath=[Environment]::GetFolderPath("MyDocuments"),
93 | [Parameter(Position=6)]
94 | [switch]$Credential
95 | )
96 |
97 | #Requires -RunAsAdministrator
98 |
99 | #Functions
100 |
101 | Add-Type -AssemblyName System.IO.Compression.FileSystem
102 | Import-Module BitsTransfer
103 |
104 | function Unzip
105 | {
106 | param([string]$zipfile, [string]$outpath)
107 |
108 | [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
109 | }
110 |
111 | $ErrorActionPreference = "Stop"
112 |
113 | #Initialice log
114 |
115 | $LogPath += "\InstallPrinterExportRemoteComputers_" + $(get-date -Format "yyyy-mm-dd_hh-mm-ss") + ".txt"
116 | Start-Transcript $LogPath
117 | Write-Host "Start remote installation on $(get-date -Format "yyyy-mm-dd hh:mm:ss")"
118 |
119 | #Initial validations.
120 |
121 | If (!(Test-Path $printerExportFile)){
122 | Write-Host "Error accessing $($printerExportFile). Script can not continue"
123 | Stop-Transcript
124 | Exit 1
125 | }
126 | if (-not($printerExportFile -Like "*.printerExport")) {
127 | Write-Host "File $($printerExportFile) does not has .printerExport extension. Script can not continue"
128 | Stop-Transcript
129 | Exit 1
130 | }
131 |
132 | if (!(Get-Command "psexec.exe" -ErrorAction SilentlyContinue)){
133 | Write-Host "Error. Microsoft Psexec not found on system. Download it from https://download.sysinternals.com/files/PSTools.zip and extract all in C:\Windows\System32" -ForegroundColor Yellow
134 | $Answer=Read-Host "Do you want to download and install PSTools (y/n)?"
135 | if (($Answer -eq "y") -or ($Answer -eq "Y")){
136 | Write-Host "Downloading PSTools"
137 | If (Test-Path "$($env:temp)\PSTools.zip"){
138 | Remove-Item "$($env:temp)\PSTools.zip" -Force
139 | }
140 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
141 | (New-Object System.Net.WebClient).DownloadFile("https://download.sysinternals.com/files/PSTools.zip", "$($env:temp)\PSTools.zip")
142 | if (Test-Path "$($env:temp)\PSTools.zip"){
143 | Write-Host "Unzipping PSTools"
144 | If (Test-Path "$($env:temp)\PSTools"){
145 | Remove-Item "$($env:temp)\PSTools" -Force -Recurse
146 | }
147 | Unzip "$($env:temp)\PSTools.zip" "$($env:temp)\PSTools"
148 | Copy-Item "$($env:temp)\PSTools\*.exe" "$($env:SystemRoot)\System32" -Force
149 | if (Test-Path "$($env:SystemRoot)\System32\psexec.exe"){
150 | Write-Host "PSTools installed" -ForegroundColor Green
151 | }
152 | else{
153 | Write-Host "Error unzipping PSTools" -ForegroundColor Red
154 | Remove-Item "$($env:temp)\PSTools.zip" -Force
155 | Stop-Transcript
156 | Exit 1
157 | }
158 | }
159 | else{
160 | Write-Host "Error downloading PSTools" -ForegroundColor Red
161 | Stop-Transcript
162 | Exit 1
163 | }
164 | }
165 | else{
166 | Stop-Transcript
167 | Exit 1
168 | }
169 | }
170 |
171 | If ($OU){
172 | if (!(Get-Command "Get-ADComputer" -ErrorAction SilentlyContinue)){
173 | Write-Host "Error. Get-ADComputer not found on system. You have to install the PowerShell Active Directory module order to query Active Directory." -ForegroundColor Red
174 | Write-Host 'Windows 10: Get-WindowsCapability -Online |? {$_.Name -like "*RSAT.ActiveDirectory*" -and $_.State -eq "NotPresent"} | Add-WindowsCapability -Online'
175 | Write-Host "Server: Install-WindowsFeature RSAT-AD-PowerShell"
176 | Write-Host "Restart console after installation"
177 | Stop-Transcript
178 | Exit 1
179 | }
180 | try{
181 | $ComputerList = Get-ADComputer -Filter * -SearchBase "$OU" | Select-Object -Expand name
182 | }catch{
183 | Write-Host "Error querying AD: $($_.Exception.Message)" -ForegroundColor Red
184 | Stop-Transcript
185 | Exit 1
186 | }
187 | }
188 | ElseIf ($CSV){
189 | try{
190 | $ComputerList = Get-Content $CSV | where {$_ -notmatch 'Name'} | Foreach-Object {$_ -replace '"', ''}
191 | }catch{
192 | Write-Host "Error getting CSV content: $($_.Exception.Message)" -ForegroundColor Red
193 | Stop-Transcript
194 | Exit 1
195 | }
196 | }
197 | ElseIf(!$ComputerList){
198 | Write-Host "You have to set a list of computers, OU or CSV." -ForegroundColor Red
199 | Stop-Transcript
200 | Exit 1
201 | }
202 | If ($Credential){
203 | $Cred = Get-Credential
204 | }
205 | $usingCredential = $false
206 | If(!$Cred -or !$Credential){
207 | Write-Host "No credential specified. Using logon account"
208 | }
209 | Else{
210 | $usingCredential = $true;
211 | Write-Host "Using user $($Cred.UserName)"
212 | $UserName = $Cred.UserName
213 | $Password = $Cred.GetNetworkCredential().Password
214 | }
215 | ForEach ($Computer in $ComputerList) {
216 | Write-Host "Processing computer $Computer"
217 | $Destination = "\\$Computer\C$\Temp\printer_drivers.printerExport"
218 | try {
219 | if (-not(Test-Path "\\$Computer\C$\Temp\")) {
220 | mkdir "\\$Computer\C$\Temp\"
221 | }
222 | Start-BitsTransfer -Source $printerExportFile -Destination $Destination -Description "Copy $driverFile to $Computer" -DisplayName "Copying"
223 | } catch {
224 | Write-Host "Error copying file: $($_.Exception.Message)"
225 | continue
226 | }
227 | Write-Host "Launching installation using PSExec in $Computer. This may take a while, please be patient..."
228 | try {
229 | if ($usingCredential) {
230 | psexec.exe /accepteula -h -i "\\$Computer" -u $UserName -p $Password C:\Windows\System32\spool\tools\Printbrm.exe -F C:\Temp\printer_drivers.printerExport -R
231 | }
232 | else {
233 | psexec /accepteula -h "\\$Computer" C:\Windows\System32\spool\tools\Printbrm.exe -F C:\Temp\printer_drivers.printerExport -R
234 | }
235 | } catch {
236 | Write-Host "PSExec return an error. Check console output above"
237 | }
238 |
239 | try {
240 | Write-Host "Removing remote file"
241 | Remove-Item $Destination -Force;
242 | } catch {
243 | Write-Host "Error removing remote file: $($_.Exception.Message)" -ForegroundColor Red
244 | }
245 | }
246 | Stop-Transcript
--------------------------------------------------------------------------------
/Install Print Drivers Remotely/readme.md:
--------------------------------------------------------------------------------
1 | # PrintNightmare - Install printer drivers on remote computers using printerExport file
2 |
3 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Install%20Print%20Drivers%20Remotely/Install-PrinterDriversRemotely.ps1)
4 |
5 | 
6 |
7 | On August 10, Microsoft posted a [blog post](https://msrc-blog.microsoft.com/2021/08/10/point-and-print-default-behavior-change/) about changes to the point and print.
8 |
9 | After the August patches, standard users cant add any printers. This means that you need to pre-install all drivers on your workstations or Remote Session Host servers.
10 |
11 | In [KB5005652](https://support.microsoft.com/topic/873642bf-2634-49c5-a23b-6d8e9a302872) documentation, Microsoft recommends this four possible solutions after install KB5005652 patch:
12 |
13 | 
14 |
15 | This script allows remote printer drivers installation on a group of computers using printerExport file and [PSExec](https://docs.microsoft.com/en-us/sysinternals/downloads/pstools). If PSExec is not found on computer, script asks to the user for download it and extract in system folder. I recommend use PSExec latest version because v2.2 does not launch printbrm.exe properly.
16 |
17 | To generate a printer export file with all printer drivers, run `PrintbrmUI.exe` from the computer or server you want to export them.
18 |
19 | Be carefully because this tool exports all printers and ports too. I recommend install all drivers in a test computer without printers and export them using `PrintbrmUI.exe`. Alternatively, you can export all from print server, import in a test computer, delete printers and ports and export again to obtain a printerExport file with only drivers.
20 |
21 | 
22 |
23 | ## Parameters
24 |
25 | **printerExportFile**: Path to the printerExport file.
26 |
27 | *Example: \\\SRVFS01\Drivers\drivers.printerExport*
28 |
29 | **ComputerList**: List of computers in install printer drivers. You can only use one source of target computers: ComputerList, OU or CSV.
30 |
31 | *Example: Computer001,Computer002,Computer003 (Without quotation marks)*
32 |
33 | **OU**: OU containing computers in which install printer drivers. You can only use one source of target computers: ComputerList, OU or CSV.
34 | RSAT for AD module for PowerShell must be installed in order to query AD.
35 | If you run script from a Domain Controller, AD module for PowerShell is already enabled.
36 |
37 | To install it from Windows 10 computer
38 |
39 | ```powershell
40 | Get-WindowsCapability -Online |? {$_.Name -like "*RSAT.ActiveDirectory*" -and $_.State -eq "NotPresent"} | Add-WindowsCapability -Online
41 | ```
42 |
43 | To install it from server
44 |
45 | ```powershell
46 | Install-WindowsFeature RSAT-AD-PowerShell
47 | ```
48 |
49 | *Example: : "OU=RDSH,OU=Servers,DC=CONTOSO,DC=COM"*
50 |
51 | **CSV**: CSV file containing computers in which install printer drivers. You can only use one source of target computers: ComputerList, OU or CSV.
52 |
53 | *Example: "C:\Scripts\Computers.csv"*
54 | CSV Format:
55 |
56 | ```CSV
57 | Name
58 | RDSH01
59 | RDSH03
60 | RDSH02
61 | ```
62 |
63 | **LogPath:** Path where save log file.
64 | Default: My Documents
65 |
66 | *Example: C:\Logs*
67 |
68 | **Credential**: Script will ask for an account to perform remote installation.
69 |
70 | ## Remote installation examples
71 | ```powershell
72 | Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -OU "OU=RDS,OU=Datacenter,DC=CONTOSO,DC=COM"
73 | Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -ComputerList SRVRSH-001,SRVRSH-002,SRVRSH-003 -Credential -LogPath C:\Temp\Logs
74 | Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -CSV "C:\scripts\computers.csv"
75 | ```
76 |
--------------------------------------------------------------------------------
/Install Software Remotely/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Install Software Remotely/Screenshot.png
--------------------------------------------------------------------------------
/Install Software Remotely/Screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Install Software Remotely/Screenshot2.png
--------------------------------------------------------------------------------
/Install Software Remotely/readme.md:
--------------------------------------------------------------------------------
1 | # Install software on multiple computers remotely with PowerShell
2 |
3 | Right click here and select "Save link as" to download
4 |
5 | This script install software remotely in a group of computers and retry the installation in case of error. It uses PowerShell to perform remote installation.
6 |
7 | *Screenshot of Edge remote installation on 20 RDS Servers.*
8 |
9 | 
10 |
11 | *Screenshot of TightVNC remote installation on 77 computers.*
12 |
13 | 
14 |
15 | As it uses Powershell to perform the remote installation, target computer must allow Windows PowerShell Remoting. Script can try to enable Windows PowerShell Remoting using Microsoft Sysinternals Psexec with the paramenter -EnablePSRemoting. If PSExec is not found on computer, script asks to the user for download it and extract to system folder.
16 |
17 | Please, read parameter description carefully before running.
18 |
19 | **AppPath:** Path to the application executable, It can be a network or local path because entire folder will be copied to remote computer before installing and deleted after installation.
20 |
21 | *Example:* `C:\Software\TeamViewer\TeamvieverHost.msi` *(Folder TeamViewer will be copied to remote computer before run ejecutable).*
22 |
23 | **AppArgs:** Application arguments to perform silent installation.
24 |
25 | *Example:* `/S /R settings.reg`
26 |
27 | **LocalPath:** Local path of the remote computer where copy application directory.
28 |
29 | *Default:* `C:\temp`
30 |
31 | **Retries:** Number of times to retry failed installations.
32 |
33 | *Default: 5.*
34 |
35 | **TimeBetweenRetries:** Seconds to wait before retrying failed installations.
36 |
37 | *Default: 60*
38 |
39 | **ComputerList:** List of computers in install software. You can only use one source of target computers: ComputerList, OU or CSV.
40 |
41 | *Example:* `Computer001,Computer002,Computer003` *(Without quotation marks)*
42 |
43 | **OU:** OU containing computers in which install software. RSAT for AD module for Powershell must be installed in order to query AD. If you run script from a Domain Controller, AD module for PowerShell is already enabled.
44 |
45 | *Example:* `OU=Test,OU=Computers,DC=CONTOSO,DC=COM`
46 |
47 | **CSV:** CSV file containing computers in which install software.
48 |
49 | *Example:* `C:\Scripts\Computers.csv`
50 |
51 | *CSV Format:*
52 |
53 | `Name`
54 |
55 | `Computer001`
56 |
57 | `Computer002`
58 |
59 | `Computer003.`
60 |
61 | **LogPath:** Path where save log file.
62 |
63 | *Default: My Documents.*
64 |
65 | **Credential:** Script will ask for an account to perform remote installation.
66 |
67 | **EnablePSRemoting:** Try to enable PSRemoting on failed computers using Psexec. Psexec has to be on system path. If PSExec is not found. Script ask to download automatically PSTools and copy them to C:\Windows\System32.
68 |
69 | **AppName:** App name as shown in registry to check if app is installed on remote computer and not reinstall it.
70 |
71 | You can check app name on a computer with it installed looking at:
72 |
73 | `HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\`
74 |
75 | `HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\`
76 |
77 | *Example: 'TightVNC'*
78 |
79 | *Default: None*
80 |
81 | **AppVersion:** App version as shown in registry. If not specified and AppName has a value, version will be ignored.
82 | If computer app version is lower than AppVersion variable, software will be installed and if it is equal or greater, software installation will be skipped.
83 |
84 | You can check app version on a computer with it installed looking at:
85 |
86 | `HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\`
87 |
88 | ` HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\`
89 |
90 | *Example: '2.0.8.1'*
91 |
92 | *Default: all*
93 |
94 | **WMIQuery:** WMI Query to execute in remote computers. Software will be installed if query returns any value.
95 |
96 | *Example:* `select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"' (64 bit computers)`
97 |
98 | *Example:* `select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="32"' (32 bit computers)`
99 |
100 | *Default: None*
101 |
102 | ## Examples
103 |
104 | Thanks Terence Luk for his amazing post:
105 |
106 | [http://terenceluk.blogspot.com/2019/02/using-installsoftwareremotelyps1-to.html](https://web.archive.org/web/20200318211458/http://terenceluk.blogspot.com/2019/02/using-installsoftwareremotelyps1-to.html)
107 |
108 | Other examples:
109 | ```powershell
110 | # Edge remote installation on all servers in OU
111 | Install-SoftwareRemotely.ps1 -AppPath "\\HSRV-FS02\Scripts\InstallSoftware\MicrosoftEdgeEnterpriseX64.msi" -AppArgs '/qn' -OU "OU=RDS,OU=Datacenter,DC=CONTOSO,DC=COM" -LogPath '\\HSRV-FS02\Scripts\InstallSoftware\Logs'
112 | ```
113 |
114 | ```powershell
115 | #Install TightVNC mirage Driver using computer list with different credentials checking before if it is installed and computers have 32 bits, enabling PSRemoting on connection error.
116 | Install-SoftwareRemotely.ps1 `
117 | -AppPath 'C:\Scripts\TightVNC\dfmirage-setup-2.0.301.exe' `
118 | -AppArgs '/verysilent /norestart' `
119 | -ComputerList PC01,PC03,PC12,PC34,PC43,PC50 `
120 | -Retries 2 `
121 | -AppName 'DemoForge Mirage Driver for TightVNC 2.0' `
122 | -AppVersion '2.0' `
123 | -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="32"' `
124 | -EnablePSRemoting `
125 | -Credential
126 | ```
127 | ```powershell
128 | #Install TightVNC on 64 bits computers in a OU checking before if it is installed and enablig PSRemoting on connection error.
129 | Install-SoftwareRemotely.ps1 `
130 | -AppPath 'C:\Scripts\TightVNC\tightvnc-2.8.8-gpl-setup-64bit.msi' `
131 | -AppArgs '/quiet /norestart ADDLOCAL="Server" SERVER_REGISTER_AS_SERVICE=1 SERVER_ADD_FIREWALL_EXCEPTION=1 SERVER_ALLOW_SAS=1 SET_USEVNCAUTHENTICATION=1 VALUE_OF_USEVNCAUTHENTICATION=1 SET_PASSWORD=1 VALUE_OF_PASSWORD=P@ssw0rd SET_USECONTROLAUTHENTICATION=1 VALUE_OF_USECONTROLAUTHENTICATION=1 SET_CONTROLPASSWORD=1 VALUE_OF_CONTROLPASSWORD=P@ssw0rd' `
132 | -OU 'OU=Central,OU=Computers,DC=Contoso,DC=local' `
133 | -Retries 2 `
134 | -AppName 'TightVNC' `
135 | -AppVersion '2.8.8.0' `
136 | -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"' `
137 | -EnablePSRemoting
138 | ```
139 | ```powershell
140 | #Upgrade VMware Tools in datacenter OU
141 | Install-SoftwareRemotely.ps1 -AppPath "\\mv-srv-fs01\Software\VMware Tools\setup64.exe" -AppArgs '/s /v "/qn reboot=r"' -OU "OU=Datacenter,DC=CONTOSO,DC=COM"
142 | ```
143 |
--------------------------------------------------------------------------------
/Install Software/Install-Software.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Install/Update/Uninstall software using RZGet repository from https://ruckzuck.tools/
4 | .DESCRIPTION
5 | Install/Update/Uninstall software using RZGet repository from https://ruckzuck.tools/
6 | .PARAMETER tempFolder
7 | Folder to download installers
8 | Default: "C:\temp\InstallSoftware"
9 | .PARAMETER software
10 | List of software to install. Check https://ruckzuck.tools/Home/Repository
11 | Default: None
12 | Example: "7-Zip","Notepad++","Edge","3CXPhone for Windows","Google Chrome","Teams","Postman"
13 | .PARAMETER logFolder
14 | Log file path.
15 | Default: "C:\temp\InstallSoftware"
16 | Example: "\\ES-CPD-BCK02\scripts\InstallSoftware\Log"
17 | .PARAMETER uninstall
18 | Uninstall software if it is already installed
19 | .PARAMETER checkOnly
20 | Check if software list is already installed
21 | .PARAMETER runAsAdmin
22 | Check if script is elevated
23 | .PARAMETER sleep
24 | Amount of seconds to sleep.
25 | When running as logon script sometimes Windows profile is not ready and installation fails.
26 | Default 0
27 | .EXAMPLE
28 | .\Install-Software -tempFolder C:\temp\InstallSoftware -software "7-Zip","Notepad++","Edge","3CXPhone for Windows","Google Chrome","Teams","Postman" -logFolder "\\ES-CPD-BCK02\scripts\InstallSoftware\Log"
29 | .LINK
30 | https://github.com/juangranados/powershell-scripts/tree/main/Install%20Software%20Locally
31 | .NOTES
32 | Thanks to Roger Zander for his amazing tool: https://ruckzuck.tools/
33 | Author: Juan Granados
34 | #>
35 | Param(
36 | [Parameter(Mandatory = $false)]
37 | [ValidateNotNullOrEmpty()]
38 | [string]$tempFolder = 'C:\temp\InstallSoftware',
39 | [Parameter(Mandatory = $true)]
40 | [string[]]$software,
41 | [Parameter(Mandatory = $false)]
42 | [string]$logFolder = 'C:\temp\InstallSoftware',
43 | [Parameter()]
44 | [switch]$uninstall,
45 | [Parameter()]
46 | [switch]$checkOnly,
47 | [Parameter()]
48 | [switch]$runAsAdmin,
49 | [Parameter()]
50 | [int]$sleep = 0
51 | )
52 | function Set-Folder([string]$folderPath) {
53 | if ($folderPath.Chars($folderPath.Length - 1) -eq '\') {
54 | $folderPath = ($folderPath.TrimEnd('\'))
55 | }
56 | if (!(Test-Path $folderPath)) {
57 | try {
58 | New-Item $folderPath -ItemType directory
59 | }
60 | catch {
61 | Write-Error "Error creating $folderPath"
62 | try {
63 | Stop-Transcript
64 | }
65 | catch { Write-Warning $Error[0] }
66 | Exit 1
67 | }
68 | }
69 | }
70 | function Get-FileHashIsOk([string]$filePath, [string]$hashType, [string]$hash) {
71 | if ($hashType.ToUpper() -eq "X509") {
72 | $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($filePath)
73 | $certHash = $cert.GetCertHashString().ToLower().Replace(" ", "")
74 | if ($certHash -ne $hash) {
75 | return $false
76 | }
77 | else {
78 | return $true
79 | }
80 | }
81 | elseif (($hashType.ToUpper() -eq "MD5")) {
82 | $fileHash = Get-FileHash $filePath -Algorithm MD5
83 | if ($fileHash.Hash -ne $hash) {
84 | return $false
85 | }
86 | else {
87 | return $true
88 | }
89 | }
90 | elseif (($hashType.ToUpper() -eq "SHA1")) {
91 | $fileHash = Get-FileHash $filePath -Algorithm SHA1
92 | if ($fileHash.Hash -ne $hash) {
93 | return $false
94 | }
95 | else {
96 | return $true
97 | }
98 | }
99 | elseif (($hashType.ToUpper() -eq "SHA256")) {
100 | $fileHash = Get-FileHash $filePath -Algorithm SHA256
101 | if ($fileHash.Hash -ne $hash) {
102 | return $false
103 | }
104 | else {
105 | return $true
106 | }
107 | }
108 | }
109 | function Get-AppInstaller ($files) {
110 | foreach ($file in $files) {
111 | $filePath = "$tempFolder\$($file.FileName)"
112 | try {
113 | if (-not $file.URL.StartsWith("http")) {
114 | try {
115 | $file.URL = Invoke-Expression -Command $file.URL
116 | }
117 | catch { Write-Warning $Error[0] }
118 | if (-not $file.URL) {
119 | Write-Warning "Error getting file URL"
120 | return $false
121 | }
122 | }
123 | Write-Host "Downloading $($file.URL)"
124 | $ProgressPreference = 'SilentlyContinue'
125 | Invoke-WebRequest $file.URL -OutFile $filePath -UseBasicParsing
126 | }
127 | catch {
128 | Write-Warning "Error downloading file"
129 | return $false
130 | }
131 | if (Test-Path $filePath) {
132 | Write-Host "File downloaded"
133 | }
134 | else {
135 | Write-Warning "File $filePath not found"
136 | return $false
137 | }
138 | if ((Get-Item $filePath).Length -eq $file.FileSize) {
139 | Write-Host "File size is ok"
140 | }
141 | else {
142 | Write-Warning "File size mismatch"
143 | }
144 | if (Get-FileHashIsOk $filePath $file.HashType $file.FileHash) {
145 | Write-Host "File hash is ok"
146 | }
147 | else {
148 | Write-Warning "File hash mismatch"
149 | return $false
150 | }
151 | }
152 | return $true
153 | }
154 | function Invoke-FilesDeletion ($files) {
155 | foreach ($file in $files) {
156 | $filePath = "$tempFolder\$($file.FileName)"
157 | Write-Host "Deleting file $filePath"
158 | Remove-Item -Path $filePath -Force -Confirm:$false
159 | }
160 | }
161 | function Get-InstalledApps {
162 | if ([IntPtr]::Size -eq 4) {
163 | $regpath = @(
164 | 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
165 | 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
166 | )
167 | }
168 | else {
169 | $regpath = @(
170 | 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
171 | 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
172 | 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
173 | )
174 | }
175 | Get-ItemProperty $regpath | . { process { if ($_.DisplayName -and $_.UninstallString) { $_ } } } |
176 | Select-Object DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |
177 | Sort-Object DisplayVersion
178 | }
179 | function StringVersionToFloat($version) {
180 | while (($version.ToCharArray() | Where-Object { $_ -eq '.' } | Measure-Object).Count -gt 1) {
181 | $aux = $version.Substring($version.LastIndexOf('.') + 1)
182 | $version = $version.Substring(0, $version.LastIndexOf('.')) + $aux
183 | }
184 | return [float]$version
185 | }
186 | function Get-AppIsInstalled($shortName) {
187 | $app = $catalog | Where-Object ShortName -in $shortName
188 | if (-not $app) {
189 | Write-Warning "$shortName not found in the catalog"
190 | return $null
191 | }
192 | $appInstalled = Get-InstalledApps | Where-Object { $_.DisplayName -like $app.ProductName }
193 | if (-not $appInstalled) {
194 | Write-Host "$shortName not found in computer"
195 | return $false
196 | }
197 | Write-Host "$shortName found in computer, installed version is $([version]$appInstalled.DisplayVersion) and current version is $([version]$app.ProductVersion)"
198 | if ([version]$appInstalled.DisplayVersion -lt [version]$app.ProductVersion) {
199 | return $false
200 | }
201 | else {
202 | return $appInstalled
203 | }
204 | }
205 | #https://cdn.ruckzuck.tools/rest/v2/GetCatalog
206 | $ErrorActionPreference = 'Stop'
207 | Set-Folder $tempFolder
208 | Set-Folder $logFolder
209 | Set-Location $tempFolder
210 | $apiKey = "26WRbGLCiABYG6xTuGYo14mxnwmBiBmUy6jyjBB7u5dR"
211 | $transcriptFile = "$logFolder\$(get-date -Format yyyy_MM_dd)_$($env:COMPUTERNAME)_InstallSoftware.txt"
212 | try {
213 | Start-Transcript $transcriptFile
214 | }
215 | catch { Write-Warning "Start-Transcript can not be started: $($Error[0])" }
216 | if ($sleep -ne 0) {
217 | Write-Host "Waiting $sleep seconds"
218 | Start-Sleep $sleep
219 | }
220 | if ($runAsAdmin) {
221 | Write-Host "Checking for elevated permissions"
222 | if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
223 | Write-Error "Insufficient permissions to run this script. Execute PowerShell script as an administrator."
224 | try {
225 | Stop-Transcript
226 | }
227 | catch { Write-Warning $Error[0] }
228 | Exit 1
229 | }
230 | Write-Host "Script is elevated"
231 | }
232 | Write-Host "Checking for software in computer"
233 | if ($software.Count -eq 1) {
234 | $software = $software.Split(',')
235 | }
236 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
237 | $apiUrl = Invoke-RestMethod -Uri "https://ruckzuck.tools/rest/v2/geturl" -UseBasicParsing
238 | if ([string]::IsNullOrEmpty($apiUrl)) {
239 | Write-Error "RuckZuck API can not be found"
240 | try {
241 | Stop-Transcript
242 | }
243 | catch { Write-Warning $Error[0] }
244 | Exit 1
245 | }
246 | $catalog = Invoke-WebRequest "$apiUrl/rest/v2/GetCatalog" -UseBasicParsing | ConvertFrom-Json
247 | if ([string]::IsNullOrEmpty($catalog)) {
248 | Write-Error "RuckZuck Catalog can not be found"
249 | try {
250 | Stop-Transcript
251 | }
252 | catch { Write-Warning $Error[0] }
253 | Exit 1
254 | }
255 | foreach ($app in $software) {
256 | $ULine = '-' * $app.Length
257 | Write-Host -Object $ULine -ForegroundColor DarkCyan
258 | Write-Host $app -ForegroundColor DarkCyan
259 | Write-Host -Object $ULine -ForegroundColor DarkCyan
260 | $uriApp = [uri]::EscapeDataString($app)
261 | $appInstalled = Get-AppIsInstalled $app
262 | if ($appInstalled -eq $false) {
263 | try {
264 | $appJson = Invoke-WebRequest "$apiUrl/rest/v2/getsoftwares?apikey=$apiKey&shortname=$uriApp" -UseBasicParsing | ConvertFrom-Json
265 | }
266 | catch {
267 | Write-Warning $Error[0]
268 | }
269 | if ($appJson) {
270 | if (-not $checkOnly -and -not $uninstall) {
271 | if ($appJson.PSPreReq) {
272 | If (Get-AppInstaller $appJson.Files) {
273 | try {
274 | if (-not [string]::IsNullOrEmpty($appJson.PSPreInstall)) {
275 | Write-Host "Running pre install command"
276 |
277 | Invoke-Expression -Command $appJson.PSPreInstall
278 | }
279 |
280 | Write-Host "Running install command"
281 | Invoke-Expression -Command $appJson.PSInstall
282 | if ($ExitCode -eq 0) {
283 | Write-Host "$app installation sucessful" -ForegroundColor Green
284 | }
285 | else {
286 | Write-Warning "$app installation returned $ExitCode"
287 | }
288 | if (-not [string]::IsNullOrEmpty($appJson.PSPostInstall)) {
289 | Write-Host "Running post install command"
290 | Invoke-Expression -Command $appJson.PSPostInstall
291 | }
292 | Invoke-FilesDeletion $appJson.Files
293 | }
294 | catch {
295 | Write-Warning $Error[0]
296 | }
297 | }
298 | }
299 | else {
300 | Write-Warning "$app can not be installed on computer because $($appJson.PSPreReq) is false"
301 | }
302 | }
303 | }
304 | else {
305 | Write-Warning "$app not found in RuckZuck repository"
306 | }
307 | }
308 | elseif ($appInstalled -and $uninstall -and -not $checkOnly) {
309 | Write-Host "Uninstalling $app"
310 | try {
311 | $appJson = Invoke-WebRequest "$apiUrl/rest/v2/getsoftwares?apikey=$apiKey&shortname=$uriApp" -UseBasicParsing | ConvertFrom-Json
312 | }
313 | catch {
314 | Write-Warning $Error[0]
315 | }
316 | if ($appJson) {
317 | Write-Host "Running uninstall command"
318 | try {
319 | Invoke-Expression -Command $appJson.PSUninstall
320 | if ($ExitCode -eq 0) {
321 | Write-Host "$app uninstallation sucessful" -ForegroundColor Green
322 | }
323 | else {
324 | Write-Warning "$app uninstallation returned $ExitCode"
325 | }
326 | }
327 | catch {
328 | Write-Warning $Error[0]
329 | }
330 | }
331 | else {
332 | Write-Warning "$app not found in RuckZuck repository"
333 | }
334 | }
335 | }
336 | Set-Location $PSScriptRoot
337 | try {
338 | Stop-Transcript
339 | }
340 | catch { Write-Warning $Error[0] }
--------------------------------------------------------------------------------
/Install Software/locally.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Install Software/locally.png
--------------------------------------------------------------------------------
/Install Software/psexec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Install Software/psexec.png
--------------------------------------------------------------------------------
/Install Software/readme.md:
--------------------------------------------------------------------------------
1 | # Install or update or uninstall software on computers using GPO/Intune/PSExec with PowerShell.
2 |
3 | This script Install/Update/Uninstall software using RZGet repository from https://ruckzuck.tools/.
4 |
5 | ⚠ **WARNING: Currently not working due to the need for an APIKey**
6 |
7 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Install%20Software/Install-Software.ps1)
8 |
9 | 
10 |
11 | ## Parameters
12 |
13 | ### tempFolder
14 | * Folder to download installers
15 | * Default: "C:\temp\InstallSoftware"
16 |
17 | ### software
18 | * List of software to install. Check https://ruckzuck.tools/Home/Repository
19 | * Default: None
20 | * Example: "7-Zip","Notepad++","Edge","3CXPhone for Windows","Google Chrome","Teams","Postman"
21 |
22 | ### logFolder
23 | * Log file path.
24 | * Default: "C:\temp\InstallSoftware"
25 | * Example: "\\ES-CPD-BCK02\scripts\InstallSoftware\Log"
26 |
27 | ### uninstall
28 | * Uninstall software if it is already installed
29 |
30 | ### checkOnly
31 | * Check if software list is already installed
32 |
33 | ### runAsAdmin
34 | * Check if script is elevated and exit if false.
35 |
36 | ### sleep
37 | * Amount of seconds to sleep.
38 | * When running as logon script sometimes Windows profile is not ready and installation fails.
39 | * Default 0
40 |
41 | ## Examples
42 |
43 | Install ```7-Zip, Notepad++, Edge, 3CXPhone for Windows, Google Chrome, Teams and Postman``` if not installed or if is outdated and save log in ```\\ES-CPD-BCK02\scripts\InstallSoftware\Log``` using ```C:\temp\InstallSoftware``` as temp folder.
44 |
45 | ```powershell
46 | Install-Software.ps1 -tempFolder C:\temp\InstallSoftware -software "7-Zip","Notepad++","Edge","3CXPhone for Windows","Google Chrome","Teams","Postman" -logFolder "\\ES-CPD-BCK02\scripts\InstallSoftware\Log"
47 | ```
48 | Uninstall ```Teams and Postman``` if installed and save log in ```\\ES-CPD-BCK02\scripts\InstallSoftware\Log```.
49 |
50 | ```powershell
51 | Install-Software.ps1 -software "Teams","Postman" -logFolder "\\ES-CPD-BCK02\scripts\InstallSoftware\Log" -uninstall
52 | ```
53 |
54 | Check if ```Teams and Postman``` are installed and save log in ```\\ES-CPD-BCK02\scripts\InstallSoftware\Log```.
55 |
56 | ```powershell
57 | Install-Software.ps1 -software "Teams","Postman" -logFolder "\\ES-CPD-BCK02\scripts\InstallSoftware\Log" -checkOnly
58 | ```
59 | ### Instructions for GPO deployment
60 |
61 | 1. Create shared folder to save script file Install-Software.ps1: ```\\FILESERVER-01\Install-Software```
62 | 2. Grant 'Authenticated users' read access to ```\\FILESERVER-01\Install-Software```
63 | 3. Copy ```Install-Software.ps1``` to ```\\FILESERVER-01\Install-Software\Install-Software.ps1```
64 | 4. Create shared folder for logs: ```\\FILESERVER-01\Install-Software\logs```
65 | 5. Grant 'Authenticated users' write access to ```\\FILESERVER-01\Install-Software\logs```
66 | 6. Create a computer GPO that runs PowerShell Script:
67 | ```
68 | Name: \\FILESERVER-01\Install-Software\Install-Software.ps1
69 | Parameters: -software "7-Zip","Notepad++","Edge" -logFolder "\\FILESERVER-01\Install-Software\logs" -sleep 60
70 | ```
71 | ### Instructions for Intune deployment
72 | As Intune does not allow parameters, you must harcode ```software``` parameter.
73 |
74 | Change line 35 by:
75 | ```powershell
76 | [Parameter(Mandatory = $false)]
77 | ```
78 |
79 | And line 36 with the software to install
80 | ```powershell
81 | [string[]]$software="7-Zip","Notepad++","Edge"
82 | ```
83 |
84 | ### Instructions for Remote Execution
85 | You can download PSExec from [here](https://docs.microsoft.com/en-us/sysinternals/downloads/psexec).
86 |
87 | **Run PSExec**
88 |
89 | psexec.exe -s \\```COMPUTER_NAME``` powershell.exe "-Command" "\\```NETWORKSHARE\Install-Software.ps1 -parameters```"
90 |
91 | Example
92 | ```
93 | psexec.exe -s \\WK-MARKETING01 powershell.exe "-Command" "\\FILESERVER-01\Install-Software\Install-Software.ps1 -software '7-Zip','Notepad++','Edge' -logFolder '\\ES-CPD-BCK02\scripts\InstallSoftware\Log'"
94 | ```
95 | 
96 |
--------------------------------------------------------------------------------
/Optimize and cleanup of WSUS on Windows Server 2012 R2 and 2016/Optimize-WSUS.ps1:
--------------------------------------------------------------------------------
1 |
2 | <#PSScriptInfo
3 |
4 | .VERSION 1.0
5 |
6 | .GUID c90bbc5c-9a05-471c-9020-741b5fc59d51
7 |
8 | .AUTHOR Juan Granados
9 |
10 | .COPYRIGHT 2021 Juan Granados
11 |
12 | .TAGS WSUS Optimice Database SQL WID DB
13 |
14 | .LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
15 |
16 | .PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/Optimize%20and%20cleanup%20of%20WSUS%20on%20Windows%20Server%202012%20R2%20and%202016
17 |
18 | .RELEASENOTES Initial release
19 |
20 | #>
21 |
22 | <#
23 | .SYNOPSIS
24 | Optimize WSUS DB and performs maintenance.
25 |
26 | .DESCRIPTION
27 | Optimize WSUS and its DB using official Microsoft SQL script and performs server maintenance.
28 |
29 | Prerequisites for running Invoke-Sqlcmd
30 |
31 | 1. Save T-SQL script as WsusDBMaintenance.sql from: https://docs.microsoft.com/en-us/troubleshoot/mem/configmgr/reindex-the-wsus-database
32 |
33 | 2. Navigate to: https://www.microsoft.com/en-US/download/details.aspx?id=55992 and install SQLSysClrTypes.msi
34 |
35 | 3. Run from PowerShell
36 | - Register-PackageSource -provider NuGet -name nugetRepository -location https://www.nuget.org/api/v2
37 | - Install-Package Microsoft.SqlServer.SqlManagementObjects
38 |
39 | 4. Run from PowerShell: Install-Module -Name SqlServer
40 |
41 | .NOTES
42 | Author: Juan Granados
43 | #>
44 | #Requires -RunAsAdministrator
45 | $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
46 |
47 | Start-Transcript "$scriptPath\$(Get-Date -format "yyyyMMdd")_Optimize-WSUS.log"
48 | function check-requisites {
49 | if (-not (Test-Path "$scriptPath\WsusDBMaintenance.sql")) {
50 | Write-Error "WsusDBMaintenance.sql missing. Save T-SQL script as WsusDBMaintenance.sql from: https://docs.microsoft.com/en-us/troubleshoot/mem/configmgr/reindex-the-wsus-database"
51 | Stop-Transcript
52 | Exit 1
53 | }
54 | if (-not (Get-Module -ListAvailable SQLServer)) {
55 | Write-Error "Module SQLServer missing. Install it running: Install-Module -Name SqlServer"
56 | Stop-Transcript
57 | Exit 1
58 | }
59 | if (-not (Test-Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server 2017 Redist\SQL Server System CLR Types\CurrentVersion")) {
60 | Write-Error "SQLSysClrTypes 2017 is misssing. Install it from https://www.microsoft.com/en-US/download/details.aspx?id=55992"
61 | Stop-Transcript
62 | Exit 1
63 | }
64 | if (-not(Get-Package Microsoft.SqlServer.SqlManagementObjects)) {
65 | Write-Error "Microsoft.SqlServer.SqlManagementObjects is missing. Install it running 'Register-PackageSource -provider NuGet -name nugetRepository -location https://www.nuget.org/api/v2' and 'Install-Package Microsoft.SqlServer.SqlManagementObjects'"
66 | Stop-Transcript
67 | Exit 1
68 | }
69 | }
70 |
71 | check-requisites
72 | Get-WsusServer | Invoke-WsusServerCleanup -CleanupObsoleteComputers -CleanupObsoleteUpdates -CleanupUnneededContentFiles -CompressUpdates -DeclineExpiredUpdates -DeclineSupersededUpdates
73 | Invoke-Sqlcmd -ServerInstance "\\.\pipe\microsoft##WID\tsql\query" -InputFile "$scriptPath\WsusDBMaintenance.sql" -Verbose
74 | Stop-Transcript
--------------------------------------------------------------------------------
/Optimize and cleanup of WSUS on Windows Server 2012 R2 and 2016/WsusDBMaintenance.sql:
--------------------------------------------------------------------------------
1 | USE SUSDB;
2 | GO
3 | SET NOCOUNT ON;
4 |
5 | -- Rebuild or reorganize indexes based on their fragmentation levels
6 | DECLARE @work_to_do TABLE (
7 | objectid int
8 | , indexid int
9 | , pagedensity float
10 | , fragmentation float
11 | , numrows int
12 | )
13 |
14 | DECLARE @objectid int;
15 | DECLARE @indexid int;
16 | DECLARE @schemaname nvarchar(130);
17 | DECLARE @objectname nvarchar(130);
18 | DECLARE @indexname nvarchar(130);
19 | DECLARE @numrows int
20 | DECLARE @density float;
21 | DECLARE @fragmentation float;
22 | DECLARE @command nvarchar(4000);
23 | DECLARE @fillfactorset bit
24 | DECLARE @numpages int
25 |
26 | -- Select indexes that need to be defragmented based on the following
27 | -- * Page density is low
28 | -- * External fragmentation is high in relation to index size
29 | PRINT 'Estimating fragmentation: Begin. ' + convert(nvarchar, getdate(), 121)
30 | INSERT @work_to_do
31 | SELECT
32 | f.object_id
33 | , index_id
34 | , avg_page_space_used_in_percent
35 | , avg_fragmentation_in_percent
36 | , record_count
37 | FROM
38 | sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'SAMPLED') AS f
39 | WHERE
40 | (f.avg_page_space_used_in_percent < 85.0 and f.avg_page_space_used_in_percent/100.0 * page_count < page_count - 1)
41 | or (f.page_count > 50 and f.avg_fragmentation_in_percent > 15.0)
42 | or (f.page_count > 10 and f.avg_fragmentation_in_percent > 80.0)
43 |
44 | PRINT 'Number of indexes to rebuild: ' + cast(@@ROWCOUNT as nvarchar(20))
45 |
46 | PRINT 'Estimating fragmentation: End. ' + convert(nvarchar, getdate(), 121)
47 |
48 | SELECT @numpages = sum(ps.used_page_count)
49 | FROM
50 | @work_to_do AS fi
51 | INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id
52 | INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id
53 |
54 | -- Declare the cursor for the list of indexes to be processed.
55 | DECLARE curIndexes CURSOR FOR SELECT * FROM @work_to_do
56 |
57 | -- Open the cursor.
58 | OPEN curIndexes
59 |
60 | -- Loop through the indexes
61 | WHILE (1=1)
62 | BEGIN
63 | FETCH NEXT FROM curIndexes
64 | INTO @objectid, @indexid, @density, @fragmentation, @numrows;
65 | IF @@FETCH_STATUS < 0 BREAK;
66 |
67 | SELECT
68 | @objectname = QUOTENAME(o.name)
69 | , @schemaname = QUOTENAME(s.name)
70 | FROM
71 | sys.objects AS o
72 | INNER JOIN sys.schemas as s ON s.schema_id = o.schema_id
73 | WHERE
74 | o.object_id = @objectid;
75 |
76 | SELECT
77 | @indexname = QUOTENAME(name)
78 | , @fillfactorset = CASE fill_factor WHEN 0 THEN 0 ELSE 1 END
79 | FROM
80 | sys.indexes
81 | WHERE
82 | object_id = @objectid AND index_id = @indexid;
83 |
84 | IF ((@density BETWEEN 75.0 AND 85.0) AND @fillfactorset = 1) OR (@fragmentation < 30.0)
85 | SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REORGANIZE';
86 | ELSE IF @numrows >= 5000 AND @fillfactorset = 0
87 | SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD WITH (FILLFACTOR = 90)';
88 | ELSE
89 | SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD';
90 | PRINT convert(nvarchar, getdate(), 121) + N' Executing: ' + @command;
91 | EXEC (@command);
92 | PRINT convert(nvarchar, getdate(), 121) + N' Done.';
93 | END
94 |
95 | -- Close and deallocate the cursor.
96 | CLOSE curIndexes;
97 | DEALLOCATE curIndexes;
98 |
99 |
100 | IF EXISTS (SELECT * FROM @work_to_do)
101 | BEGIN
102 | PRINT 'Estimated number of pages in fragmented indexes: ' + cast(@numpages as nvarchar(20))
103 | SELECT @numpages = @numpages - sum(ps.used_page_count)
104 | FROM
105 | @work_to_do AS fi
106 | INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id
107 | INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id
108 |
109 | PRINT 'Estimated number of pages freed: ' + cast(@numpages as nvarchar(20))
110 | END
111 | GO
112 |
113 |
114 | --Update all statistics
115 | PRINT 'Updating all statistics.' + convert(nvarchar, getdate(), 121)
116 | EXEC sp_updatestats
117 | PRINT 'Done updating statistics.' + convert(nvarchar, getdate(), 121)
118 | GO
--------------------------------------------------------------------------------
/Optimize and cleanup of WSUS on Windows Server 2012 R2 and 2016/readme.md:
--------------------------------------------------------------------------------
1 | # Optimize and cleanup of WSUS on Windows Server
2 |
3 | Optimize WSUS DB using [official Microsoft SQL script](https://docs.microsoft.com/en-us/troubleshoot/mem/configmgr/reindex-the-wsus-database) and performs server maintenance.
4 |
5 | WsusDBMaintenance.sql and script must be on the same path.
6 |
7 | - Right click here and select "Save link as" to download script
8 |
9 | - Right click here and select "Save link as" to download WsusDBMaintenance.sql
10 |
11 | It saves log in script path: yyyyMMdd__Optimize-WSUS.log
12 |
13 | Prerequisites for running Invoke-Sqlcmd
14 |
15 | 1. Download and install SQLSysClrTypes.msi from: [Microsoft® SQL Server® 2017 Feature Pack](https://www.microsoft.com/en-US/download/details.aspx?id=55992)
16 | 2. Run from PowerShell:
17 | - `Register-PackageSource -provider NuGet -name nugetRepository -location https://www.nuget.org/api/v2`
18 | - `Install-Package Microsoft.SqlServer.SqlManagementObjects` in case of error of circular dependency add `-SkipDependencies`
19 | 3. Run from PowerShell: `Install-Module -Name SqlServer`
20 |
--------------------------------------------------------------------------------
/Optimize drives/Invoke-DiskDefrag.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Runs Windows disks defragmentation.
4 | .DESCRIPTION
5 | Runs Windows disks defragmentation in all or selected drives.
6 | .PARAMETER disks
7 | Disks to run defragmentation.
8 | Default: all.
9 | Example: "C:","D:","F:"
10 | .PARAMETER defragPercentage
11 | Percentage of fragmentation in order to run defragmentation.
12 | Default: 10
13 | .PARAMETER forceDefrag
14 | Defrag disks if free space is low.
15 | Default: false
16 | .PARAMETER logPath
17 | Path where save log file.
18 | Default: Temp folder
19 | .EXAMPLE
20 | Defrag all drives if they are 10% fragmented.
21 | Invoke-DiskDefrag.ps1
22 | .EXAMPLE
23 | Defrag only C and D drives if they are 20% fragmented.
24 | Invoke-DiskDefrag.ps1 -disks "C:","D:" -defragPercentage 20
25 | .EXAMPLE
26 | Defrag C: drive if they are 10% fragmented. It runs disk defragmentation even C: disk free space is low.
27 | Invoke-DiskDefrag.ps1 -disks "C:" -forceDefrag
28 | .NOTES
29 | Author: Juan Granados
30 | #>
31 |
32 | Param(
33 | [Parameter(Mandatory = $false)]
34 | [ValidateRange(0, 100)]
35 | [int]$defragPercentage = 10,
36 | [Parameter(Mandatory = $false)]
37 | [ValidateNotNullOrEmpty()]
38 | [string[]]$disks = "all",
39 | [Parameter(Mandatory = $false)]
40 | [ValidateNotNullOrEmpty()]
41 | [string]$logPath = $env:temp,
42 | [Parameter()]
43 | [switch]$forceDefrag
44 | )
45 |
46 | #Requires -RunAsAdministrator
47 |
48 | Function Invoke-DiskDefragmentation($diskToDefrag) {
49 |
50 | if ([Environment]::UserInteractive) {
51 | Start-Process "C:\Windows\System32\dfrgui.exe"
52 | }
53 | if ($forceDefrag) {
54 | Write-Host "Forcing $($diskToDefrag.DriveLetter) defragmentation"
55 | $result = $diskToDefrag.Defrag($true)
56 | }
57 | else {
58 | Write-Host "Performing $($diskToDefrag.DriveLetter) defragmentation"
59 | $result = $diskToDefrag.Defrag($false)
60 | }
61 | if ($result.ReturnValue -eq 0) {
62 | Write-Host "Defragmentation successful"
63 | Write-Host "Current fragmentation is $($result.DefragAnalysis.FilePercentFragmentation)"
64 | $diskToDefrag.DefragResult = $result
65 | }
66 | else {
67 | Write-Output "CRITICAL: Error $($result.ReturnValue) defragmenting drive $($diskToDefrag.DriveLetter)"
68 | Write-Output "Check error codes: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/vdswmi/defrag-method-in-class-win32-volume"
69 | Exit(2)
70 | }
71 | $global:output += "Disk $($diskToDefrag.DriveLetter) fragmentation is $($result.DefragAnalysis.FilePercentFragmentation)."
72 | }
73 |
74 | $ErrorActionPreference = "SilentlyContinue"
75 | Stop-Transcript | out-null
76 | $ErrorActionPreference = "Stop"
77 |
78 | $global:output = ""
79 |
80 | $logPath = $logPath.TrimEnd('\')
81 | if (-not (Test-Path $logPath)) {
82 | Write-Host "Log path $($logPath) not found"
83 | Exit (1)
84 | }
85 |
86 | Start-Transcript -path "$($logPath)\$(get-date -Format yyyy_MM_dd)_$($env:COMPUTERNAME).txt"
87 |
88 | try {
89 | if ($disks -eq "all") {
90 | $drives = get-wmiobject win32_volume | Where-Object { $_.DriveType -eq 3 -and $_.DriveLetter -and (Get-WMIObject Win32_LogicalDiskToPartition | Select-Object Dependent) -match $_.DriveLetter }
91 | }
92 | else {
93 | foreach ($disk in $disks) {
94 | if (-not ($disk -match '[A-Za-z]:')) {
95 | Write-Output "UNKNOWN: Error $($drive) is not a valid disk unit. Expected N:, where N is drive unit. Example C: or D: or F:"
96 | Exit(3)
97 | }
98 | }
99 | $drives = get-wmiobject win32_volume | Where-Object { $_.DriveType -eq 3 -and $_.DriveLetter -in $disks }
100 | }
101 | if (-not ($drives)) {
102 | Write-Output "UNKNOWN: No drives found with get-wmiobject win32_volume command"
103 | Exit(3)
104 | }
105 | foreach ($drive in $drives) {
106 | Write-Host "Analizing drive $($drive.DriveLetter)"
107 | $result = $drive.DefragAnalysis()
108 | if ($result.ReturnValue -eq 0) {
109 | Write-Host "Current fragmentation is $($result.DefragAnalysis.FilePercentFragmentation)"
110 | $drive | Add-Member -NotePropertyName 'DefragResult' -NotePropertyValue $result
111 | if (($defragPercentage -gt 0) -and ($result.DefragAnalysis.FilePercentFragmentation -gt $defragPercentage)) {
112 | Invoke-DiskDefragmentation -diskToDefrag $drive
113 | }
114 | else {
115 | $global:output += "Disk $($drive.DriveLetter) fragmentation is $($result.DefragAnalysis.FilePercentFragmentation)."
116 | }
117 | }
118 | else {
119 | Write-Output "CRITICAL: Error $($result.ReturnValue) checking status of drive $($drive.DriveLetter)"
120 | Write-Output "Check error codes: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/vdswmi/defraganalysis-method-in-class-win32-volume#return-value"
121 | Exit(2)
122 | }
123 | }
124 | }
125 | catch {
126 | Write-Output "CRITICAL: $($_.Exception.Message)"
127 | Exit(2)
128 | }
129 | Write-Host "-------"
130 | Write-Host "Summary"
131 | Write-Host "-------"
132 | Write-Host $global:output
133 | Stop-Transcript
--------------------------------------------------------------------------------
/Optimize drives/Invoke-DiskOptimize.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Runs Windows disks optimization.
4 | .DESCRIPTION
5 | Runs Windows disks optimization in all or selected drives.
6 | .PARAMETER disks
7 | Disks to run optimization.
8 | Default: all.
9 | Example: "C:","D:","F:"
10 | .PARAMETER logPath
11 | Path where save log file.
12 | Default: Temp folder
13 | .PARAMETER params
14 | Params for Optimize-Volume command
15 | Default: -Verbose
16 | Example: "-ReTrim -Verbose"
17 | .EXAMPLE
18 | Optimize all drives.
19 | Invoke-DiskOptimize.ps1
20 | .EXAMPLE
21 | Optimize only C and D drives.
22 | Invoke-DiskOptimize.ps1 -disks "C:","D:"
23 | .NOTES
24 | Author: Juan Granados
25 | #>
26 |
27 | Param(
28 | [Parameter(Mandatory = $false)]
29 | [ValidateNotNullOrEmpty()]
30 | [string[]]$disks = "all",
31 | [Parameter(Mandatory = $false)]
32 | [ValidateNotNullOrEmpty()]
33 | [string]$logPath = $env:temp,
34 | [Parameter(Mandatory = $false)]
35 | [ValidateNotNullOrEmpty()]
36 | [string]$params = "-Verbose"
37 | )
38 | #Requires -RunAsAdministrator
39 |
40 | $ErrorActionPreference = "SilentlyContinue"
41 | Stop-Transcript | out-null
42 | $ErrorActionPreference = "Stop"
43 |
44 | $logPath = $logPath.TrimEnd('\')
45 | if (-not (Test-Path $logPath)) {
46 | Write-Host "Log path $($logPath) not found"
47 | Exit (1)
48 | }
49 |
50 | Start-Transcript -path "$($logPath)\$(get-date -Format yyyy_MM_dd)_$($env:COMPUTERNAME).txt"
51 |
52 | try {
53 | if ($disks -eq "all") {
54 | $drives = get-wmiobject win32_volume | Where-Object { $_.DriveType -eq 3 -and $_.DriveLetter -and (Get-WMIObject Win32_LogicalDiskToPartition | Select-Object Dependent) -match $_.DriveLetter }
55 | }
56 | else {
57 | foreach ($disk in $disks) {
58 | if (-not ($disk -match '[A-Za-z]:')) {
59 | Write-Output "UNKNOWN: Error $($drive) is not a valid disk unit. Expected N:, where N is drive unit. Example C: or D: or F:"
60 | Exit(3)
61 | }
62 | }
63 | $drives = get-wmiobject win32_volume | Where-Object { $_.DriveType -eq 3 -and $_.DriveLetter -in $disks }
64 | }
65 | if (-not ($drives)) {
66 | Write-Output "UNKNOWN: No drives found with get-wmiobject win32_volume command"
67 | Exit(3)
68 | }
69 | foreach ($drive in $drives) {
70 | Write-Host "-------------------"
71 | Write-Host "Optimizing drive $($drive.DriveLetter)"
72 | Write-Host "-------------------"
73 | Invoke-Expression "Optimize-Volume -Driveletter $($drive.DriveLetter.TrimEnd(':')) $params"
74 | }
75 | }
76 | catch {
77 | Write-Output "CRITICAL: $($_.Exception.Message)"
78 | Exit(2)
79 | }
80 | Stop-Transcript
--------------------------------------------------------------------------------
/Optimize drives/readme.md:
--------------------------------------------------------------------------------
1 | # **Optimize or Defrag Windows Drives**
2 |
3 | Right click and select "Save link as" to download
4 |
5 | * [Invoke-DiskOptimize.ps1](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Optimize%20drives/Invoke-DiskOptimize.ps1): Runs Windows disks optimization in all or selected drives.
6 | * [Invoke-DiskDefrag.ps1](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Optimize%20drives/Invoke-DiskDefrag.ps1): Runs Windows disks defragmentation in all or selected drives.
7 |
8 | Examples:
9 |
10 | Optimize all drives.
11 |
12 | ```powershell
13 | Invoke-DiskOptimize.ps1 -LogPath "\\SERVER-FS01\Logs"
14 | ```
15 |
16 | Optimize only C and D drives.
17 |
18 | ```powershell
19 | Invoke-DiskOptimize.ps1 -disks "C:","D:"
20 | ```
21 |
22 | Defrag all drives if they are 10% fragmented (default value).
23 |
24 | ```powershell
25 | Invoke-DiskDefrag.ps1
26 | ```
27 |
28 | Defrag only C and D drives if they are 20% fragmented.
29 |
30 | ```powershell
31 | Invoke-DiskDefrag.ps1 -disks "C:","D:" -defragPercentage 20 -LogPath "\\SERVER-FS01\Logs"
32 | ```
33 |
34 | Defrag C: drive if they are 10% fragmented. It runs disk defragmentation even C: disk free space is low.
35 |
36 | ```powershell
37 | Invoke-DiskDefrag.ps1 -disks "C:" -forceDefrag
38 | ```
39 |
40 | ```powershell
41 | <#
42 | .SYNOPSIS
43 | Runs Windows disks optimization.
44 | .DESCRIPTION
45 | Runs Windows disks optimization in all or selected drives.
46 | .PARAMETER disks
47 | Disks to run optimization.
48 | Default: all.
49 | Example: "C:","D:","F:"
50 | .PARAMETER LogPath
51 | Path where save log file.
52 | Default: Temp folder
53 | .EXAMPLE
54 | Optimize all drives.
55 | Invoke-DiskOptimize.ps1
56 | .EXAMPLE
57 | Optimize only C and D drives.
58 | Invoke-DiskOptimize.ps1 -disks "C:","D:"
59 | .NOTES
60 | Author:Juan Granados
61 | #>
62 | ```
63 |
64 | ```powershell
65 | <#
66 | .SYNOPSIS
67 | Runs Windows disks defragmentation.
68 | .DESCRIPTION
69 | Runs Windows disks defragmentation in all or selected drives.
70 | .PARAMETER disks
71 | Disks to run defragmentation.
72 | Default: all.
73 | Example: "C:","D:","F:"
74 | .PARAMETER defragPercentage
75 | Percentage of fragmentation in order to run defragmentation.
76 | Default: 10
77 | .PARAMETER forceDefrag
78 | Defrag disks if free space is low.
79 | Default: false
80 | .PARAMETER LogPath
81 | Path where save log file.
82 | Default: Temp folder
83 | .EXAMPLE
84 | Defrag all drives if they are 10% fragmented.
85 | Invoke-DiskDefrag.ps1
86 | .EXAMPLE
87 | Defrag only C and D drives if they are 20% fragmented.
88 | Invoke-DiskDefrag.ps1 -disks "C:","D:" -defragPercentage 20
89 | .EXAMPLE
90 | Defrag C: drive if they are 10% fragmented. It runs disk defragmentation even C: disk free space is low.
91 | Invoke-DiskDefrag.ps1 -disks "C:" -forceDefrag
92 | .NOTES
93 | Author:Juan Granados
94 | #>
95 | ```
96 |
--------------------------------------------------------------------------------
/Password Encryption for PowerShell scripts/New-StringDecryption.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Cmdlet will decode a base64 encrypted string
4 |
5 | .DESCRIPTION
6 | Function will take as input a base64 encrypted string and output the unencrypted string
7 |
8 | .PARAMETER EncryptedString
9 | The string to be decrypted
10 |
11 | .PARAMETER EncryptPassPhrase
12 | A string representing the encryption passphrase to be used to decrypt the string.
13 |
14 | If not specified hostname of the computer where function is invoked will be used.
15 |
16 | .PARAMETER EncryptSalt
17 | A string representing the encryption salt to use to decrypt the string.
18 |
19 | If not specified hostname of the computer where function is invoked will be used.
20 |
21 | .PARAMETER IntersectingVector
22 | A string representing the intersecting vector to be used during strict decryption.
23 |
24 | If not specified it will default to a standard value which must match the one used to encrypt the string.
25 |
26 | For better security this should be changed to a custom string eatch time.
27 |
28 | .EXAMPLE
29 | PS C:\> New-StringDecryption
30 |
31 | .OUTPUTS
32 | System.String
33 | #>
34 |
35 | [OutputType([string])]
36 | param
37 | (
38 | [Parameter(Mandatory = $true)]
39 | [ValidateNotNullOrEmpty()]
40 | [string]
41 | $EncryptedString,
42 | [ValidateNotNullOrEmpty()]
43 | [string]
44 | $EncryptPassPhrase = $env:Computername,
45 | [ValidateNotNullOrEmpty()]
46 | [string]
47 | $EncryptSalt = $env:Computername,
48 | [ValidateNotNullOrEmpty()]
49 | [string]
50 | $IntersectingVector = 'Q!L@2QTCYgsG'
51 | )
52 |
53 | # Instantiate empty return value
54 | [string]$DecryptedString = $null
55 |
56 | # Regex to check if string is in the correct format
57 | [regex]$base64RegEx = '^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$'
58 |
59 | # Instantiate COM Object for RijndaelManaged Cryptography
60 | [System.Security.Cryptography.RijndaelManaged]$encryptionObject = New-Object System.Security.Cryptography.RijndaelManaged
61 |
62 | # If the value in the Encrypted is a string, convert it to Base64
63 | if ($EncryptedString -is [string])
64 | {
65 | # Check string is in correct format
66 | if ($EncryptedString -match $base64RegEx)
67 | {
68 | [byte[]]$encryptedStringByte = [Convert]::FromBase64String($EncryptedString)
69 | }
70 | else
71 | {
72 | Write-Warning -Message 'String is not base64 encoded!'
73 |
74 | return
75 | }
76 | }
77 | else
78 | {
79 | Write-Warning 'Input is not a string!'
80 |
81 | return
82 | }
83 |
84 | # Convert Salt and Passphrase to UTF8 Bytes array
85 | [System.Byte[]]$byteEncryptSalt = [Text.Encoding]::UTF8.GetBytes($EncryptSalt)
86 | [System.Byte[]]$bytePassPhrase = [Text.Encoding]::UTF8.GetBytes($EncryptPassPhrase)
87 |
88 | # Create the Encryption Key using the passphrase, salt and SHA1 algorithm at 256 bits
89 | $encryptionObject.Key = (New-Object Security.Cryptography.PasswordDeriveBytes $bytePassPhrase,
90 | $byteEncryptSalt,
91 | 'SHA',
92 | 5).GetBytes(32) # 256/8 - 32byts
93 |
94 | # Create the Intersecting Vector Cryptology Hash with the init
95 | $encryptionObject.IV = (New-Object Security.Cryptography.SHA1Managed).ComputeHash([Text.Encoding]::UTF8.GetBytes($IntersectingVector))[0 .. 15]
96 |
97 | # Create new decryptor Key and IV
98 | [System.Security.Cryptography.RijndaelManagedTransform]$objectDecryptor = $encryptionObject.CreateDecryptor()
99 |
100 | # Create a New memory stream with the encrypted value
101 | [System.IO.MemoryStream]$memoryStream = New-Object IO.MemoryStream @( ,$encryptedStringByte)
102 |
103 | # Read the new memory stream and read it in the cryptology stream
104 | [System.Security.Cryptography.CryptoStream]$cryptoStream = New-Object Security.Cryptography.CryptoStream $memoryStream, $objectDecryptor, 'Read'
105 |
106 | # Read the new decrypted stream
107 | [System.IO.StreamReader]$streamReader = New-Object IO.StreamReader $cryptoStream
108 |
109 | try
110 | {
111 | # Return from the function the stream
112 | [string]$DecryptedString = $streamReader.ReadToEnd()
113 |
114 | # Stop the stream
115 | $streamReader.Close()
116 |
117 | # Stop the crypto stream
118 | $cryptoStream.Close()
119 |
120 | # Stop the memory stream
121 | $memoryStream.Close()
122 |
123 | # Clears all crypto objects
124 | $encryptionObject.Clear()
125 |
126 | # Return decrypted string
127 | return $DecryptedString
128 | }
129 |
130 | catch
131 | {
132 | # Save exception
133 | [string]$reportedException = $Error[0].Exception.Message
134 |
135 | Write-Warning -Message "String $EncryptedString could not be decripted - Use the -Verbose paramter for more details"
136 |
137 | # Check we have an exception message
138 | if ([string]::IsNullOrEmpty($reportedException) -eq $false)
139 | {
140 | Write-Verbose -Message $reportedException
141 | }
142 | else
143 | {
144 | Write-Verbose -Message 'No inner exception reported by Disconnect-AzureAD cmdlet'
145 | }
146 |
147 | return [string]::Empty
148 | }
--------------------------------------------------------------------------------
/Password Encryption for PowerShell scripts/New-StringEncription.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Encryps a string using RijndaelManaged Cryptography
4 |
5 | .DESCRIPTION
6 | New-StringEncryption is used to encrypt a string using RijndaelManaged Cryptography allowing
7 | user to specify Salt, Passphrase and Intersecting Vector values.
8 |
9 | For better security Intersecting Vector (IV) value should be changed on each machine where function is used.
10 |
11 | .PARAMETER StringToEncrypt
12 | Any string that needs to be encrypted. Parameter is mandatory and cannot be empty.
13 |
14 | .PARAMETER EncryptPassPhrase
15 | An optional passphrase to be used during the encryption process.
16 |
17 | If parameter is not specified hostname value will be used.
18 |
19 | .PARAMETER EncryptSalt
20 | Specify a custom string to use as the Encryption Salt.
21 |
22 | If parameter is not specified hostname value will be used.
23 |
24 | .PARAMETER IntersectingVector
25 | Intersecting Vector value used in the encryption process. If parameter is not used a
26 | default value is used but for security reasons this should be updated to a custom value.
27 |
28 | .EXAMPLE
29 | PS C:\> New-StringEncryption
30 | #>
31 |
32 | [OutputType([string])]
33 | param
34 | (
35 | [Parameter(Mandatory = $true)]
36 | [ValidateNotNullOrEmpty()]
37 | [Alias('string')]
38 | [string]
39 | $StringToEncrypt,
40 | [ValidateNotNullOrEmpty()]
41 | [Alias('PassPhrase', 'EncryptionPassPhrase')]
42 | [string]
43 | $EncryptPassPhrase,
44 | [ValidateNotNullOrEmpty()]
45 | [Alias('Salt', 'SaltValue')]
46 | [string]
47 | $EncryptSalt,
48 | [ValidateNotNullOrEmpty()]
49 | [Alias('Vector')]
50 | [string]
51 | $IntersectingVector = 'Q!L@2QTCYgsG'
52 | )
53 |
54 | # Instantiate empty return value
55 | [string]$EncryptedString = $null
56 |
57 | # Instantiate COM Object for RijndaelManaged Cryptography
58 | [System.Security.Cryptography.RijndaelManaged]$encryptionObject = New-Object System.Security.Cryptography.RijndaelManaged
59 |
60 | # Check if we have a passphrase
61 | if ([string]::IsNullOrEmpty($EncryptPassPhrase) -eq $true)
62 | {
63 | # Use hostname
64 | $EncryptPassPhrase = $env:Computername
65 | }
66 |
67 | # Check if we have a salt value
68 | if ([string]::IsNullOrEmpty($EncryptSalt) -eq $true)
69 | {
70 | # Use hostname
71 | $EncryptSalt = $env:Computername
72 | }
73 |
74 | # Convert Salt and Passphrase to UTF8 Bytes array
75 | [System.Byte[]]$byteEncryptSalt = [Text.Encoding]::UTF8.GetBytes($EncryptSalt)
76 | [System.Byte[]]$bytePassPhrase = [Text.Encoding]::UTF8.GetBytes($EncryptPassPhrase)
77 |
78 | # Create the Encryption Key using the passphrase, salt and SHA1 algorithm at 256 bits
79 | $encryptionObject.Key = (New-Object Security.Cryptography.PasswordDeriveBytes $bytePassPhrase,
80 | $byteEncryptSalt,
81 | 'SHA1',
82 | 5).GetBytes(32) # 256/8 - 32bytes
83 |
84 | # Create the Intersecting Vector (IV) Cryptology Hash with the init value
85 | $paramNewObject = @{
86 | TypeName = 'Security.Cryptography.SHA1Managed'
87 | }
88 | $encryptionObject.IV = (New-Object @paramNewObject).ComputeHash([Text.Encoding]::UTF8.GetBytes($IntersectingVector))[0 .. 15]
89 |
90 | # Starts the New Encryption using the Key and IV
91 | $encryptorObject = $encryptionObject.CreateEncryptor()
92 |
93 | # Creates a MemoryStream for encryption
94 | [System.IO.MemoryStream]$memoryStream = New-Object IO.MemoryStream
95 |
96 | # Creates the new Cryptology Stream --> Outputs to $MS or Memory Stream
97 | [System.Security.Cryptography.CryptoStream]$cryptoStream = New-Object Security.Cryptography.CryptoStream $memoryStream, $encryptorObject, 'Write'
98 |
99 | # Starts the new Cryptology Stream
100 | $cryptoStreamWriter = New-Object IO.StreamWriter $cryptoStream
101 |
102 | # Writes the string in the Cryptology Stream
103 | $cryptoStreamWriter.Write($StringToEncrypt)
104 |
105 | # Stops the stream writer
106 | $cryptoStreamWriter.Close()
107 |
108 | # Stops the Cryptology Stream
109 | $cryptoStream.Close()
110 |
111 | # Stops writing to Memory
112 | $memoryStream.Close()
113 |
114 | # Clears the IV and HASH from memory to prevent memory read attacks
115 | $encryptionObject.Clear()
116 |
117 | # Takes the MemoryStream and puts it to an array
118 | [byte[]]$result = $memoryStream.ToArray()
119 |
120 | # Converts the array from Base 64 to a string and returns
121 | $EncryptedString = $([Convert]::ToBase64String($result))
122 |
123 | # Return value
124 | return $EncryptedString
--------------------------------------------------------------------------------
/Password Encryption for PowerShell scripts/readme.md:
--------------------------------------------------------------------------------
1 | # Password Encryption for PowerShell scripts
2 |
3 | [Original code from PsCustomObject](https://github.com/PsCustomObject/IT-ToolBox)
4 |
5 | This two scripts generate a encrypted password and allow use it in PowerShell scripts.
6 |
7 | **New-StringEncryption.ps1**: generate a an encrypted string that can only be decrypted on the same machine that performed the original encryption.
8 |
9 | **New-StringDecryption.ps1**: takes a Base64 encoded string and will output the clear text version of it.
10 |
11 | ## Examples
12 |
13 | Encrypt password
14 |
15 | ```powershell
16 | New-StringEncryption.ps1 -StringToEncrypt 'Password'
17 | ```
18 | Returns ```fzdxB8+jXgchfghU98mbOc5g==```
19 |
20 | Decrypt password and use it
21 |
22 | ```powershell
23 | $365Username="admin@contoso.onmicrosoft.com"
24 | $365Password="fzdxB8+jXgchfghU98mbOc5g=="
25 | $Secure365AdminPassword = ConvertTo-SecureString -String (C:\Scripts\New-StringDecryption.ps1 -EncryptedString $365Password) -AsPlainText -Force
26 | $365Credentials = New-Object System.Management.Automation.PSCredential $365Username, $Secure365AdminPassword
27 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $365Credentials -Authentication Basic -AllowRedirection
28 | Import-PSSession $Session -AllowClobber | Out-Null
29 | # .....
30 | Remove-PSSession $Session
31 | ```
32 |
--------------------------------------------------------------------------------
/RZGet launcher for Intune/Install-Software.ps1:
--------------------------------------------------------------------------------
1 | ## ------------------------------------------------------------------
2 | # Arguments
3 | ## ------------------------------------------------------------------
4 | [string]$folder = 'C:\temp\InstallSoftware' #Folder to download RZGet.exe and save log.
5 | [string]$RZGetArguments = 'install 7-Zip Notepad++(x64) Edge "3CXPhone for Windows" "Google Chrome"' # RZget Arguments. Check https://github.com/rzander/ruckzuck/wiki/RZGet and https://ruckzuck.tools/Home/Repository
6 | # Mail Settings. If you do not want to send and email, leave empty $SMTPServer variable: $SMTPServer = ''
7 | [string]$SMTPServer = 'smtp.office365.com'
8 | [string[]]$recipient = 'juangranados@contoso.com', 'support@contoso.com'
9 | [String]$subject = "Installation on computer $env:COMPUTERNAME"
10 | [string]$sender = 'support@contoso.com'
11 | [string]$username = 'support@contoso.com'
12 | [string]$password = 'P@ssw0rd'
13 | [bool]$enableSsl = $true
14 | [int]$port = 587
15 | ## ------------------------------------------------------------------
16 | # function Invoke-Process
17 | # https://stackoverflow.com/a/66700583
18 | ## ------------------------------------------------------------------
19 | function Invoke-Process {
20 | param
21 | (
22 | [Parameter(Mandatory)]
23 | [ValidateNotNullOrEmpty()]
24 | [string]$FilePath,
25 |
26 | [Parameter()]
27 | [ValidateNotNullOrEmpty()]
28 | [string]$ArgumentList,
29 |
30 | [ValidateSet("Full", "StdOut", "StdErr", "ExitCode", "None")]
31 | [string]$DisplayLevel
32 | )
33 |
34 | $ErrorActionPreference = 'Stop'
35 |
36 | try {
37 | $pinfo = New-Object System.Diagnostics.ProcessStartInfo
38 | $pinfo.FileName = $FilePath
39 | $pinfo.RedirectStandardError = $true
40 | $pinfo.RedirectStandardOutput = $true
41 | $pinfo.UseShellExecute = $false
42 | $pinfo.WindowStyle = 'Hidden'
43 | $pinfo.CreateNoWindow = $true
44 | $pinfo.Arguments = $ArgumentList
45 | $p = New-Object System.Diagnostics.Process
46 | $p.StartInfo = $pinfo
47 | $p.Start() | Out-Null
48 | $result = [pscustomobject]@{
49 | Title = ($MyInvocation.MyCommand).Name
50 | Command = $FilePath
51 | Arguments = $ArgumentList
52 | StdOut = $p.StandardOutput.ReadToEnd()
53 | StdErr = $p.StandardError.ReadToEnd()
54 | ExitCode = $p.ExitCode
55 | }
56 | $p.WaitForExit()
57 |
58 | if (-not([string]::IsNullOrEmpty($DisplayLevel))) {
59 | switch ($DisplayLevel) {
60 | "Full" { return $result; break }
61 | "StdOut" { return $result.StdOut; break }
62 | "StdErr" { return $result.StdErr; break }
63 | "ExitCode" { return $result.ExitCode; break }
64 | }
65 | }
66 | }
67 | catch {
68 | Write-Host "An error has ocurred"
69 | }
70 | }
71 | if ($folder.Chars($folder.Length - 1) -eq '\') {
72 | $folder = ($folder.TrimEnd('\'))
73 | }
74 | if (!(Test-Path $folder)) {
75 | mkdir $folder
76 | }
77 | $transcriptFile = "$folder\$(get-date -Format yyyy_MM_dd)_InstallSoftware.txt"
78 | Start-Transcript $transcriptFile
79 | Write-Host "Checking for elevated permissions"
80 | if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
81 | Write-Warning "Insufficient permissions to run this script. Execute PowerShell script as an administrator."
82 | Stop-Transcript
83 | Exit
84 | }
85 | Write-Host "Script is elevated"
86 | Write-Host "Downloading RZGet.exe"
87 | Invoke-WebRequest "https://github.com/rzander/ruckzuck/releases/latest/download/RZGet.exe" -OutFile "$folder\RZGet.exe"
88 | Write-Host "Running $($folder)\RZGet.exe $($RZGetArguments)"
89 | Invoke-Process -FilePath "$folder\RZGet.exe" -ArgumentList $RZGetArguments -DisplayLevel StdOut
90 | Stop-Transcript
91 |
92 | if (!([string]::IsNullOrEmpty($SMTPServer))) {
93 | $msg = new-object Net.Mail.MailMessage
94 | $smtp = new-object Net.Mail.SmtpClient($SMTPServer, $port)
95 | $msg.From = $sender
96 | $msg.ReplyTo = $sender
97 | ForEach ($mail in $recipient) {
98 | $msg.To.Add($mail)
99 | }
100 | if ($username -ne "None" -and $password -ne "None") {
101 | $smtp.Credentials = new-object System.Net.NetworkCredential($username, $password)
102 | }
103 | $smtp.EnableSsl = $enableSsl
104 | $msg.subject = $subject
105 | $msg.body = Get-Content $transcriptFile -Raw
106 | try {
107 | Write-Output "Sending email"
108 | $smtp.Send($msg)
109 | }
110 | catch {
111 | Write-Host "Error sending email" -ForegroundColor Red
112 | write-host "Caught an exception:" -ForegroundColor Red
113 | write-host "Exception Type: $($_.Exception.GetType().FullName)" -ForegroundColor Red
114 | write-host "Exception Message: $($_.Exception.Message)" -ForegroundColor Red
115 | }
116 | }
--------------------------------------------------------------------------------
/Redirect Folder/Redirect-Folder.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Redirects user shell folders.
4 | .DESCRIPTION
5 | Redirects user shell folders to a custom path.
6 | .PARAMETER logPath
7 | Log file path.
8 | Default "Documents"
9 | Example: "\\ES-CPD-BCK02\scripts\FolderRedirection\Log"
10 | .PARAMETER folders
11 | Array of folders to redirect.
12 | Check folder names in HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
13 | Example: "Personal","My Pictures","Desktop"
14 | .PARAMETER paths
15 | Array of paths to redirect, in the same order than folders array.
16 | Example: "D:\%USERNAME%\Documents","D:\%USERNAME%\Pictures","D:\%USERNAME%\Desktop"
17 | .EXAMPLE
18 | .\Redirect-Folder -folders "Personal","My Pictures","Desktop" -paths "D:\%USERNAME%\Documents","D:\%USERNAME%\Pictures","D:\%USERNAME%\Desktop"
19 | .EXAMPLE
20 | .\Redirect-Folder -folders "Personal" -paths "\\SRV-FS01\Users\%USERNAME%\Documents" -logPath "\\SRV-DC01\Scripts\FolderRedirection\Log"
21 | .LINK
22 | https://github.com/juangranados/powershell-scripts/tree/main/Redirect%20Folder
23 | .NOTES
24 | Author: Juan Granados
25 | #>
26 | Param(
27 | [Parameter(Mandatory = $true)]
28 | [ValidateNotNullOrEmpty()]
29 | [string[]]$folders,
30 | [Parameter(Mandatory = $true)]
31 | [string[]]$paths,
32 | [Parameter(Mandatory = $false)]
33 | [string]$logPath = [Environment]::GetFolderPath("MyDocuments")
34 | )
35 | Function Set-RegistryKey ([string]$path, [string]$name, $value) {
36 |
37 | $currentValue = (Get-Item -Path $path).GetValue($name, $null, 'DoNotExpandEnvironmentNames')
38 | if (-not $currentValue) {
39 | Write-Host "Key $($path)\$($name) do not exist - Changing by $($value)" -ForegroundColor Yellow
40 | Set-ItemProperty -Path $path -name $name -Value $value -Type 'ExpandString'
41 | }
42 | elseif ($currentValue -ne $value) {
43 | Write-Host "Key $($path)\$($name) has a value of $($currentValue) - Changing by $($value)" -ForegroundColor Yellow
44 | Set-ItemProperty -Path $path -name $name -Value $value -Type 'ExpandString'
45 | }
46 | else {
47 | Write-Host "Key $($path)\$($name) already has the value of $($currentValue)" -ForegroundColor Green
48 | }
49 | }
50 | Start-Transcript $logPath
51 | $regKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
52 | $i = 0
53 | foreach ($folder in $folders) {
54 | Write-Host "Setting $folder as $($paths[$i])"
55 | Set-RegistryKey -path $regKey -name $folder -value $paths[$i]
56 | $i++
57 | }
58 | Stop-Transcript
59 |
--------------------------------------------------------------------------------
/Remote Computer Update/Launch-Remote.ps1:
--------------------------------------------------------------------------------
1 | $logPath = "\\ES-CPD-BCK02\scripts\WindowsUpdate\Log"
2 | $rebootMessage = "Se va a reiniciar el equipo dentro de 2 horas para terminar de instalar las actualizaciones de Windows. Por favor, cierra todo antes de esa hora o reinicia el equipo manualmente"
3 | $RZGetPath = "\\ES-CPD-BCK02\scripts\WindowsUpdate\RZGet.exe"
4 | [int]$rebootHours = 2
5 | \\SRVHTS-FS01\Scripts\RemoteComputerUpdate\Update-Computer.ps1 -logPath $logPath -scheduleReboot -rebootHours $rebootHours -rebootMessage $rebootMessage -RZGetPath $RZGetPath
--------------------------------------------------------------------------------
/Remote Computer Update/LaunchRemote.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | SETLOCAL
3 | SET computer=192.168.0.26
4 | SET scriptPath="\\SRVHTS-FS01\Scripts\RemoteComputerUpdate\Launch-Remote.ps1"
5 | psexec.exe -s \\%computer% powershell.exe -ExecutionPolicy Bypass -file %scriptPath%
6 | pause
--------------------------------------------------------------------------------
/Remote Computer Update/readme.md:
--------------------------------------------------------------------------------
1 | # Remote Computer Update
2 |
3 | Right click here and select "Save link as" to download
4 |
5 | Runs Windows Update and software update using RuckZuck on local or remote computer.
6 |
7 | In order to run in remote computer it has to be executed from [PsExec](https://docs.microsoft.com/en-us/sysinternals/downloads/psexec). See examples below.
8 |
9 | It uses [RZGet](https://github.com/rzander/ruckzuck/releases) to update computer software.
10 |
11 | 
12 |
13 | ## Parameters
14 |
15 | ### logPath
16 |
17 | Log file path.
18 | Default Documents
19 | Example: "\\ES-CPD-BCK02\scripts\ComputerUpdate\Log"
20 |
21 | ### scheduleReboot
22 |
23 | Reboot wil be scheduled if needed.
24 | Default: false
25 |
26 | ### rebootHours
27 |
28 | Number of hours after finish update to reboot computer.
29 | Default: 2
30 |
31 | ### rebootNow
32 |
33 | Reboots after finish update.
34 | Default: false
35 |
36 | ### rebootMessage
37 |
38 | Shows a message to user.
39 | Default: none
40 |
41 | ### RZGetPath
42 |
43 | RZGet.exe path.
44 | Example: \\SRV-FS05\RZGet\rzget.exe
45 | If path not found RZGet will not be called unless you set -downloadRZGet switch and it will be downloaded to this path.
46 | Default: none
47 |
48 | ### RZGetArguments
49 |
50 | RZGet.exe Arguments.
51 | Default: update --all
52 |
53 | ### downloadRZGet
54 |
55 | Download RZGet.exe latest version to RZGetPath
56 | Default: false
57 |
58 | RZGet.exe Arguments.
59 | Default: update --all
60 |
61 | ## Examples
62 |
63 | ### Run remotely
64 |
65 | #### Harcode parameters on script and run PsExec.
66 |
67 | ```bash
68 | psexec.exe -s \\ComputerName powershell.exe -ExecutionPolicy Bypass -file \\ES-CPD-BCK02\scripts\WindowsUpdate\Update-Computer.ps1
69 | ```
70 |
71 | #### Use an script to run with parameters.
72 |
73 | [LaunchRemote.cmd](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Remote%20Computer%20Update/LaunchRemote.cmd): run [LaunchRemote.ps1](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Remote%20Computer%20Update/LaunchRemote.ps1) with PsExec.
74 |
75 | [LaunchRemote.ps1](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Remote%20Computer%20Update/LaunchRemote.ps1): run [Update-Computer.ps1](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Remote%20Computer%20Update/Update-Computer.ps1) with arguments.
76 |
77 | ### Run locally
78 |
79 | ```powershell
80 | Update-Computer.ps1 -$logPath "\\ES-CPD-BCK02\scripts\WindowsUpdate\Log" -scheduleReboot -rebootHours 2 -rebootMessage "Computer will reboot in two hours. You can reboot now or it will reboot later" -RZGetPath "\\ES-CPD-BCK02\scripts\WindowsUpdate\RZGet.exe" $RZGetArguments 'update "7-Zip" "Google Chrome" "Notepad++(x64)" "AdobeReader DC"'
81 | ```
82 |
83 |
--------------------------------------------------------------------------------
/Remote Computer Update/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Remote Computer Update/screenshot.png
--------------------------------------------------------------------------------
/SQL Server Backup/BackupSQL.ps1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/SQL Server Backup/BackupSQL.ps1
--------------------------------------------------------------------------------
/SQL Server Backup/readme.md:
--------------------------------------------------------------------------------
1 | # SQL Server Backup
2 |
3 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/SQL%20Server%20Backup/BackupSQL.ps1)
4 |
5 | This script perform full and log backup of a SQL Server databases into a zip file.
6 |
7 | You can specify what databases will be saved and the remote location of backup, also it can delete old backup files to save disk space, e.g keeping one week of backups. It can send an email report with backup result.
8 |
9 | This script can be executed in the SQL Server you want to backup, but if you want to run it from a computer without SQL Server installed:
10 |
11 | 1. Download and install SQLSysClrTypes.msi from: [Microsoft® SQL Server® 2017 Feature Pack](https://www.microsoft.com/en-US/download/details.aspx?id=55992)
12 | 2. Run from PowerShell:
13 | - `Register-PackageSource -provider NuGet -name nugetRepository -location https://www.nuget.org/api/v2`
14 | - `Install-Package Microsoft.SqlServer.SqlManagementObjects` in case of error of circular dependency add `-SkipDependencies`
15 | 3. Run from PowerShell: `Install-Module -Name SqlServer`
16 |
17 | #### *Examples*
18 |
19 | Backup default instance databases to a network share
20 |
21 | `BackupSQL.ps1 -BackupDirectory \\FS-SERVER01\BackupSQL`
22 |
23 | Backup Windows Internal Database (WID) to a network share
24 |
25 | `BackupSQL.ps1 -BackupDirectory "\\MV0SRV-C01\Backups" -Instance "\\.\pipe\MICROSOFT##WID\tsql\query"`
26 |
27 | Backup default instance databases to a network share and send an email with result using gmail
28 |
29 | `BackupSQL.ps1 -BackupDirectory \\FS-SERVER01\BackupSQL -SMTPServer smtp.gmail.com -Recipient jgranados@contoso.com,administrator@contoso.com -Sender backupSQL@gmail.com -Username backupSQL@gmail.com -Password Pa$$W0rd -SSL True -Port 587`
30 |
31 | Backup named instance databases to a network share
32 |
33 | `BackupSQL.ps1 -BackupDirectory \\FS-SERVER01\BackupSQL -Instance SQLSVR01\BKUPEXEC`
34 |
35 | Backup default instance databases to a network share, delete from network share files older than a week and write result in Windows Application Event
36 |
37 | `BackupSQL.ps1 -BackupDirectory \\FS-SERVER01\BackupSQL -RetainDays 7 -WriteEvent True`
38 |
39 | Backup only specified databases of a named instance
40 |
41 | `BackupSQL.ps1 -Instance SQLSVR01\BKUPEXEC -DataBases BEDB,msdb,model`
42 |
--------------------------------------------------------------------------------
/System Center DPM 2012 (R2) HTML Report/DPMReport.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Create and send a protection group backup report of System Center Data Protection Manager (DPM) 2012 (R2) Servers
4 | .DESCRIPTION
5 | This script Create and send a protection group backup report of System Center Data Protection Manager (DPM) 2012 (R2) Servers based on the recovery points and protection groups
6 | Usage: DPMProtectionGroupReport.ps1 [-DPMServers ] [-ProtectionGroups ] [-MinLastBackupHours ] [-MinRecoveryPoints ] [ReportLocation ] [SMTPServer ] [Recipient ] [Sender ] [Username ] [Password ] [-SSL ] [Port ]
7 | .PARAMETER DPMPServers
8 | List of DPM servers to get report.
9 | Default: localhost
10 | .PARAMETER ProtectionGroups
11 | List of protection groups to get report.
12 | Default: All (Report will contain data of all protection groups)
13 | .PARAMETER MinLastBackupHours
14 | If last recovery point is older than MinLastBackupHours it will show with red background.
15 | Default: 24
16 | .PARAMETER MinRecoveryPoints
17 | If number of recovery points is less than MinRecoveryPoint it will show with red background.
18 | Default: 1
19 | .PARAMETER ReportLocation
20 | Path where report will be saved
21 | Default: None
22 | .PARAMETER SMTPServer
23 | Sets smtp server in order to sent an email with backup result.
24 | Default: None
25 | .PARAMETER Recipient
26 | Array of emails addresses which will receive the backup result.
27 | Default: None
28 | .PARAMETER Sender
29 | Email address which will send the backup result.
30 | Default: None
31 | .PARAMETER Username
32 | Username in case of smtp server requires authentication.
33 | Default: None
34 | .PARAMETER Password
35 | Password in case of smtp server requires authentication.
36 | Default: None
37 | .PARAMETER SSL
38 | Use of SSL in case of smtp server requires SSL.
39 | Default: False
40 | .PARAMETER Port
41 | Port to connect to smtp server.
42 | Default: 25
43 | .EXAMPLE
44 | DPMReport.ps1 -DPMServers DPM01,DPM02,DPM03 -HtmlReport \\SERVER1\Reports\ -SMTPServer mail.contoso.com -Sender soporte@contoso.com -Recipient jgranados@contoso.com,administrador@contoso.com -Username contoso\jgranados -Password P@ssw0rd
45 | .NOTES
46 | Author: Juan Granados
47 | Date: July 2017
48 | #>
49 |
50 | Param(
51 | [Parameter(Mandatory=$false,Position=0)]
52 | [ValidateNotNullOrEmpty()]
53 | [string[]]$DPMServers=$env:computername,
54 | [Parameter(Mandatory=$false,Position=1)]
55 | [ValidateNotNullOrEmpty()]
56 | [string[]]$ProtectionGroups="All",
57 | [Parameter(Mandatory=$false,Position=2)]
58 | [ValidateNotNullOrEmpty()]
59 | [int]$MinLastBackupHours=24,
60 | [Parameter(Mandatory=$false,Position=2)]
61 | [ValidateNotNullOrEmpty()]
62 | [int]$MinRecoveryPoints=1,
63 | [Parameter(Mandatory=$false,Position=3)]
64 | [ValidateNotNullOrEmpty()]
65 | [string]$ReportLocation="None",
66 | [Parameter(Mandatory=$false,Position=4)]
67 | [ValidateNotNullOrEmpty()]
68 | [string]$SMTPServer="None",
69 | [Parameter(Mandatory=$false,Position=5)]
70 | [ValidateNotNullOrEmpty()]
71 | [string[]]$Recipient,
72 | [Parameter(Mandatory=$false,Position=6)]
73 | [ValidateNotNullOrEmpty()]
74 | [string]$Sender,
75 | [Parameter(Mandatory=$false,Position=7)]
76 | [ValidateNotNullOrEmpty()]
77 | [string]$Username="None",
78 | [Parameter(Mandatory=$false,Position=8)]
79 | [ValidateNotNullOrEmpty()]
80 | [string]$Password="None",
81 | [Parameter(Mandatory=$false,Position=9)]
82 | [ValidateSet("True","False")]
83 | [string[]]$SSL="False",
84 | [Parameter(Mandatory=$false,Position=10)]
85 | [ValidateNotNullOrEmpty()]
86 | [int]$Port=25
87 | )
88 |
89 | $ErrorActionPreference = "silentlycontinue"
90 |
91 | if (Get-Module -ListAvailable -Name DataProtectionManager) {
92 | Import-Module DataProtectionManager
93 | } else {
94 | Write-Host "Module DataProtectionManager does not exist. Script can not continue"
95 | Exit
96 | }
97 |
98 | $DomainName=((Get-WmiObject Win32_ComputerSystem).Domain).toupper()
99 |
100 | $HTMLFile = @"
101 |
102 |
103 |
104 |
105 | DPM Backup Status
106 |
116 |
117 |
118 |
194 |
195 |
196 | "@
197 | if ([boolean](get-variable "ReportPath" -ErrorAction SilentlyContinue))
198 | {Clear-Variable -Name "ReportPath" -Scope Global}
199 | $ReportPath = $ReportPath + "$timestamp" + "_WSBReport.html"
200 | try{
201 | ConvertTo-HTML -Body $HTMLFile -title "Windows Server Backup Report" | Out-File $ReportPath
202 | }catch
203 | {
204 | Write-Host "Error storing htlm report" -ForegroundColor Red
205 | write-host "Caught an exception:" -ForegroundColor Red
206 | write-host "Exception Type: $($_.Exception.GetType().FullName)" -ForegroundColor Red
207 | write-host "Exception Message: $($_.Exception.Message)" -ForegroundColor Red
208 | }
209 | # Mail sending
210 | if($SMTPServer -ne "None")
211 | {
212 | #Creating a Mail object
213 | $msg = new-object Net.Mail.MailMessage
214 |
215 | #Creating SMTP server object
216 | $smtp = new-object Net.Mail.SmtpClient($SMTPServer,$Port)
217 |
218 | #Email structure
219 | $msg.From = $Sender
220 | $msg.ReplyTo = $Sender
221 | ForEach($mail in $Recipient)
222 | {
223 | $msg.To.Add($mail)
224 | }
225 | if ($Username -ne "None" -and $Password -ne "None")
226 | {
227 | $smtp.Credentials = new-object System.Net.NetworkCredential($Username, $Password)
228 | }
229 | if ($SSL -ne "False")
230 | {
231 | $smtp.EnableSsl = $true
232 | }
233 | #Email subject
234 | $msg.subject = "Windows Server Backup Status"
235 | #Email body
236 | $msg.body = $HTMLFile
237 | $msg.IsBodyHtml = $true
238 | #Sending email
239 | try{
240 | Write-Output "Sending email"
241 | $smtp.Send($msg)
242 | }catch
243 | {
244 | Write-Host "Error sending email" -ForegroundColor Red
245 | write-host "Caught an exception:" -ForegroundColor Red
246 | write-host "Exception Type: $($_.Exception.GetType().FullName)" -ForegroundColor Red
247 | write-host "Exception Message: $($_.Exception.Message)" -ForegroundColor Red
248 | }
249 | }
--------------------------------------------------------------------------------
/Windows Server Backup Email Report of Several Servers/Servers.txt:
--------------------------------------------------------------------------------
1 | SRV01
2 | SRV02
3 | SRV03
4 | SRV04
5 | SRV05
6 | SRV06
7 | SRV07
8 | SRV08
9 | SRV09
10 | SRV010
11 | SRV011
12 | SRV012
--------------------------------------------------------------------------------
/Windows Server Backup Email Report of Several Servers/readme.md:
--------------------------------------------------------------------------------
1 | # Windows Server Backup Email Report of Several Servers
2 |
3 | [Right click here and select "Save link as" to download](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Windows%20Server%20Backup%20Email%20Report%20of%20Several%20Servers/Get-WSBReport.ps1)
4 |
5 | Performs a Windows Server Backup HTML report of a servers list and send it via email.
6 |
7 | 
8 |
9 | It gets server list from a file called 'Servers.txt'. [Right click here and select "Save link as" to download an example of 'Servers.txt'](https://raw.githubusercontent.com/juangranados/powershell-scripts/main/Windows%20Server%20Backup%20Email%20Report%20of%20Several%20Servers/Servers.txt)
10 |
11 | Requires Windows Server Backup Command Line Tools installed on remote servers. On each server, open PowerShell console as administrator and run `Add-WindowsFeature Backup-Tools`
12 |
13 | *Example*
14 |
15 | ```powershell
16 | Get-WSBReport.ps1 -ServerList C:\Scripts\servers_contoso.txt -HtmlReport \\SERVER1\Reports\ -SMTPServer mail.contoso.com -Sender soporte@contoso.com -Recipient jgranados@contoso.com,administrador@contoso.com -Username contoso\jgranados -Password P@ssw0rd
17 | ```
18 |
19 | *Full description*
20 |
21 | ```powershell
22 | <#
23 | .SYNOPSIS
24 | This script collect information about Windows Server Backup on a list of servers.
25 | Requires Windows Server Backup Command Line Tools installed on remote servers (Add-WindowsFeature Backup-Tools)
26 | .DESCRIPTION
27 | This script collect information about Windows Server Backup on a list of servers and show results on console. It has the posibility of generate an send an html report with backup results.
28 | Usage: Get-WSBReport.ps1 [-Servers ] [-HtmlReport ] [SMTPServer ] [Recipient ] [Sender ] [Username ] [Password ] [-SSL ] [Port ]
29 | .PARAMETER Servers
30 | Full path of a file containing the list of servers to check Windows Server Backup Status
31 | Default "C:\Scripts\Servers.txt"
32 | .PARAMETER HtmlReport
33 | Folder to store html report file.
34 | Default "C:\Scripts\"
35 | .PARAMETER SMTPServer
36 | Sets smtp server in order to sent an email with backup result.
37 | Default: None
38 | .PARAMETER Recipient
39 | List of emails addresses which will receive the backup result separated by commas.
40 | Default: None
41 | .PARAMETER Sender
42 | Email address which will send the backup result.
43 | Default: None
44 | .PARAMETER Username
45 | Username in case of smtp server requires authentication.
46 | Default: None
47 | .PARAMETER Password
48 | Password in case of smtp server requires authentication.
49 | Default: None
50 | .PARAMETER SSL
51 | Use of SSL in case of smtp server requires SSL.
52 | Default: False
53 | .PARAMETER Port
54 | Port to connect to smtp server.
55 | Default: 25
56 | .EXAMPLE
57 | .\Get-WSBReport.ps1 -ServerList C:\Scripts\servers_contoso.txt -HtmlReport \\SERVER1\Reports\ -SMTPServer mail.contoso.com -Sender soporte@contoso.com -Recipient jgranados@contoso.com,administrador@contoso.com -Username contoso\jgranados -Password P@ssw0rd
58 | .NOTES
59 | Author: Juan Granados
60 | #>
61 | ```
62 |
63 |
--------------------------------------------------------------------------------
/Windows Server Backup Email Report of Several Servers/wsb.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangranados/powershell-scripts/72a4a09a77f385461c2e427359d8e98808084029/Windows Server Backup Email Report of Several Servers/wsb.PNG
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Script index
2 |
3 | Click in any link to read full description
4 |
5 | - [Install software on multiple computers remotely with PowerShell](https://github.com/juangranados/powershell-scripts/tree/main/Install%20Software%20Remotely): install software remotely in a group of computers and retry the installation in case of error. It uses PowerShell to perform remote installation, WMI and reg query to filter target computers.
6 | - [Windows Server Backup Report of several servers](https://github.com/juangranados/powershell-scripts/tree/main/Windows%20Server%20Backup%20Email%20Report%20of%20Several%20Servers): performs a Windows Server Backup HTML report of a servers list and send it via email.
7 | - [Change Lock Screen and Desktop Background in Windows 10 Pro](https://github.com/juangranados/powershell-scripts/tree/main/Change%20Lock%20Screen%20and%20Desktop%20Background%20in%20Windows%2010%20Pro): change logon screen and desktop background in Windows 10 Professional using GPO startup script.
8 | - [GUI Password Reset Tool for Active Directory](https://github.com/juangranados/powershell-scripts/tree/main/GUI%20Password%20Reset%20Tool%20for%20Active%20Directory): script for IT support team which allow to reset AD users password in a GUI interface.
9 | - [Optimize and cleanup of WSUS](https://github.com/juangranados/powershell-scripts/tree/main/Optimize%20and%20cleanup%20of%20WSUS%20on%20Windows%20Server%202012%20R2%20and%202016): Optimize WSUS DB using official Microsoft SQL script and performs server maintenance. Supports Server 2019, 2016 and 2012 R2.
10 | - [Remote computer update](https://github.com/juangranados/powershell-scripts/tree/main/Remote%20Computer%20Update): Launch Windows Update and software update using RuckZuck on remote or local computers.
11 | - [File Server Access Audit Report](https://github.com/juangranados/powershell-scripts/tree/main/File%20Server%20Access%20Audit%20Report%20with%20PowerShell): audit several file servers and send a report in CSV and HTML by mail. HTML report can filter and sorting rows by server, time, user, file or operation (read, delete or write). CSV file can be import on Excel to generate a File Audit Report.
12 | - [Delete reappearing printers that keeps coming back](https://github.com/juangranados/powershell-scripts/tree/main/Delete%20reappearing%20printers%20that%20keeps%20comming%20back): delete ghost printers that keeps coming back after deletion.
13 | - [Email Report of File Permissions on HTML and CSV](https://github.com/juangranados/powershell-scripts/tree/main/Email%20Report%20of%20File%20Permissions%20on%20HTML%20and%20CSV): starting with a root folder, it generates a folders permissions report. Number of sub folders examined depends on FolderDeep parameter. Report is generated in CSV format and can be send attached via mail with a HTML report in the body.
14 | - [SQL Server Backup](https://github.com/juangranados/powershell-scripts/tree/main/SQL%20Server%20Backup): perform full and log backup of Microsoft SQL Server databases into a zip file. You can specify what databases will be saved and the remote location of backup.
15 | - [Install Printer Drivers Remotely](https://github.com/juangranados/powershell-scripts/tree/main/Install%20Print%20Drivers%20Remotely): allows remote printer drivers installation on a group of computers or servers.
16 | - [Bulk Change PrinterDriverAttributes for non Package-Aware printer drivers](https://github.com/juangranados/powershell-scripts/tree/main/Bulk%20Change%20PrinterDriverAttributes%20for%20non%20Package-Aware%20printer%20drivers): since KB3170455, deployed Printers via Group Policy does not install non Package-Aware printer drivers automatically. Users are prompted with a message saying "Do yo trust this printer?"
17 | - [Windows Maintenance](https://github.com/juangranados/powershell-scripts/tree/main/Windows%20Mainteinance): Performs several commands to check and repair a Windows Computer, Server or Workstation.
18 | - [System Center DPM 2012 (R2) HTML Report](https://github.com/juangranados/powershell-scripts/tree/main/System%20Center%20DPM%202012%20(R2)%20HTML%20Report): create and send a protection group backup report of System Center Data Protection Manager (DPM) 2012 (R2) Servers based on the recovery points and protection groups.
19 |
--------------------------------------------------------------------------------