#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