This commit is contained in:
2023-07-04 12:59:44 +02:00
parent 2cef42a718
commit 09c2faad93
231 changed files with 261001 additions and 4 deletions

View File

@ -0,0 +1,8 @@
# https://docs.aws.amazon.com/cli/latest/userguide/awscli-install-windows.html
$dlurl = "https://s3.amazonaws.com/aws-cli/AWSCLI64PY3.msi"
$installerPath = Join-Path $env:TEMP (Split-Path $dlurl -Leaf)
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest $dlurl -OutFile $installerPath
Start-Process -FilePath msiexec -Args "/i $installerPath /passive" -Verb RunAs -Wait
Remove-Item $installerPath
$env:Path += ";C:\Program Files\Amazon\AWSCLI\bin"

View File

@ -0,0 +1,28 @@
# Silently install Adobe Reader DC with Microsoft Intune
# In order to distribute Adobe Acrobat Reader DC software you need to have
# a valid Adobe Acrobat Reader DC Distribution Agreement in place.
# See http://www.adobe.com/products/acrobat/distribute.html?readstep for details.
# Check if Software is installed already in registry.
$CheckADCReg = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | where {$_.DisplayName -like "Adobe Acrobat Reader DC*"}
# If Adobe Reader is not installed continue with script. If it's istalled already script will exit.
If ($CheckADCReg -eq $null) {
# Path for the temporary downloadfolder. Script will run as system so no issues here
$Installdir = "c:\temp\install_adobe"
New-Item -Path $Installdir -ItemType directory
# Download the installer from the Adobe website. Always check for new versions!!
$source = "ftp://ftp.adobe.com/pub/adobe/reader/win/AcrobatDC/1800920044/AcroRdrDC1800920044_fr_FR.exe"
$destination = "$Installdir\AcroRdrDC1800920044_fr_FR.exe"
Invoke-WebRequest $source -OutFile $destination
# Start the installation when download is finished
Start-Process -FilePath "$Installdir\AcroRdrDC1800920044_fr_FR.exe" -ArgumentList "/sAll /rs /rps /msi /norestart /quiet EULA_ACCEPT=YES"
# Wait for the installation to finish. Test the installation and time it yourself. I've set it to 240 seconds.
Start-Sleep -s 240
# Finish by cleaning up the download. I choose to leave c:\temp\ for future installations.
rm -Force $Installdir\AcroRdrDC*
}

View File

