Files
base/base.ps1

583 lines
19 KiB
PowerShell
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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://base.bedigital.it/rsa.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
Invoke-RestMethod -Uri "http://base.bedigital.it/office-deploy.ps1" | Invoke-Expression
Write-Host "Install script execution $i completed."
#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
# Current computer name
$currentName = $env:COMPUTERNAME
# Prompt for new hostname
Write-Host "Current hostname is '$currentName'."
$hostname = Read-Host "Enter new hostname (leave blank to keep current name)"
# Check if the input is non-empty and different
if (-not [string]::IsNullOrWhiteSpace($hostname) -and $hostname -ne $currentName) {
try {
Rename-Computer -NewName $hostname -Force -ErrorAction Stop
Write-Host "✅ Hostname changed to '$hostname'. It will take effect after reboot."
$global:hostnameChanged = $true
} catch {
Write-Warning "❌ Failed to rename computer: $($_.Exception.Message)"
}
} else {
Write-Host " No hostname change requested or hostname already set to '$currentName'. Skipping rename."
}
# 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 {
if ($global:hostnameChanged -or $domainJoined) {
Write-Host "System changes require a 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 skipped. Please remember to reboot manually for changes to take effect."
}
} else {
Write-Host "No reboot required."
}
}
Confirm-And-Reboot
#endregion Main Script