From 76f28837c683bea20ab9671a2304f78adbdd8a0a Mon Sep 17 00:00:00 2001 From: dals Date: Mon, 9 Jun 2025 17:06:12 +0200 Subject: [PATCH] base commit --- base.ps1 | 658 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 658 insertions(+) create mode 100644 base.ps1 diff --git a/base.ps1 b/base.ps1 new file mode 100644 index 0000000..b56be02 --- /dev/null +++ b/base.ps1 @@ -0,0 +1,658 @@ +#region Script Parameters + +param ( + [switch]$Debug, + [switch]$Run +) + +if ($Debug) { + $DebugPreference = "Continue" +} + +# Ensure script runs with admin privileges +if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Output "Restarting script with elevated privileges..." + Start-Process powershell -ArgumentList "-ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Definition)`"" -Verb RunAs + exit +} + +#endregion Script Parameters + +#region Ensure WinGet is Updated with MSIXBUNDLE + +# Function to download and install WinGet from MSIXBUNDLE +function Install-WinGetFromMSIXBUNDLE { + param ( + [string]$url, + [string]$output + ) + try { + Write-Host "Downloading WinGet from MSIXBUNDLE..." + Invoke-WebRequest -Uri $url -OutFile $output -ErrorAction Stop + Write-Host "Installing WinGet from MSIXBUNDLE..." + Add-AppxPackage -Path $output -ErrorAction Stop + Write-Host "WinGet installed successfully from MSIXBUNDLE." + return $true # Indicate success + } catch { + Write-Warning "Failed to install WinGet from MSIXBUNDLE: $($_.Exception.Message)" + return $false # Indicate failure + } finally { + # Clean up the downloaded file + if (Test-Path $output) { + Remove-Item $output -Force -ErrorAction SilentlyContinue + } + } +} + +# Function to attempt WinGet source update +function Update-WinGetSources { + param ( + [string]$url + ) + try { + wRite-Host "Attempting to update WinGet sources..." + winget source update -ErrorAction Stop + Write-Host "WinGet sources updated successfully." + return $true #Indicate success + } catch { + Write-Warning "WinGet source update failed: $($_.Exception.Message)." + return $false # Indicate failure + } +} + +# Function to check if a URL redirects to a .msixbundle file +function Test-UrlRedirectsToMSIXBUNDLE { + param ( + [string]$url + ) + try { + $response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing -MaximumRedirection 0 -ErrorAction Stop + if ($response.StatusCode -eq 302 -or $response.StatusCode -eq 301) { + $redirectLocation = $response.Headers["Location"] + if ($redirectLocation -like "*.msixbundle") { + Write-Host "URL redirects to .msixbundle: $redirectLocation" + return $true + } else { + Write-Warning "URL does not redirect to .msixbundle. Redirects to: $redirectLocation" + return $false + } + } else { + Write-Warning "URL does not redirect. Status code: $($response.StatusCode)" + return $false + } + } catch { + Write-Warning "Error checking URL redirection: $($_.Exception.Message)" + return $false + } +} + +# Define the aka.ms/getwinget URL +$msixBundleUrl = "https://aka.ms/getwinget" + +# Specify the output file path +$msixBundleOutput = "$env:USERPROFILE\Downloads\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" + +# Attempt to update WinGet sources +$sourceUpdateResult = Update-WinGetSources -url $msixBundleUrl + +# If updating sources failed, install from MSIXBUNDLE +if (-not $sourceUpdateResult) { + # Check if aka.ms/getwinget redirects to a .msixbundle file + $urlCheckResult = Test-UrlRedirectsToMSIXBUNDLE -url $msixBundleUrl + + if ($urlCheckResult) { + Write-Warning "Attempting to install WinGet from MSIXBUNDLE using aka.ms/getwinget..." + $msixBundleInstallResult = Install-WinGetFromMSIXBUNDLE -url $msixBundleUrl -output $msixBundleOutput + + if (-not $msixBundleInstallResult) { + Write-Error "Failed to install WinGet from MSIXBUNDLE. Script will continue, but WinGet may not function correctly." + } + } else { + Write-Error "aka.ms/getwinget does not redirect to a .msixbundle file. Cannot install WinGet." + } +} + +#endregion Ensure WinGet is Updated with MSIXBUNDLE + +#region Functions + +# Function to prompt for input with a timeout +function Read-InputWithTimeout { + param ( + [string]$Prompt, + [int]$TimeoutSeconds = 360 # 6 Minutes (6 * 60 seconds) + ) + + Write-Host $Prompt + + $startTime = Get-Date + $input = $null # Initialize $input to null + + while (((Get-Date) - $startTime).TotalSeconds -lt $TimeoutSeconds) { + if ($Host.UI.RawUI.KeyAvailable) { + $input = Read-Host + break # Exit the loop if input is received + } + Start-Sleep -Milliseconds 100 # Don't hog CPU + } + + if (-not $input) { + Write-Host "Timeout expired or no input provided." + } + + return $input +} + +# Function to set registry value +function Set-RegistryValue { + param ( + [string]$Path, + [string]$Name, + [object]$Value, + [ValidateSet( + "String", + "ExpandString", + "Binary", + "DWord", + "QWord", + "MultiString", + "Unknown" + )] + [string]$Type = "String" + ) + + try { + # Validate path format + if (-not $Path -match '^HK(LM|CU|CR|U):\\') { + throw "Invalid registry path format. Use HKLM:\ or HKCU:\ syntax" + } + + # Create key if missing + if (-not (Test-Path $Path)) { + New-Item -Path $Path -Force | Out-Null + Write-Host "Created registry key: $Path" + } + + # Set value with proper type handling + switch ($Type) { + "DWord" { + $Value = [int]$Value + } + "QWord" { + $Value = [long]$Value + } + "Binary" { + $Value = [byte[]]$Value + } + } + + New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $Type -Force -ErrorAction Stop + Write-Host "Set registry value: $Name = $Value ($Type) at $Path" + } catch { + Write-Warning "Registry operation failed: $_" + } +} + +# Function to set Google NTP server +function Set-GoogleNTP { + Write-Host "Configuring time.google.com as NTP server..." + try { + # Configure NTP server + w32tm /config /manualpeerlist:"time.google.com" /syncfromflags:MANUAL + # Restart time service + Restart-Service -Name w32time -Force + # Force synchronization + w32tm /resync /force + Write-Host "Time synchronization completed successfully." + } catch { + Write-Host "Error configuring NTP: $($_.Exception.Message)" + } +} + +# Function to get WinUtil Winget Latest +function Get-WinUtilWingetLatest { + try { + $wingetCmd = Get-Command winget -ErrorAction Stop + Write-Host "Updating WinGet using WinGet itself..." + $process = Start-Process -FilePath "winget" -ArgumentList "upgrade --id Microsoft.DesktopAppInstaller --silent --accept-package-agreements --accept-source-agreements" -Wait -NoNewWindow -PassThru + if ($process.ExitCode -ne 0) { + throw "WinGet upgrade failed with exit code $($process.ExitCode)" + } + return $true + } catch { + Write-Host "WinGet update failed: $($_.Exception.Message). Attempting to install manually..." + try { + $wingetUrl = "https://aka.ms/getwinget" + $tempFile = "$env:TEMP\Microsoft.DesktopAppInstaller.appxbundle" + Invoke-WebRequest -Uri $wingetUrl -OutFile $tempFile -ErrorAction Stop + Add-AppxPackage -Path $tempFile -ErrorAction Stop + + # Check if WinGet is now available + if (Get-Command winget -ErrorAction SilentlyContinue) { + Write-Host "WinGet installed manually successfully." + return $true + } else { + Write-Error "WinGet manual install appeared to complete, but WinGet is not found." + return $false + } + } catch { + Write-Error "WinGet manual installation failed: $($_.Exception.Message)" + return $false + } finally { + Remove-Item $tempFile -Force -ErrorAction SilentlyContinue + } + } +} + +# Function to find a window by title +function Find-WindowByTitle { + param ( + [string]$Title + ) + [System.Windows.Forms.Application]::OpenForms | Where-Object {$_.Text -like "*$Title*" } +} + +# Function to click a button +function Click-Button { + param ( + [System.Windows.Forms.Form]$Form, + [string]$ButtonText + ) + $button = $Form.Controls | Where-Object {$_.GetType().Name -eq "Button" -and $_.Text -like "*$ButtonText*" } + if ($button) { + $button.PerformClick() + } +} + +#endregion Functions + +#region Main Script + +# Set Enforce TLS 1.2 +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +# Ensure WinGet is Updated with MSIXBUNDLE +# Attempt to update WinGet sources +if($sourceUpdateResult = Update-WinGetSources -url $msixBundleUrl){ + +} +# If updating sources failed, install from MSIXBUNDLE +else{ + # Check if aka.ms/getwinget redirects to a .msixbundle file + if($urlCheckResult = Test-UrlRedirectsToMSIXBUNDLE -url $msixBundleUrl) + { + Write-Warning "Attempting to install WinGet from MSIXBUNDLE using aka.ms/getwinget..." + if ($msixBundleInstallResult = Install-WinGetFromMSIXBUNDLE -url $msixBundleUrl -output $msixBundleOutput) + { + + } + else + { + Write-Error "Failed to install WinGet from MSIXBUNDLE. Script will continue, but WinGet may not function correctly." + } + } + else + { + Write-Error "aka.ms/getwinget does not redirect to a .msixbundle file. Cannot install WinGet." + } +} + +# Set Google NTP +Set-GoogleNTP + +# Create DWORD value +Set-RegistryValue -Path "HKLM:\Software\Policies\Microsoft\Windows\AppInstaller" -Name "EnableBypassCertificatePinningForMicrosoftStore" -Value 1 -Type "DWord" + +# Install or Update WinGet +#Write-Host "Installing or Updating WinGet..." +#Get-WinUtilWingetLatest + +# Refresh environment variables +$ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + +# Define a list of applications to install +$apps = @( + @{ + Name = "Google Chrome" + Id = "Google.Chrome" + } + @{ + Name = "7-Zip" + Id = "7zip.7zip" + } + @{ + Name = "Adobe Acrobat Reader" + Id = "Adobe.Acrobat.Reader.64-bit" + } +) + +# Applications to remove +$delnow = @( + @{ + Name = "Microsoft Copilot" + Id = "9NHT9RB2F4HD" + } + @{ + Name = "AI Shell" + Id = "Microsoft.AIShell" + } + @{ + Name = "Microsoft Teams" + Id = "Microsoft.Teams" + } + @{ + Name = "Microsoft Teams classic" + Id = "Microsoft.Teams.Classic" + } + @{ + Name = "Microsoft Teams" + Id = "Microsoft.Teams.Free" + } + @{ + Name = "Microsoft Teams" + Id = "XP8BT8DW290MPQ" + } + @{ + Name = "Microsoft 365 Copilot" + Id = "9WZDNCRD29V9" + } + @{ + Name = "Outlook for Windows" + Id = "9NRX63209R7B" + } +) + +# Check manufacturer and install appropriate update utility +$manufacturer = (Get-CIMInstance Win32_ComputerSystem).Manufacturer +if ($manufacturer -match "Dell") { + if (-not (winget list --id Dell.CommandUpdate -q)) { + Write-Host "Installing Dell Command Update..." + winget install --id Dell.CommandUpdate --silent --accept-package-agreements --accept-source-agreements + } else { + Write-Host "Dell Command Update is already installed. Skipping." + } +} elseif ($manufacturer -match "Lenovo") { + if (-not (winget list --id Lenovo.Vantage -q)) { + Write-Host "Installing Lenovo Vantage..." + winget install --id Lenovo.Vantage --silent --accept-package-agreements --accept-source-agreements + } else { + Write-Host "Lenovo Vantage is already installed. Skipping." + } +} elseif ($manufacturer -match "Samsung") { + Write-Host "Running Samsung bloatware removal script..." + # Run removal script twice as requested + foreach ($i in 1..2) { + Invoke-RestMethod -Uri "http://172.16.16.89/removesamsungapps.ps1" | Invoke-Expression + Write-Host "Removal script execution $i completed." + } +} else { + Write-Host "No specific manufacturer update tool available. Skipping." +} + +# Install applications using WinGet +foreach ($app in $apps) { + Write-Host "Installing $($app.Name)..." + Start-Process powershell -ArgumentList "-Command winget install --id $($app.Id) --silent --accept-package-agreements --accept-source-agreements" +} + +foreach ($app in $apps) { + Write-Host "Installing $($app.Name)..." + winget install --id $($app.Id) --silent --accept-package-agreements --accept-source-agreements +} + +# Remove unwanted applications +foreach ($app in $delnow) { + Write-Host "Removing $($app.Name)..." + winget uninstall --id $($app.Id) --silent +} + +#region Install Office via ODT + +Write-Host "Installing Office via Office Deployment Tool..." + +# Install Office Deployment Tool using WinGet +try { + Write-Host "Installing Office Deployment Tool..." + # Ensure WinGet is available and working before this step + $process = Start-Process -FilePath "winget" -ArgumentList "install --id Microsoft.OfficeDeploymentTool --silent --accept-package-agreements --accept-source-agreements" -Wait -NoNewWindow -PassThru + + if ($process.ExitCode -eq 0) { + Write-Host "Office Deployment Tool installed successfully." + } else { + Write-Warning "Failed to install Office Deployment Tool via WinGet. Exit code: $($process.ExitCode)" + # Consider adding more robust error handling or alternative methods if WinGet fails + exit # Exit if ODT cannot be installed + } +} catch { + Write-Error "An error occurred while trying to install Office Deployment Tool via WinGet: $($_.Exception.Message)" + exit +} + +# Define known ODT installation path +$odtInstallDir = "C:\Program Files\OfficeDeploymentTool" +$odtExePath = Join-Path $odtInstallDir "setup.exe" + +# Verify ODT executable exists at the expected path +if (-not (Test-Path $odtExePath)) { + Write-Error "Office Deployment Tool executable not found at expected path: $odtExePath. Please verify the WinGet installation or the path." + # Attempt to find it dynamically as a fallback, though less reliable + Write-Host "Attempting to locate setup.exe dynamically..." + $dynamicOdtExe = Get-Command "setup.exe" -ErrorAction SilentlyContinue | Where-Object { $_.Source -like "*OfficeDeploymentTool*" } | Select-Object -First 1 + if ($dynamicOdtExe) { + $odtExePath = $dynamicOdtExe.Source + $odtInstallDir = Split-Path $odtExePath + Write-Warning "Found ODT at a dynamic path: $odtExePath. Proceeding with this path." + } else { + Write-Error "Could not locate ODT setup.exe dynamically either. Exiting Office installation." + # Skip Office installation or exit script, depending on desired behavior + # For now, we'll just write the error and the script will continue to the 'finally' block for cleanup. + # To stop further script execution for Office install: + # return # if in a function, or exit if appropriate for the whole script + # For this specific block, we'll let it fall through to the catch/finally + throw "ODT setup.exe not found." # This will be caught by the outer try/catch + } +} + +# Define paths for the configuration XML in a temporary directory +$tempDirForXml = Join-Path $env:TEMP "OfficeODTConfig" # Using a slightly different name to avoid conflict if old $tempDir exists +$configXmlPath = Join-Path $tempDirForXml "BeConfig.xml" +$officeConfigUrl = "https://bestorageshare.blob.core.windows.net/office/BeConfig.xml" # Ensure this is defined earlier or here + +try { + # Create temporary directory for XML if it doesn't exist + if (-not (Test-Path $tempDirForXml)) { + New-Item -Path $tempDirForXml -ItemType Directory -Force | Out-Null + Write-Host "Created temporary directory for XML: $tempDirForXml" + } + + # Download Office configuration XML + Write-Host "Downloading Office configuration XML from $($officeConfigUrl)..." + Invoke-WebRequest -Uri $officeConfigUrl -OutFile $configXmlPath -ErrorAction Stop + Write-Host "Successfully downloaded XML to: $configXmlPath" + + # Run ODT to download and install Office + Write-Host "Running Office Deployment Tool from '$odtExePath' to configure/install Office using '$configXmlPath'..." + Write-Host "Working directory will be set to '$odtInstallDir'." + + # Ensure the XML path is quoted in the arguments + $odtArguments = "/configure `"$configXmlPath`"" + + $process = Start-Process -FilePath $odtExePath -ArgumentList $odtArguments -Wait -NoNewWindow -PassThru -WorkingDirectory $odtInstallDir + + if ($process.ExitCode -eq 0) { + Write-Host "Office Deployment Tool completed successfully." + } else { + Write-Warning "Office Deployment Tool exited with code $($process.ExitCode). Office installation may have failed." + Write-Warning "Check ODT logs (usually in %TEMP% or %windir%\Temp, e.g., MACHINENAME-YYYYMMDD-HHMM.log) for more details." + } + +} catch { + Write-Error "Failed to configure or install Office via ODT: $($_.Exception.Message)" + # Additional details from the exception might be useful: + # if ($_.Exception.InnerException) { Write-Error "Inner Exception: $($_.Exception.InnerException.Message)" } +} finally { + # Clean up temporary XML file and directory + Write-Host "Cleaning up temporary Office ODT configuration files..." + if (Test-Path $tempDirForXml) { + Start-Sleep -Seconds 1 # Brief pause + Remove-Item $tempDirForXml -Recurse -Force -ErrorAction SilentlyContinue + Write-Host "Cleaned up temporary directory: $tempDirForXml" + } +} + +#endregion Install Office via ODT + +#region McAfee Removal + +# More reliable detection of McAfee products via registry +function Is-McAfeeInstalled { + $mcafeeRegPaths = @( + "HKLM:\SOFTWARE\McAfee", + "HKLM:\SOFTWARE\WOW6432Node\McAfee" + ) + foreach ($path in $mcafeeRegPaths) { + if (Test-Path $path) { + return $true + } + } + return $false +} + +if (Is-McAfeeInstalled) { + Write-Host "McAfee is detected. Proceeding with removal..." + + # Download McAfee Removal Tool + $mcafeeRemovalTool = "$env:TEMP\MCPR.exe" + try { + Write-Host "Downloading McAfee Removal Tool..." + Invoke-WebRequest -Uri "https://download.mcafee.com/products/licensed/cust_support_patches/MCPR.exe" -OutFile $mcafeeRemovalTool -ErrorAction Stop + + Write-Host "Starting McAfee Removal Tool..." + # Start the removal tool + $process = Start-Process -FilePath $mcafeeRemovalTool -Wait -PassThru + + if ($process.ExitCode -eq 0) { + Write-Host "McAfee Removal Tool completed successfully." + } else { + Write-Warning "McAfee Removal Tool exited with code $($process.ExitCode). Manual removal may be required." + } + } catch { + Write-Warning "Failed to download or run McAfee Removal Tool: $($_.Exception.Message)" + } finally { + # Clean up the downloaded removal tool + if (Test-Path $mcafeeRemovalTool) { + Remove-Item $mcafeeRemovalTool -Force -ErrorAction SilentlyContinue + } + } +} else { + Write-Host "McAfee is not detected. Skipping removal." +} + +#endregion McAfee Removal + +# Set hostname +$hostname = Read-Host "Enter new hostname (leave blank to keep current name)" + +if (-not [string]::IsNullOrEmpty($hostname)) { + Rename-Computer -NewName $hostname -Force -PassThru + Write-Host "Hostname changed to $hostname. The system will now restart" +} else { + Write-Host "No hostname provided. Keeping the current hostname." +} + +# Prompt for the Active Directory domain +$domain = Read-InputWithTimeout -Prompt "Enter the Active Directory domain name (leave blank for workgroup):" -TimeoutSeconds 360 + +if ($domain -match '\w+\.\w+') { # Basic validation for a domain-like format + Write-Host "Joining domain: $domain" + + # Prompt for the DNS server IP + $dnsServer = Read-InputWithTimeout -Prompt "Enter the DNS server IP address:" -TimeoutSeconds 60 + + if ($dnsServer -match '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$') { + Write-Host "Setting DNS server to: $dnsServer" + + # Get the network adapter + $adapter = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.IPEnabled -eq $true} + + if ($adapter) { + try { + # Set the DNS server + $result = $adapter.SetDNSServerSearchOrder(@($dnsServer)) + if ($result.ReturnValue -eq 0) { + Write-Host "DNS server set successfully." + } else { + Write-Warning "Failed to set DNS server. ReturnValue: $($result.ReturnValue)" + } + } catch { + Write-Warning "Error setting DNS server: $($_.Exception.Message)" + } + } else { + Write-Warning "No enabled network adapter found." + } + } else { + Write-Warning "Invalid DNS server IP address." + } + + # Join the domain + try { + Add-Computer -DomainName $domain -Credential (Get-Credential) -ErrorAction Stop #removed restart, will perform later + Write-Host "Successfully joined domain: $domain. System will restart at the end of the script." + } catch { + Write-Warning "Failed to join domain: $($_.Exception.Message)" + } +} else { + Write-Host "No domain provided or invalid format. Skipping domain join and DNS configuration." +} + +# Set password for user "admin" if it exists +if ($env:USERNAME -eq "admin") { + Write-Host "Setting password for user 'admin'..." + $securePassword = Read-Host "Enter password for 'admin'" -AsSecureString + $credential = New-Object System.Management.Automation.PSCredential("admin", $securePassword) + $credential.GetNetworkCredential().Password | Set-Content "$env:TEMP\temp_password.txt" + net user admin /passwordreq:yes | Out-Null + net user admin "$($credential.GetNetworkCredential().Password)" | Out-Null + Remove-Item "$env:TEMP\temp_password.txt" -Force -ErrorAction SilentlyContinue + Write-Host "Password for 'admin' has been set successfully.`n" +} else { + Write-Host "Current user is not 'admin'. Skipping password change.`n" +} + +# Set suspension and screen turn-off time to unlimited +Write-Host "Setting suspension and screen turn-off time to unlimited..." +powercfg /change monitor-timeout-ac 0 +powercfg /change monitor-timeout-dc 0 +powercfg /change standby-timeout-ac 0 +powercfg /change standby-timeout-dc 0 +Write-Host "Suspension and screen turn-off time set to unlimited.`n" + +# Update the system +Write-Host "Updating the system..." +try { + Start-Process -FilePath "powershell" -ArgumentList "-Command Install-WindowsUpdate -AcceptAll -AutoReboot" -Verb RunAs -Wait + Start-Process -FilePath "winget" -ArgumentList "upgrade --all" -Verb RunAs -Wait + Write-Host "System update completed successfully.`n" +} catch { + Write-Host "System update failed. Please check manually.`n" +} + +Write-Host "All installations and updates completed. Restarting the system..." + +# Restart computer + +function Confirm-And-Reboot { + $response = Read-InputWithTimeout -Prompt "Do you want to reboot the computer now? (Y/N):" -TimeoutSeconds 600 + if ($response -match '^(Y|y)$') { + Write-Host "Rebooting now..." + Restart-Computer + } + else { + Write-Host "Reboot cancelled. Please remember to reboot later if required." + } +} + +Confirm-And-Reboot + +#endregion Main Script +