@ -0,0 +1,236 @@
<#
.SYNOPSIS
Get-EdgeEnterpriseMSI
.DESCRIPTION
Imports all device configurations in a folder to a specified tenant
.PARAMETER Channel
Channel to download, Valid Options are: Dev, Beta, Stable, EdgeUpdate, Policy.
.PARAMETER Platform
Platform to download, Valid Options are: Windows or MacOS, if using channel "Policy" this should be set to "any"
Defaults to Windows if not set.
.PARAMETER Architecture
Architecture to download, Valid Options are: x86, x64, arm64, if using channel "Policy" this should be set to "any"
Defaults to x64 if not set.
.PARAMETER Version
If set the script will try and download a specific version. If not set it will download the latest.
.PARAMETER Folder
Specifies the Download folder
.PARAMETER Force
Overwrites the file without asking.
.NOTES
Version: 1.2
Author: Mattias Benninge
Creation Date: 2020-07-01
Version history:
1.0 - Initial script development
1.1 - Fixes and improvements by @KarlGrindon
- Script now handles multiple files for e.g. MacOS Edge files
- Better error handling and formating
- URI Validation
1.2 - Better compability on servers (force TLS and remove dependency to IE)
https://docs.microsoft.com/en-us/mem/configmgr/apps/deploy-use/deploy-edge
.EXAMPLE
Download the latest version for the Beta channel and overwrite any existing file
.\Get-EdgeEnterpriseMSI.ps1 -Channel Beta -Folder D:\SourceCode\PowerShell\Div -Force
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $True, HelpMessage = 'Channel to download, Valid Options are: Dev, Beta, Stable, EdgeUpdate, Policy')]
[ValidateSet('Dev', 'Beta', 'Stable', 'EdgeUpdate', 'Policy')]
[string]$Channel = "Stable",
[Parameter(Mandatory = $True, HelpMessage = 'Folder where the file will be downloaded')]
[ValidateNotNullOrEmpty()]
[string]$Folder = "C:\temp",
[Parameter(Mandatory = $false, HelpMessage = 'Platform to download, Valid Options are: Windows or MacOS')]
[ValidateSet('Windows', 'MacOS', 'any')]
[string]$Platform = "Windows",
[Parameter(Mandatory = $false, HelpMessage = "Architecture to download, Valid Options are: x86, x64, arm64, any")]
[ValidateSet('x86', 'x64', 'arm64', 'any')]
[string]$Architecture = "x64",
[parameter(Mandatory = $false, HelpMessage = "Specifies which version to download")]
[ValidateNotNullOrEmpty()]
[string]$ProductVersion,
[parameter(Mandatory = $false, HelpMessage = "Overwrites the file without asking")]
[Switch]$Force
)
$ErrorActionPreference = "Stop"
$edgeEnterpriseMSIUri = 'https://edgeupdates.microsoft.com/api/products?view=enterprise'
# Validating parameters to reduce user errors
if ($Channel -eq "Policy" -and ($Architecture -ne "Any" -or $Platform -ne "Any")) {
Write-Warning ("Channel 'Policy' requested, but either 'Architecture' and/or 'Platform' is not set to 'Any'.
Setting Architecture and Platform to 'Any'")
$Architecture = "Any"
$Platform = "Any"
}
elseif ($Channel -ne "Policy" -and ($Architecture -eq "Any" -or $Platform -eq "Any")) {
throw "If Channel isn't set to policy, architecture and/or platform can't be set to 'Any'"
}
elseif ($Channel -eq "EdgeUpdate" -and ($Architecture -ne "x86" -or $Platform -eq "Windows")) {
Write-Warning ("Channel 'EdgeUpdate' requested, but either 'Architecture' is not set to x86 and/or 'Platform'
is not set to 'Windows'. Setting Architecture to 'x86' and Platform to 'Windows'")
$Architecture = "x86"
$Platform = "Windows"
}
Write-Host "Enabling connection over TLS for better compability on servers" -ForegroundColor Green
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
# Test if HTTP status code 200 is returned from URI
try {
Invoke-WebRequest $edgeEnterpriseMSIUri -UseBasicParsing | Where-Object StatusCode -match 200 | Out-Null
}
catch {
throw "Unable to get HTTP status code 200 from $edgeEnterpriseMSIUri. Does the URL still exist?"
}
Write-Host "Getting available files from $edgeEnterpriseMSIUri" -ForegroundColor Green
# Try to get JSON data from Microsoft
try {
$response = Invoke-WebRequest -Uri $edgeEnterpriseMSIUri -Method Get -ContentType "application/json" -UseBasicParsing -ErrorVariable InvokeWebRequestError
$jsonObj = ConvertFrom-Json $([String]::new($response.Content))
Write-Host "Succefully retrived data" -ForegroundColor Green
}
catch {
throw "Could not get MSI data: $InvokeWebRequestError"
}
# Alternative is to use Invoke-RestMethod to get a Json object directly
# $jsonObj = Invoke-RestMethod -Uri "https://edgeupdates.microsoft.com/api/products?view=enterprise" -UseBasicParsing
$selectedIndex = [array]::indexof($jsonObj.Product, "$Channel")
if (-not $ProductVersion) {
try {
Write-host "No version specified, getting the latest for $Channel" -ForegroundColor Green
$selectedVersion = (([Version[]](($jsonObj[$selectedIndex].Releases |
Where-Object { $_.Architecture -eq $Architecture -and $_.Platform -eq $Platform }).ProductVersion) |
Sort-Object -Descending)[0]).ToString(4)
Write-Host "Latest Version for channel $Channel is $selectedVersion`n" -ForegroundColor Green
$selectedObject = $jsonObj[$selectedIndex].Releases |
Where-Object { $_.Architecture -eq $Architecture -and $_.Platform -eq $Platform -and $_.ProductVersion -eq $selectedVersion }
}
catch {
throw "Unable to get object from Microsoft. Check your parameters and refer to script help."
}
}
else {
Write-Host "Matching $ProductVersion on channel $Channel" -ForegroundColor Green
$selectedObject = ($jsonObj[$selectedIndex].Releases |
Where-Object { $_.Architecture -eq $Architecture -and $_.Platform -eq $Platform -and $_.ProductVersion -eq $ProductVersion })
if (-not $selectedObject) {
throw "No version matching $ProductVersion found in $channel channel for $Architecture architecture."
}
else {
Write-Host "Found matching version`n" -ForegroundColor Green
}
}
if (Test-Path $Folder) {
foreach ($artifacts in $selectedObject.Artifacts) {
# Not showing the progress bar in Invoke-WebRequest is quite a bit faster than default
$ProgressPreference = 'SilentlyContinue'
Write-host "Starting download of: $($artifacts.Location)" -ForegroundColor Green
# Work out file name
$fileName = Split-Path $artifacts.Location -Leaf
if (Test-Path "$Folder\$fileName" -ErrorAction SilentlyContinue) {
if ($Force) {
Write-Host "Force specified. Will attempt to download and overwrite existing file." -ForegroundColor Green
try {
Invoke-WebRequest -Uri $artifacts.Location -OutFile "$Folder\$fileName" -UseBasicParsing
}
catch {
throw "Attempted to download file, but failed: $error[0]"
}
}
else {
# CR-someday: There should be an evaluation of the file version, if possible. Currently the function only
# checks if a file of the same name exists, not if the versions differ
Write-Host "$Folder\$fileName already exists!" -ForegroundColor Yellow
do {
$overWrite = Read-Host -Prompt "Press Y to overwrite or N to quit."
}
# -notmatch is case insensitive
while ($overWrite -notmatch '^y$|^n$')
if ($overWrite -match '^y$') {
Write-Host "Starting Download" -ForegroundColor Green
try {
Invoke-WebRequest -Uri $artifacts.Location -OutFile "$Folder\$fileName" -UseBasicParsing
}
catch {
throw "Attempted to download file, but failed: $error[0]"
}
}
else {
Write-Host "File already exists and user chose not to overwrite, exiting script." -ForegroundColor Red
exit
}
}
}
else {
Write-Host "Starting Download" -ForegroundColor Green
try {
Invoke-WebRequest -Uri $artifacts.Location -OutFile "$Folder\$fileName" -UseBasicParsing
}
catch {
throw "Attempted to download file, but failed: $error[0]"
}
}
if (((Get-FileHash -Algorithm $artifacts.HashAlgorithm -Path "$Folder\$fileName").Hash) -eq $artifacts.Hash) {
Write-Host "Calculated checksum matches known checksum`n" -ForegroundColor Green
}
else {
Write-Warning "Checksum mismatch!"
Write-Warning "Expected Hash: $($artifacts.Hash)"
Write-Warning "Downloaded file Hash: $((Get-FileHash -Algorithm $($artifacts.HashAlgorithm) -Path "$Folder\$fileName").Hash)`n"
}
}
}
Else {
throw "Folder $Folder does not exist"
}
Write-Host "-- Script Completed: File Downloaded -- " -ForegroundColor Green
Write-Host "Installing..." -ForegroundColor Green
cmd /c start /wait msiexec /i "$Folder\$fileName" /qn /norestart
Set-Service edgeupdate -StartupType Manual -ErrorAction SilentlyContinue
Set-Service edgeupdatem -StartupType Manual -ErrorAction SilentlyContinue
Unregister-ScheduledTask -TaskName MicrosoftEdgeUpdateTaskMachineCore -Confirm:$false -ErrorAction SilentlyContinue
Unregister-ScheduledTask -TaskName MicrosoftEdgeUpdateTaskMachineUA -Confirm:$false -ErrorAction SilentlyContinue
Write-Host "Installed Microsoft Edge" -ForegroundColor Green

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,126 @@
<#
===========================================================================
Created on: 0/01/2021 13:06
Created by: Ben Whitmore
Organization: -
Filename: Install_Flash_Removal_KB4577586.ps1
Target System: Windows 10 , Windows Server 2012/R2 | 2016 | 2019 | 1903 | 1909 | 2004
===========================================================================
Version:
1.2.1 - 22/01/2021
Added support for Server OS - Thanks @Hoorge for the suggestion
1.2 - 04/01/2021
Fixed 20H2 coding error - Credit @AndyUpperton
1.1 02/01/2021
Basic Transcript Logging added
1.0 - 01/01/2021
Release
#>
#Set Current Directory
$ScriptPath = $MyInvocation.MyCommand.Path
$CurrentDir = Split-Path $ScriptPath
$Log = Join-Path $ENV:TEMP "Flash_Uninstall.log"
Start-Transcript $Log
#Set WUSA.EXE Variable
$WUSA = "$env:systemroot\System32\wusa.exe"
#Get OS Product Name
$OS_ProductName = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion' ProductName).ProductName
#Build OS Version String
Switch ($OS_ProductName) {
{ $_.StartsWith("Windows 10") } { $OS_String = ($OS_ProductName -split "\s+" | Select-Object -First 2) -Join ' ' }
{ $_.StartsWith("Windows Server 2012 R2") } { $OS_String = ($OS_ProductName -split "\s+" | Select-Object -First 4) -Join ' ' }
{ ($_.StartsWith("Windows Server") -and (!($_.Contains("R2")))) } { $OS_String = ($OS_ProductName -split "\s+" | Select-Object -First 3) -Join ' ' }
}
#Get OS Release ID for valid OS's
If (!($OS_String -match "Server 2012")) {
$OS_ReleaseID = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion' ReleaseId).ReleaseId
}
else {
Write-Output "Skipping check of Release ID for $($OS_ProductName)"
}
#Rename $OS_ReleaseID variable for "Windows 10 20H2" and "Windows Server, version 1909" because the same KB update is used for both 2004 and 2009
If (($OS_ReleaseID -eq "2009" -and $OS_ProductName -match "Windows 10")) {
$OS_ReleaseID = "2004"
}
#Build OS Version Name variable
Switch ($OS_String) {
{ $_.Equals("Windows 10") } { $Version_String = $OS_String + " Version " + $OS_ReleaseID }
{ $_.StartsWith("Windows Server 2") } { $Version_String = $OS_String }
{ $_.StartsWith("Windows Server,") } { $Version_String = $OS_String + $OS_ReleaseID }
}
#Get OS Architecture
$OS_Architecture = Switch (Get-CIMInstance -Namespace "ROOT\CIMV2" -Class "Win32_Processor" | Select-Object -Unique -ExpandProperty Architecture) {
9 { 'x64-based' }
0 { 'x86-based' }
5 { 'ARM64-based' }
}
$PatchRequired = "Update for Removal of Adobe Flash Player for " + $Version_String + " for " + $OS_Architecture + " systems (KB4577586)"
#Get Patch Titles
$PatchNames = Get-ChildItem $CurrentDir | Where-Object { $_.PSIsContainer } | Foreach-Object { $_.Name }
#Check if the patch has been downloaded for the current system
$PatchFound = $False
#Check Installation
$Patch = Get-Hotfix | Where-Object { $_.HotFixID -match "KB4577586" }
If ($Patch) {
Write-Host "Patch Already Installed"
}
else {
Foreach ($Patch in $PatchNames) {
If ($Patch -eq $PatchRequired) {
$PatchFound = $True
#Get MSU from the correct Directory
$MSU = Get-ChildItem (Join-Path $CurrentDir $Patch) -Recurse | Where-Object { $_.Extension -eq ".msu" }
$MSUFullPath = Join-Path (Join-Path $CurrentDir $PatchRequired) $MSU.Name
#Set WUSA Args
$Args = @(
"""$MSUFullPath"""
"/quiet"
"/norestart"
)
}
}
#Patch detection determines outcome
If ($PatchFound) {
Write-Host "Patch found for this system"
Write-Host "Patch Required: $($PatchRequired)"
Write-Host "Patch Name: $($MSU.Name)"
Write-Host "Installing Update..."
#Install Patch
Start-Process -FilePath $WUSA -ArgumentList $Args -Wait
#Check Installation
$Patch = Get-Hotfix | Where-Object { $_.HotFixID -match "KB4577586" }
If ($Patch) {
Write-Host "Patch Installed Successfully"
}
Else {
Write-Warning "Patch Installation Failed"
}
}
Else {
Write-Host "Patch not found for this system"
Write-Host "Patch Required: $($PatchRequired)"
}
}
Stop-Transcript

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
# Modern websites require TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
#requires -RunAsAdministrator
# Let's go directly to the website and see what it lists as the current version
$BaseUri = "https://notepad-plus-plus.org"
$BasePage = Invoke-WebRequest -Uri $BaseUri -UseBasicParsing
$ChildPath = $BasePage.Links | Where-Object { $_.outerHTML -like '*Current Version*' } | Select-Object -ExpandProperty href
# Now let's go to the latest version's page and find the installer
$DownloadPageUri = $BaseUri + $ChildPath
$DownloadPage = Invoke-WebRequest -Uri $DownloadPageUri -UseBasicParsing
# Determine bit-ness of O/S and download accordingly
if ( [System.Environment]::Is64BitOperatingSystem ) {
$DownloadUrl = $DownloadPage.Links | Where-Object { $_.outerHTML -like '*npp.*.Installer.x64.exe"*' } | Select-Object -ExpandProperty href -Unique
} else {
$DownloadUrl = $DownloadPage.Links | Where-Object { $_.outerHTML -like '*npp.*.Installer.exe"*' } | Select-Object -ExpandProperty href -Unique
}
Write-Host "Downloading the latest Notepad++ to the temp folder"
Invoke-WebRequest -Uri $DownloadUrl -OutFile "$env:TEMP\$( Split-Path -Path $DownloadUrl -Leaf )" | Out-Null
Write-Host "Installing the latest Notepad++"
Start-Process -FilePath "$env:TEMP\$( Split-Path -Path $DownloadUrl -Leaf )" -ArgumentList "/S" -Wait

View File

@ -0,0 +1,437 @@
<#
tes
.SYNOPSIS
Uninstalls all Zoom applications registered with the Windows installer.
Whether they are installed to the local computer or the users profile.
.DESCRIPTION
Searches registry for applications registered with 'Zoom' as publisher.
If any found, the uninstall string is retrieved and used to uninstall the application.
If applications are found to be installed in the users profile, the users context is invoked and the application is uninstalled coming from SYSTEM context.
.NOTES
Filename: Uninstall-EverythingZoom.ps1
Version: 1.0
Author: Martin Bengtsson
Blog: www.imab.dk
Twitter: @mwbengtsson
.LINK
https://www.imab.dk/uninstall-all-zoom-applications-in-a-jiffy-using-configuration-manager-and-powershell/
#>
function Execute-AsLoggedOnUser($Command,$Hidden=$true) {
<#
.SYNOPSIS
Function that can execute powershell in the context of the logged-in user.
.DESCRIPTION
This function will use advanced API's to get the access token of the currently logged-in user, in order to execute a script in the users context.
This is useful for scripts that are run in the local system users context.
.REQUIREMENTS
This script myst be run from the context of the SYSTEM account.
Designes to be run by Intune or SCCM Agent.
Absolute paths required.
.EXAMPLE
Running a powershell script visible to the user
$userCommand = '-file c:\windows\temp\script.ps1'
executeAsLoggedOnUser -Command $userCommand -Hidden $false
.EXAMPLE
Running a powershell command hidden from the user (hidden is default true)
$userCommand = '-command &{remove-item c:\temp\secretfile.txt}'
executeAsLoggedOnUser -Command $userCommand
.COPYRIGHT
MIT License, feel free to distribute and use as you like, please leave author information.
.AUTHOR
Michael Mardahl - @michael_mardahl on twitter - BLOG: https://www.iphase.dk
C# borrowed from the awesome Justin Myrray (https://github.com/murrayju/CreateProcessAsUser)
.DISCLAIMER
This function is provided AS-IS, with no warranty - Use at own risk!
#>
$csharpCode = @"
using System;
using System.Runtime.InteropServices;
namespace murrayju.ProcessExtensions
{
public static class ProcessExtensions
{
#region Win32 Constants
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int CREATE_NO_WINDOW = 0x08000000;
private const int CREATE_NEW_CONSOLE = 0x00000010;
private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
#endregion
#region DllImports
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
private static extern bool DuplicateTokenEx(
IntPtr ExistingTokenHandle,
uint dwDesiredAccess,
IntPtr lpThreadAttributes,
int TokenType,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
private static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[DllImport("wtsapi32.dll", SetLastError = true)]
private static extern int WTSEnumerateSessions(
IntPtr hServer,
int Reserved,
int Version,
ref IntPtr ppSessionInfo,
ref int pCount);
#endregion
#region Win32 Structs
private enum SW
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10
}
private enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public readonly UInt32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public readonly String pWinStationName;
public readonly WTS_CONNECTSTATE_CLASS State;
}
#endregion
// Gets the user token from the currently active session
private static bool GetSessionUserToken(ref IntPtr phUserToken)
{
var bResult = false;
var hImpersonationToken = IntPtr.Zero;
var activeSessionId = INVALID_SESSION_ID;
var pSessionInfo = IntPtr.Zero;
var sessionCount = 0;
// Get a handle to the user access token for the current active session.
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
{
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
var current = pSessionInfo;
for (var i = 0; i < sessionCount; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
current += arrayElementSize;
if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
{
activeSessionId = si.SessionID;
}
}
}
// If enumerating did not work, fall back to the old method
if (activeSessionId == INVALID_SESSION_ID)
{
activeSessionId = WTSGetActiveConsoleSessionId();
}
if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
{
// Convert the impersonation token to a primary token
bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
ref phUserToken);
CloseHandle(hImpersonationToken);
}
return bResult;
}
public static bool StartProcessAsCurrentUser(string cmdLine, bool visible, string appPath = null, string workDir = null)
{
var hUserToken = IntPtr.Zero;
var startInfo = new STARTUPINFO();
var procInfo = new PROCESS_INFORMATION();
var pEnv = IntPtr.Zero;
int iResultOfCreateProcessAsUser;
startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
try
{
if (!GetSessionUserToken(ref hUserToken))
{
throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
}
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
startInfo.lpDesktop = "winsta0\\default";
if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
{
throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed.");
}
if (!CreateProcessAsUser(hUserToken,
appPath, // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags,
pEnv,
workDir, // Working directory
ref startInfo,
out procInfo))
{
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.\n");
}
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
}
finally
{
CloseHandle(hUserToken);
if (pEnv != IntPtr.Zero)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
return true;
}
}
}
"@
# Compiling the source code as csharp
$compilerParams = [System.CodeDom.Compiler.CompilerParameters]::new()
$compilerParams.ReferencedAssemblies.AddRange(('System.Runtime.InteropServices.dll', 'System.dll'))
$compilerParams.CompilerOptions = '/unsafe'
$compilerParams.GenerateInMemory = $True
Add-Type -TypeDefinition $csharpCode -Language CSharp -CompilerParameters $compilerParams
# Adding powershell executeable to the command
$Command = '{0}\System32\WindowsPowerShell\v1.0\powershell.exe -executionPolicy bypass {1}' -f $($env:windir),$Command
# Adding double slashes to the command paths, as this is required.
$Command = $Command.Replace("\","\\")
# Execute a process as the currently logged on user.
# Absolute paths required if running as SYSTEM!
if($Hidden) { #running the command hidden
$runCommand = [murrayju.ProcessExtensions.ProcessExtensions]::StartProcessAsCurrentUser($Command,$false)
}else{ #running the command visible
$runCommand = [murrayju.ProcessExtensions.ProcessExtensions]::StartProcessAsCurrentUser($Command,$true)
}
if ($runCommand) {
return "Executed `"$Command`" as loggedon user"
} else {
throw "Something went wrong when executing process as currently logged-on user"
}
}
function Uninstall-ZoomLocalMachine() {
Write-Verbose -Verbose -Message "Running Uninstall-ZoomLocalMachine function"
$registryPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
if (Test-Path -Path $registryPath) {
$installedZoomApps = Get-ChildItem -Path $registryPath -Recurse | Get-ItemProperty | Where-Object {$_.Publisher -like "Zoom*" } | Select-Object Displayname,UninstallString
if ($installedZoomApps) {
Write-Verbose -Verbose -Message "Installed Zoom applications found in HKLM"
foreach ($zoomApp in $installedZoomApps) {
if ($zoomApp.UninstallString) {
# Regular expression for format of MSI product code
$msiRegEx = "\w{8}-\w{4}-\w{4}-\w{4}-\w{12}"
# Formatting the productcode in a creative way.
# Needed this separately, as the uninstall string retrieved from registry sometimes wasn't formatted properly
$a = $zoomApp.Uninstallstring.Split("{")[1]
$b = $a.Split("}")[0]
# Only continuing if the uninstall string matches a regular MSI product code
if ($b -match $msiRegEx) {
$productCode = "{" + $b + "}"
if ($productCode) {
try {
Write-Verbose -Verbose -Message "Uninstalling application: $($zoomApp.DisplayName)"
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList ("/x" + $productCode + " /passive") -Wait
}
catch {
Write-Error -Message "Failed to uninstall application: $($zoomApp.DisplayName)"
}
}
}
}
}
}
else {
Write-Verbose -Verbose -Message "No Zoom applications found in HKLM"
}
}
else {
Write-Verbose -Verbose -Message "Registry path not found"
}
}
function Uninstall-ZoomCurrentUser() {
Write-Verbose -Verbose -Message "Running Uninstall-ZoomCurrentUser function"
# Getting all user profiles on the computer
$userProfiles = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" | Where-Object {$_.PSChildName -match "S-1-5-21-(\d+-?){4}$"} | Select-Object @{Name="SID"; Expression={$_.PSChildName}}, @{Name="UserHive";Expression={"$($_.ProfileImagePath)\NTuser.dat"}}
foreach ($userProfile in $userProfiles) {
# Formatting the username in a separate variable
$userName = $userProfile.UserHive.Split("\")[2]
$registryPath = "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\CurrentVersion\Uninstall"
if (Test-Path -Path $registryPath) {
$installedZoomApps = Get-ChildItem -Path $registryPath -Recurse | Get-ItemProperty | Where-Object {$_.Publisher -like "Zoom*" } | Select-Object Displayname,UninstallString
if ($installedZoomApps) {
Write-Verbose -Verbose -Message "Installed Zoom applications found in HKCU for user: $userName"
foreach ($zoomApp in $installedZoomApps) {
if ($zoomApp.UninstallString) {
$userCommand = '-command &{Start-Process "C:\Users\USERNAME\AppData\Roaming\Zoom\uninstall\Installer.exe" -ArgumentList "/uninstall" -Wait}'
# Replacing the placeholder: USERNAME with the actual username retrieved from the userprofile
# This can probably be done smarter, but I failed to find another method
$userCommand = $userCommand -replace "USERNAME",$userName
try {
Write-Verbose -Verbose -Message "Uninstalling application: $($zoomApp.DisplayName) as the logged on user: $userName"
Execute-AsLoggedOnUser -Command $userCommand
}
catch {
Write-Error -Message "Failed to uninstall application: $($zoomApp.DisplayName) for user: $userName"
}
}
}
}
else {
Write-Verbose -Verbose -Message "No Zoom applications found in HKCU for user: $userName"
}
}
else {
Write-Verbose -Verbose -Message "Registry path not found for user: $userName"
}
}
}
try {
Write-Verbose -Verbose -Message "Script is running"
Uninstall-ZoomLocalMachine
Uninstall-ZoomCurrentUser
}
catch {
Write-Verbose -Verbose -Message "Something went wrong during running of the script"
}
finally {
Write-Verbose -Verbose -Message "Script is done running"
}