You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wintriallab/scripts/wintriallab-postinstall.psm1

1162 lines
50 KiB

param(
[String] $ScriptProductName = "PostInstall-Marionettist",
[String] $ScriptPath = $MyInvocation.MyCommand.Path,
[String] $ScriptName = $MyInvocation.MyCommand.Name
)
### Global Constants that I use elsewhere
$ArchitectureId = @{
amd64 = "amd64"
i386 = "i386"
}
$WindowsVersionId = @{
w81 = "w81"
w10 = "w10"
w10ltsb = "w10ltsb"
server2012r2 = "server2012r2"
}
$URLs = @{
SevenZipDownload = @{
$ArchitectureId.i386 = "http://7-zip.org/a/7z920.msi"
$ArchitectureId.amd64 = "http://7-zip.org/a/7z920-x64.msi"
}
UltraDefragDownload = @{
$ArchitectureId.i386 = "http://downloads.sourceforge.net/project/ultradefrag/stable-release/6.1.0/ultradefrag-portable-6.1.0.bin.i386.zip"
$ArchitectureId.amd64 = "http://downloads.sourceforge.net/project/ultradefrag/stable-release/6.1.0/ultradefrag-portable-6.1.0.bin.amd64.zip"
}
SdeleteDownload = "http://download.sysinternals.com/files/SDelete.zip"
WindowsIsoDownload = @{
$WindowsVersionId.w81 = @{
$ArchitectureId.i386 = @{
URL = "http://care.dlservice.microsoft.com/dl/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X86FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X86FREE_EN-US_DV9.ISO"
SHA1 = "4ddd0881779e89d197cb12c684adf47fd5d9e540"
}
$ArchitectureId.amd64 = @{
URL = "http://download.microsoft.com/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.16384.WINBLUE_RTM.130821-1623_X64FRE_ENTERPRISE_EVAL_EN-US-IRM_CENA_X64FREE_EN-US_DV5.ISO"
SHA1 = "5e4ecb86fd8619641f1d58f96e8561ec"
}
}
$WindowsVersionId.w10 = @{
$ArchitectureId.i386 = @{
URL = "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X86FRE_EN-US.ISO"
SHA1 = "875b450d67e7176b8b3c72a80c60a0628bf1afac"
}
$ArchitectureId.amd64 = @{
URL = "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO"
SHA1 = "56ab095075be28a90bc0b510835280975c6bb2ce"
}
}
}
}
$script:ScriptPath = $MyInvocation.MyCommand.Path
### Private support functions I use behind the scenes
<#
.description
Add a line to a file idempotently; that is, if the line is not already present in the file, add it, but if it is already present, then do nothing
#>
function Add-FileLineIdempotently {
[CmdletBinding()] param(
[Parameter(Mandatory=$true)] [String] $file,
[Parameter(Mandatory=$true)] [String[]] $newLine,
[Parameter(Mandatory=$true)] [String] $encoding = "UTF8"
)
if (-not (Test-Path $file)) { New-Item -ItemType File -Path $file | Out-Null }
$origContents = Get-Content $file
$newLine |% {
if ($origContents -notcontains $_) {
Out-File -FilePath $file -InputObject $_ -Encoding $encoding -Append
}
}
}
<#
.description
Do some very basic filename sanitization
#>
function Get-SanitizedFilename {
[cmdletbinding()] param(
[Parameter(Mandatory=$true)] [String] $fileName
)
$invalidChars = [System.IO.Path]::GetInvalidFileNameChars()
$replacementCharacter = "_"
$newName = [System.String]::Copy($fileName)
foreach ($invChar in $invalidChars) {
$newName = $newName.Replace($invChar, $replacementCharacter)
}
return $newName
}
<#
.synopsis
Get a rooted path
.notes
Especially useful for .NET functions, which don't understand Powershell's $pwd, and instead have their own concept of the working directory, which is (in any normal case) always %USERPROFILE%. This means that if you do this:
cd C:\Windows
(New-Object System.Net.WebClient).DownloadFile("http://example.com/file.txt", "./file.txt")
... the file will be downloaded to %USERPROFILE%\file.txt, not C:\Windows\file.txt
#>
function Get-RootedPath {
[cmdletbinding()] param(
[Parameter(Mandatory=$true)] [String] $path
)
if (-not [System.IO.Path]::IsPathRooted($path)) {
$path = Join-Path -Path $pwd -ChildPath $path
}
try {
$rootedPath = [System.IO.Path]::GetFullPath($path)
}
catch {
Write-Error "Failed to validate path '$path'"
throw $_
}
return $rootedPath
}
<#
.synopsis
Download a URL from the web
.parameter url
The URL to download
.parameter outDir
Save the file to this directory. The filename will be the last part of the URL. This will make sense for a basic case like http://example.com/file.txt, but might be a little ugly for URLs like http://example.com/?product=exampleProduct&version=exampleVersion
.parameter outFile
Save the file to this exact filename.
.notes
Why not use Invoke-WebRequest or Invoke-RestMethod? Because those are not available before Powershell 3.0, and I still want to be able to use this function on vanilla Windows 7 (hopefully just before applying all updates and getting a more recent Powershell, but still.)
#>
function Get-WebUrl {
[cmdletbinding(DefaultParameterSetName="outDir")] param(
[parameter(mandatory=$true)] [string] $url,
[parameter(mandatory=$true,ParameterSetName="outDir")] [string] $outDir,
[parameter(mandatory=$true,ParameterSetName="outFile")] [string] $outFile
)
if ($PScmdlet.ParameterSetName -match "outDir") {
# If the URL is http://example.com/whatever/somefile.txt, the last URL component is somefile.txt
$lastUrlComponent = [System.IO.Path]::GetFileName($url)
$filename = Get-SanitizedFilename -fileName $lastUrlComponent
$outFile = Join-Path -Path $outDir -ChildPath $fileName
}
$outFile = Get-RootedPath $outFile
Write-EventLogWrapper "Downloading '$url' to '$outFile'..."
(New-Object System.Net.WebClient).DownloadFile($url, $outFile)
return (Get-Item $outFile)
}
<#
.synopsis
Invoke an expression; log the expression, optionally with any output, and the last exit code if appropriate
#>
function Invoke-ExpressionEx {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $command,
[switch] $invokeWithCmdExe,
[switch] $checkExitCode,
[switch] $logToStdout,
[int] $sleepSeconds
)
$global:LASTEXITCODE = 0
if ($invokeWithCmdExe) {
$commandSb = {cmd /c "$command"}.GetNewClosure()
}
else {
$commandSb = {invoke-expression -command $command}.GetNewClosure()
}
Write-EventLogWrapper "Invoke-ExpressionEx called to run command '$command'`r`n`r`nUsing scriptblock: $($commandSb.ToString())"
$output = $null
try {
if ($logToStdout) {
$commandSb.invoke()
$message = "Expression '$command' exited with code '$LASTEXITCODE'"
}
else {
$output = $commandSb.invoke()
$message = "Expression '$command' exited with code '$LASTEXITCODE' and output the following to the console:`r`n`r`n$output"
}
Write-EventLogWrapper -message $message
}
catch {
Write-EventLogWrapper -message "Invoke-ExpressionEx failed to run command '$command'"
Write-ErrorStackToEventLog -errorStack $_
throw $_
}
if ($checkExitCode -and $global:LASTEXITCODE -ne 0) {
throw "LASTEXITCODE: ${global:LASTEXITCODE} for command: '${command}'"
}
if ($sleepSeconds) { start-sleep $sleepSeconds }
}
### Publicly exported functions called directly from slipstreaming scripts
<#
.synopsis
Create a temporary directory
#>
function New-TemporaryDirectory {
$dirPath = [System.IO.Path]::GetTempFileName() # creates a file automatically
rm $dirPath
mkdir $dirPath # mkdir returns a DirectoryInfo object; not capturing it here returns it to the caller
}
<#
.synopsis
Return an object containing metadata for the trial ISO for a particular version of Windows
.notes
TODO: this sucks but I can't think of anything better to do
#>
function Get-WindowsTrialISO {
[cmdletbinding()] param(
$WindowsVersion = ([Environment]::OSVersion.Version),
$WindowsArchitecture = (Get-OSArchitecture)
)
if ($WindowsVersion.Major -eq 6 -and $WindowsVersion.Minor -eq 3) {
return $URLs.WindowsIsoDownload.w81.$WindowsArchitecture
}
elseif ($WindowsVersion.Major -eq 10 -and $WindowsVersion.Minor -eq 0) {
return $URLs.WindowsIsoDownload.w10.$WindowsArchitecture
}
else {
throw "No URL known for Windows version '$WindowsVersion' and architecture '$WindowsArchitecture'"
}
}
<#
.synopsis
Wrapper that writes to the event log but also to the screen
#>
function Write-EventLogWrapper {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [String] $message,
[int] $eventId = 0,
[ValidateSet("Error",'Warning','Information','SuccessAudit','FailureAudit')] $entryType = "Information",
[String] $EventLogName = $ScriptProductName,
[String] $EventLogSource = $ScriptName
)
if (-not (get-eventlog -logname * |? { $_.Log -eq $eventLogName })) {
New-EventLog -Source $EventLogSource -LogName $eventLogName
}
$messagePlus = "$message`r`n`r`nScript: $($script:ScriptPath)`r`nUser: ${env:USERDOMAIN}\${env:USERNAME}"
if ($messagePlus.length -gt 32766) { $messagePlus = $messagePlus.SubString(0,32766) } # Because Write-EventLog will die otherwise
Write-Host -foreground magenta "====Writing to $EvengLogName event log===="
Write-Host -foreground darkgray (get-date -Format "yyyy-MM-dd HH:mm:ss") # The event log tracks the date, but writing to host never shows it
write-host -foreground darkgray "$messagePlus`r`n"
Write-EventLog -LogName $eventLogName -Source $EventLogSource -EventID $eventId -EntryType $entryType -Message $MessagePlus
}
<#
.synopsis
Invoke a scriptblock. If it throws, write the errors out to the event log and exist with an error code
.notes
This is intended to be a handy wrapper for calling functions in this module that takes care of logging an exception for you.
See the autounattend-postinstall.ps1 and provisioner-postinstall.ps1 scripts for examples.
#>
function Invoke-ScriptblockAndCatch {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [ScriptBlock] $scriptBlock,
[int] $failureExitCode = 666
)
try {
Invoke-Command $scriptBlock
}
catch {
Write-ErrorStackToEventLog -errorStack $error
exit $failureExitCode
}
}
function Write-ErrorStackToEventLog {
[cmdletbinding()] param(
[parameter(mandatory=$true)] $errorStack
)
$message = "======== CAUGHT EXCEPTION ========`r`n$errorStack`r`n"
$message += "======== ERROR STACK ========`r`n"
$errorStack |% { $message += "$_`r`n----`r`n" }
$message += "======== ========"
Write-EventLogWrapper $message
}
function Test-PowershellSyntax {
[cmdletbinding(DefaultParameterSetName='FromText')]
param(
[parameter(mandatory=$true,ParameterSetName='FromText')] [string] $text,
[parameter(mandatory=$true,ParameterSetName='FromFile')] [string] $fileName,
[switch] $ThrowOnFailure
)
$tokens = @()
$parseErrors = @()
$parser = [System.Management.Automation.Language.Parser]
if ($pscmdlet.ParameterSetName -eq 'FromText') {
$parsed = $parser::ParseInput($text, [ref]$tokens, [ref]$parseErrors)
}
elseif ($pscmdlet.ParameterSetName -eq 'FromFile') {
$fileName = resolve-path $fileName
$parsed = $parser::ParseFile($fileName, [ref]$tokens, [ref]$parseErrors)
}
write-verbose "$($tokens.count) tokens found."
if ($parseErrors.count -gt 0) {
$message = "$($parseErrors.count) parse errors found in file '$fileName':`r`n"
$parseErrors |% { $message += "`r`n $_" }
if ($ThrowOnFailure) { throw $message } else { write-verbose $message }
return $false
}
return $true
}
<#
.description
Set a scheduled task to run on next logon of the calling user. Intended for tasks that need to reboot and then be restarted such as applying Windows Updates
.notes
The Powershell New-ScheduledTask cmdlet is broken for me on Win81, but SchTasks.exe doesn't support actions with long arguments (requires a command line of < 200something characters). lmfao.
My workaround is to take a scriptblock, and then just save it to a file and call the file from Powershell.
I create the scheduled task with SchTasks.exe, then modify it with Powershell cmdlets that can handle long arguments just fine
#>
function Set-RestartScheduledTask {
[cmdletbinding()] param(
[Parameter(Mandatory=$true)] [Scriptblock] $restartCommand,
[string] $tempRestartScriptPath = "${env:temp}\$ScriptProductName-TempRestartScript.ps1",
[string] $taskName = "$ScriptProductName-RestartTask"
)
Remove-RestartScheduledTask -taskName $taskName
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent().Name
$restartCommand.ToString() | Out-File -FilePath $tempRestartScriptPath
"Unregister-ScheduledTask -taskName '$taskName' -Confirm:`$false" | Out-File -Append -FilePath $tempRestartScriptPath
Test-PowershellSyntax -ThrowOnFailure -FileName $tempRestartScriptPath
$schTasksCmd = 'SchTasks.exe /create /sc ONLOGON /tn "{0}" /tr "cmd.exe /c echo TemporparyPlaceholderCommand" /ru "{1}" /it /rl HIGHEST /f' -f $taskName,$currentUser
Invoke-ExpressionEx -command $schTasksCmd -invokeWithCmdExe -checkExitCode
# schtasks.exe cannot modify specific battery arguments without importing XML (not gonna do.dat). Modify it here:
$settings = New-ScheduledTaskSettingsSet -allowStartIfonBatteries -dontStopIfGoingOnBatteries
# SchTasks.exe cannot specify a user for the LOGON schedule - it applies to all users. Modify it here:
$trigger = New-ScheduledTaskTrigger -AtLogon -User $currentUser
# SchTasks.exe cannot specify an action with long arguments (maxes out at like 200something chars). Modify it here:
$action = New-ScheduledTaskAction -Execute "$PSHome\Powershell.exe" -Argument "-File `"$tempRestartScriptPath`""
Set-ScheduledTask -taskname $taskName -settings $settings -action $action -trigger $trigger
$message = "Created scheduled task called '$taskName', which will run a temp file at '$tempRestartScriptPath', containing:`r`n`r`n"
$message += (Get-Content $tempRestartScriptPath) -join "`r`n"
Write-EventLogWrapper -message $message
}
function Get-RestartScheduledTask {
[cmdletbinding()] param(
[string] $taskName = $ScriptProductName
)
Get-ScheduledTask |? -Property TaskName -match $taskName
}
function Remove-RestartScheduledTask {
[cmdletbinding()] param(
[string] $taskName = $ScriptProductName
)
$existingTask = Get-RestartScheduledTask -taskName $taskName
if ($existingTask) {
Write-EventLogWrapper -message "Found existing task named '$taskName'; deleting..."
Unregister-ScheduledTask -InputObject $existingTask -Confirm:$false | out-null
}
else {
Write-EventLogWrapper -message "Did not find any existing task named '$taskName'"
}
}
<#
.description
Return the OS Architecture of the current system, as determined by WMI
Will return either "i386" or "amd64"
TODO: this isn't a great method but I'm tired of trying to find the totally correct one. This one isn't ideal because OSArchitecture can be localized.
I've seen some advice that you should call into the registry
- reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set OSARCHITECTURE=32BIT || set OSARCHITECTURE=64BIT
- http://stackoverflow.com/a/24590583/868206
- https://support.microsoft.com/en-us/kb/556009
... however, this lets you know about the HARDWARE, not the OPERATING SYSTEM - we care about the latter
#>
function Get-OSArchitecture {
$OSArch = Get-WmiObject -class win32_operatingsystem -property osarchitecture | select -expand OSArchitecture
if ($OSArch -match "64") { return $ArchitectureId.amd64 }
elseif ($OSArch -match "32") { return $ArchitectureId.i386 }
else { throw "Could not determine OS Architecture from string '$OSArch'" }
}
function Test-AdminPrivileges {
[cmdletbinding()] param(
[switch] $ThrowIfNotElevated
)
$me = [Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
$elevated = $me.IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if ($ThrowIfNotElevated -and (! $elevated)) { throw "Administrative privileges are required" }
return $elevated
}
function Install-SevenZip {
$OSArch = Get-OSArchitecture
$szDlPath = Get-WebUrl -url $URLs.SevenZipDownload.$OSArch -outDir $env:temp
try {
Write-EventLogWrapper "Downloaded '$($URLs.SevenZipDownload.$OSArch)' to '$szDlPath', now running msiexec..."
$msiCall = '& msiexec /qn /i "{0}"' -f $szDlPath
# Windows suxxx so msiexec sometimes returns right away? or something idk. fuck
Invoke-ExpressionEx -checkExitCode -command $msiCall -sleepSeconds 30
}
finally {
rm -force $szDlPath
}
}
set-alias sevenzip "${env:ProgramFiles}\7-Zip\7z.exe"
function Install-VBoxAdditions {
[cmdletbinding(DefaultParameterSetName="InstallFromDisc")] param(
[parameter(ParameterSetName="InstallFromIsoPath",mandatory=$true)] [string] $isoPath,
[parameter(ParameterSetName="InstallFromDisc",mandatory=$true)] [switch] $fromDisc
)
function InstallVBoxAdditionsFromDir {
param([Parameter(Mandatory=$true)][String]$baseDir)
$baseDir = resolve-path $baseDir | select -expand Path
Write-EventLogWrapper "Installing VBox Additions from '$baseDir'"
Write-EventLogWrapper "Installing the Oracle certificate..."
$oracleCert = resolve-path "$baseDir\cert\*sha*" | select -expand path
foreach($cert in $oracleCert) {
Invoke-ExpressionEx -checkExitCode -command ('& "{0}" add-trusted-publisher "{1}" --root "{1}"' -f "$baseDir\cert\VBoxCertUtil.exe",$cert)
}
# NOTE: Checking for exit code, but this command will fail with an error if the cert is already installed
Write-EventLogWrapper "Installing the virtualbox additions"
Invoke-ExpressionEx -checkExitCode -command ('& "{0}" /with_wddm /S' -f "$baseDir\VBoxWindowsAdditions.exe") # returns IMMEDIATELY, goddamn fuckers
while (get-process -Name VBoxWindowsAdditions*) { write-host 'Waiting for VBox install to finish...'; sleep 1; }
Write-EventLogWrapper "virtualbox additions have now been installed"
}
switch ($PSCmdlet.ParameterSetName) {
"InstallFromIsoPath" {
$isoPath = resolve-path $isoPath | select -expand Path
$vbgaPath = mkdir -force "${env:Temp}\InstallVbox" | select -expand fullname
try {
Write-EventLogWrapper "Extracting iso at '$isoPath' to directory at '$vbgaPath'..."
Invoke-ExpressionEx -checkExitCode -command ('sevenzip x "{0}" -o"{1}"' -f $isoPath, $vbgaPath)
InstallVBoxAdditionsFromDir $vbgaPath
}
finally {
rm -recurse -force $vbgaPath
}
}
"InstallFromDisc" {
$vboxDiskDrive = get-psdrive -PSProvider Filesystem |? { test-path "$($_.Root)\VBoxWindowsAdditions.exe" }
if ($vboxDiskDrive) {
Write-EventLogWrapper "Found VBox Windows Additions disc at $vboxDiskDrive"
InstallVBoxAdditionsFromDir $vboxDiskDrive.Root
}
else {
$message = "Could not find VBox Windows Additions disc"
Write-EventLogWrapper $message
throw $message
}
}
}
}
function Set-AutoAdminLogon {
[CmdletBinding(DefaultParameterSetName="Enable")] param(
[Parameter(Mandatory=$true,ParameterSetName="Enable")] [String] $Username,
[Parameter(Mandatory=$true,ParameterSetName="Enable")] [String] $Password,
[Parameter(Mandatory=$true,ParameterSetName="Disable")] [Switch] $Disable
)
if ($PsCmdlet.ParameterSetName -Match "Disable") {
Write-EventLogWrapper "Disabling auto admin logon"
$AutoAdminLogon = 0
$Username = ""
$Password = ""
}
elseif ($PsCmdlet.ParameterSetName -Match "Enable") {
Write-EventLogWrapper "Enabling auto admin logon for user '$Username'"
$AutoAdminLogon = 1
}
else {
throw "Invalid parameter set name"
}
$winLogonKey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
Set-ItemProperty -Path $winLogonKey -Name "AutoAdminLogon" -Value $AutoAdminLogon
Set-ItemProperty -Path $winLogonKey -Name "DefaultUserName" -Value $Username
Set-ItemProperty -Path $winLogonKey -Name "DefaultPassword" -Value $Password
}
function Enable-RDP {
Write-EventLogWrapper "Enabling RDP"
netsh advfirewall firewall add rule name="Open Port 3389" dir=in action=allow protocol=TCP localport=3389
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
}
function Install-CompiledDotNetAssemblies {
# http://support.microsoft.com/kb/2570538
# http://robrelyea.wordpress.com/2007/07/13/may-be-helpful-ngen-exe-executequeueditems/
# Don't check the return value - sometimes it fails and that's fine
$ngen32path = "${env:WinDir}\microsoft.net\framework\v4.0.30319\ngen.exe"
# Invoke-ExpressionEx "$ngen32path update /force /queue"
# Invoke-ExpressionEx "$ngen32path executequeueditems"
set-alias ngen32 $ngen32path
ngen32 update /force /queue
ngen32 executequeueditems
if ((Get-OSArchitecture) -match $ArchitectureId.amd64) {
$ngen64path = "${env:WinDir}\microsoft.net\framework64\v4.0.30319\ngen.exe"
# Invoke-ExpressionEx "$ngen64path update /force /queue"
# Invoke-ExpressionEx "$ngen64path executequeueditems"
set-alias ngen64 $ngen64path
ngen64 update /force /queue
ngen64 executequeueditems
}
}
function Compress-WindowsInstall {
$OSArch = Get-OSArchitecture
try {
$udfZipPath = Get-WebUrl -url $URLs.UltraDefragDownload.$OSArch -outDir $env:temp
$udfExPath = "${env:temp}\ultradefrag-portable-6.1.0.$OSArch"
# This archive contains a folder - extract it directly to the temp dir
Invoke-ExpressionEx -command ('sevenzip x "{0}" "-o{1}"' -f $udfZipPath,$env:temp)
$sdZipPath = Get-WebUrl -url $URLs.SdeleteDownload -outDir $env:temp
$sdExPath = "${env:temp}\SDelete"
# This archive does NOT contain a folder - extract it to a subfolder (will create if necessary)
Invoke-ExpressionEx -command ('sevenzip x "{0}" "-o{1}"' -f $sdZipPath,$sdExPath)
stop-service wuauserv
rm -recurse -force ${env:WinDir}\SoftwareDistribution\Download
start-service wuauserv
Invoke-ExpressionEx -logToStdout -command ('& {0} --optimize --repeat "{1}"' -f "$udfExPath\udefrag.exe","$env:SystemDrive")
Invoke-ExpressionEx -command ('& {0} /accepteula -q -z "{1}"' -f "$sdExPath\SDelete.exe",$env:SystemDrive)
}
finally {
rm -recurse -force $udfZipPath,$udfExPath,$sdZipPath,$sdExPath -ErrorAction Continue
}
}
function Disable-WindowsUpdates {
Test-AdminPrivileges -ThrowIfNotElevated
$Updates = (New-Object -ComObject "Microsoft.Update.AutoUpdate").Settings
if ($Updates.ReadOnly) {
throw "Cannot update Windows Update settings due to GPO restrictions."
}
$Updates.NotificationLevel = 1 # 1 = Disabled lol
$Updates.Save()
$Updates.Refresh()
}
function Enable-MicrosoftUpdate {
[cmdletbinding()] param()
Write-EventLogWrapper "Enabling Microsoft Update..."
stop-service wuauserv
$auKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update"
Set-ItemProperty -path $auKey -name EnableFeaturedSoftware -value 1
Set-ItemProperty -path $auKey -name IncludeRecommendedUpdates -value 1
$ServiceManager = New-Object -ComObject "Microsoft.Update.ServiceManager"
$ServiceManager.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",7,"") | out-null
start-service wuauserv
}
function Install-Chocolatey {
[cmdletbinding()] param()
$chocoExePath = "${env:ProgramData}\Chocolatey\bin"
if ($($env:Path).ToLower().Contains($($chocoExePath).ToLower())) {
Write-EventLogWrapper "Attempting to install Chocolatey but it's already in path, exiting..."
return
}
$systemPath = [Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::Machine)
$systemPath += ";$chocoExePath"
[Environment]::SetEnvironmentVariable("PATH", $systemPath, [System.EnvironmentVariableTarget]::Machine)
$env:Path = $systemPath
$userPath = [Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::User)
if ($userPath) { $env:Path += ";$userPath" }
# TODO: capture and log output
$chocoOutput = iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
Write-EventLogWrapper "Chocolatey install process completed:`r`n`r`n$chocoOutput"
}
function Get-FirefoxInstallDirectory {
[cmdletbinding()] param()
@($env:ProgramFiles,${env:ProgramFiles(x86)}) |% {
$testPath = "$_\Mozilla Firefox"
if (Test-Path "$testPath\firefox.exe") {$ffDir = $testPath}
}
if (-not $ffDir) {
throw "Could not find the Firefox install location."
}
else {
return $ffDir
}
}
<#
.notes
One nice thing about FF and Chrome is that you don't have to handle updates yourself - they both install services that update the browser for you
#>
function Install-Firefox {
[cmdletbinding()] param(
[ValidateSet("Standard","ESR")] [String] $edition = "Standard",
[String] $language = "en-US"
)
switch ($edition) {
"Standard" {$downloadPageUrl = 'https://www.mozilla.org/en-US/firefox/all/'}
"ESR" {$downloadPageUrl = 'https://www.mozilla.org/en-US/firefox/organizations/all'}
}
$osarch = Get-OSArchitecture
switch ($osarch) {
$ArchitectureId.amd64 {$os = 'win64'}
$ArchitectureId.i386 {$os = 'win'}
}
$firefoxIniFile = "${env:temp}\firefox-installer.ini"
try {
Write-EventLogWrapper "Finding download location for $edition edition of Firefox..."
$response = Invoke-WebRequest -Uri $downloadPageUrl
$downloadUrl = $response.ParsedHtml.getElementById($language).getElementsByClassName("download $os")[0].getElementsByTagName('a') | Select -Expand href
$firefoxInstallerFile = Get-WebUrl -url $downloadUrl -outFile "${env:temp}\firefox-installer.exe"
$firefoxIniContents = @(
"QuickLaunchShortcut=false"
"DesktopShortcut=false"
)
Out-File -FilePath $firefoxIniFile -InputObject $firefoxIniContents -Encoding UTF8
Write-EventLogWrapper "Beginning Firefox installation process..."
$process = Start-Process -FilePath $firefoxInstallerFile.FullName -ArgumentList @("/INI=`"$firefoxIniFile`"") -Wait -PassThru
if ($process.ExitCode -ne 0) {
throw "Firefox installer at $($firefoxInstallerFile.FullName) exited with code $($process.ExitCode)"
}
}
catch {
@($firefoxInstallerFile,$firefoxIniFile) |% { if ($_ -and (Test-Path $_)) { Remove-Item $_ } }
throw $_
}
Write-EventLogWrapper "Firefox installation process complete"
Remove-Item @($firefoxInstallerFile,$firefoxIniFile)
}
function Uninstall-Firefox {
[cmdletbinding()] param()
$ffDir = Get-FirefoxInstallDirectory
$ffUninstallHelper = Get-Item "$ffDir\uninstall\helper.exe"
$process = Start-Process -FilePath $ffUninstallHelper.FullName -ArgumentList "/S" -Wait -PassThru
if ($process.ExitCode -ne 0) {
throw "Firefox uninstall helper at $($ffUninstallHelper.FullName) exited with code $($process.ExitCode)"
}
Remove-Item -Recurse -Force $ffDir
}
<#
.parameter systemDisableImportWizard
Don't run the import wizard when starting Firefox for the first time
.parameter systemDisableWhatsNew
Don't open dumb tabs that no one needs when starting Firefox for the first time
See also: http://kb.mozillazine.org/Browser.startup.homepage_override.mstone
.parameter systemEnableGlobalAddOns
By default, add-ons that are installed to the global Firefox application directory are available to users, but disabled by default. Enable them by default instead.
.parameter userDeleteConfiguration
Wipe out the configuration, including profiles, of the current user
.parameter userSetDefaultBrowser
Set Firefox to be the default browser for the current user
.notes
Parameters prepended with "system" affect all Firefox users on the entire machine
Parameters prepended with "user" affect only the current user's Firefox configuration
#>
function Set-FirefoxOptions {
[cmdletbinding()] param(
[switch] $systemDisableImportWizard,
[switch] $systemDisableWhatsNew,
[switch] $systemEnableGlobalAddOns,
[string[]] $systemInstallAddOnsFromUrl,
[switch] $userDeleteConfiguration,
[switch] $userSetDefaultBrowser
)
$ffDir = Get-FirefoxInstallDirectory
$ffPath = "$ffDir\firefox.exe"
function Test-LockCfgSetting {
param(
[String] $name,
[String] $lockFile = "$(Get-FirefoxInstallDirectory)\mozilla.cfg"
)
if (-not (Test-Path $lockFile)) { return $false }
foreach ($line in (Get-Content $lockFile)) {
if ($line -match "`"$name`"") {
return $true
}
}
return $false
}
function Remove-LockCfgSetting {
param(
[String] $name,
[String] $lockFile = "$(Get-FirefoxInstallDirectory)\mozilla.cfg"
)
$newLockFileContents = @()
foreach ($line in (Get-Content $lockFile)) {
if ($line -notmatch "`"$name`"") {
$newLockFileContents += @($line)
}
}
Out-File -InputObject $newLockFileContents -FilePath $lockFile -Encoding ASCII -Force
}
function Add-LockCfgSetting {
param(
[String] $name,
$value,
[String] $lockFile = "$(Get-FirefoxInstallDirectory)\mozilla.cfg"
)
if ($value.GetType().FullName -match "System.Int*") {
$wrappedValue = $value
}
else {
$wrappedValue = "`"$value`""
}
$newSettingLine = 'pref("{0}", {1});' -f $name, $wrappedValue
if (-not (Test-Path $lockFile)) {
Out-File -InputObject "//" -FilePath $lockFile -Encoding "ASCII"
}
if (Test-LockCfgSetting -name $name -lockFile $lockFile) {Remove-LockCfgSetting -name $name -lockFile $lockFile}
Add-FileLineIdempotently -file $lockFile -Encoding ASCII -newLine $newSettingLine
}
<#
.notes
We assume that $lockPrefFile is unique to us and we can always overwrite it
See also: http://kb.mozillazine.org/Locking_preferences
#>
function Enable-LockCfg {
[cmdletbinding()] param(
[String] $lockPrefFile = "$(Get-FirefoxInstallDirectory)\defaults\pref\marionettist-locked-configuration.js"
)
$lockPrefContents = @(
'pref("general.config.obscure_value", 0);' # only needed if you do not want to obscure the content with ROT-13
'pref("general.config.filename", "mozilla.cfg");'
)
Out-File -InputObject $lockPrefContents -FilePath $lockPrefFile -Encoding ASCII -Force
}
if ($systemDisableImportWizard) {
$overrideIniContents = @(
'[XRE]'
'EnableProfileMigrator=false'
)
Out-File -InputObject $overrideIniContents -FilePath "$ffDir\browser\override.ini" -Encoding UTF8
}
if ($systemDisableWhatsNew) {
Add-LockCfgSetting -name "browser.startup.homepage_override.mstone" -value "ignore"
Enable-LockCfg
}
if ($systemEnableGlobalAddOns) {
$globalAddOnsPrefFile = "$ffDir\defaults\pref\marionettist-enable-global-add-ons.js"
# See also: https://mike.kaply.com/2012/02/21/understanding-add-on-scopes/
Add-FileLineIdempotently -file "$globalAddOnsPrefFile" -Encoding ASCII -newLine @(
'pref("extensions.enabledScopes", "15");'
'pref("extensions.autoDisableScopes", 0);'
'pref("extensions.shownSelectionUI", true);'
)
}
if ($systemInstallAddOnsFromUrl) {
foreach ($url in $systemInstallAddOnsFromUrl) {
Install-FirefoxAddOnGlobally $url
}
}
if ($userDeleteConfiguration) {
Get-Process |? Name -eq "firefox" | Stop-Process
@("${env:AppData}\Mozilla\Firefox", "${env:LocalAppData}\Mozilla\Firefox") |% { if (test-path $_) {Remove-Item -Recurse -Force $_} }
}
if ($userSetDefaultBrowser) {
Start-Process -FilePath $ffPath -ArgumentList @("-silent", "-setDefaultBrowser") -Wait -Verb RunAs
# This didn't appear to work:
# $defaultBrowserPath = "HKCU:\Software\Classes\http\shell\open\command"
# $defaultBrowserValue = '"{0}" -osint -url "%1"' -f $ffPath
# Set-ItemProperty -path $defaultBrowserPath -name "(default)" -value $defaultBrowserValue
}
}
<#
.parameter latestDownloadUrl
Obtain this parameter by going to the site for the add-on at addons.mozilla.org and copying the link from under the "Add to Firefox" button
.notes
Since recent versions of Firefox, you must use only signed add-ons, which typically means you have to get them from addons.mozilla.org
See also: https://support.mozilla.org/en-US/questions/966922
#>
function Install-FirefoxAddOnGlobally {
[CmdletBinding(DefaultParameterSetName="Name")] param(
[Parameter(ParameterSetName="Url", Mandatory=$true)] [String] $latestDownloadUrl,
[Parameter(ParameterSetName="Name", Mandatory=$true)] [String] $addOnName
)
$ffDir = Get-FirefoxInstallDirectory
$ffSystemExtensionsDir = "$ffDir\browser\extensions"
if ($addOnName) {
$downloadPageUrl = "https://addons.mozilla.org/en-US/firefox/addon/$addOnName/"
$downloadPageResponse = Invoke-WebRequest -Uri $downloadPageUrl
#$downloadUrl = $downloadPageResponse.ParsedHtml.getElementById("addon").getElementsByClassName("install-button")[0].getElementsByTagName('a')[0] | Select -Expand href
$addOnId = $downloadPageResponse.ParsedHtml.getElementById("addon").attributes |? { $_.nodeName -eq "data-id" } | Select -Expand nodeValue
$latestDownloadUrl = "https://addons.mozilla.org/firefox/downloads/latest/$addOnId/addon-$addOnId-latest.xpi"
}
# Cannot install from GitHub, because the version posted there is not signed
# $latestReleaseUrl = "https://api.github.com/repos/gorhill/uBlock/releases/latest"
# $uboXpiInfo = Invoke-RestMethod -Uri $latestReleaseUrl | Select -Expand assets |? -Property content_type -eq "application/x-xpinstall"
# $downloadedXpiPath = Get-WebUrl -url $uboXpiInfo.browser_download_url -outDir $ffSystemExtensionsDir
# Instead, install from addons.mozilla.org:
$downloadedXpiPath = Get-WebUrl -url $latestDownloadUrl -outDir $ffSystemExtensionsDir
$tempExtractDir = Join-Path ${env:temp} $downloadedXpiPath.BaseName
sevenzip x -y "-o$tempExtractDir" $downloadedXpiPath
# For automatic installation, you must install the extension to a folder named after its id, which can be found in the install.rdf of the extension itself:
[System.Xml.XmlDocument] $installRdfXml = Get-Content "$tempExtractDir\install.rdf"
$deployedExtractDir = Join-Path $ffSystemExtensionsDir $installrdfxml.RDF.Description.id
if (Test-Path $deployedExtractDir) { Remove-Item -Force -Recurse $deployedExtractDir }
mv $tempExtractDir $deployedExtractDir
Set-FirefoxOptions -systemEnableGlobalAddOns
}
<#
.parameter uniquePreferenceFileName
We assume that this file is unique to us and we can always overwrite it
.notes
See also: https://support.mozilla.org/en-US/questions/966922
#>
function Install-FirefoxUBlockOrigin {
[CmdletBinding()] param()
$ffDir = Get-FirefoxInstallDirectory
$ffSystemExtensionsDir = "$ffDir\browser\extensions"
$tempExtractDir = Join-Path ${env:temp} 'uBlockOrigin'
# Cannot install from GitHub, because the version posted there is not signed
# $latestReleaseUrl = "https://api.github.com/repos/gorhill/uBlock/releases/latest"
# $uboXpiInfo = Invoke-RestMethod -Uri $latestReleaseUrl | Select -Expand assets |? -Property content_type -eq "application/x-xpinstall"
# $downloadedXpiPath = Join-Path $ffSystemExtensionsDir $uboXpiInfo.name
# Get-WebUrl -url $uboXpiInfo.browser_download_url -outFile $downloadedXpiPath
# Instead, install from addons.mozilla.org:
Get-WebUrl -url "https://addons.mozilla.org/firefox/downloads/latest/607454/addon-607454-latest.xpi"
sevenzip x -y "-o$tempExtractDir" $downloadedXpiPath
# For automatic installation, you must install the extension to a folder named after its id, which can be found in the install.rdf of the extension itself:
[System.Xml.XmlDocument] $installRdfXml = Get-Content "$tempExtractDir\install.rdf"
$deployedExtractDir = Join-Path $ffSystemExtensionsDir $installrdfxml.RDF.Description.id
if (Test-Path $deployedExtractDir) { Remove-Item -Force -Recurse $deployedExtractDir }
mv $tempExtractDir $deployedExtractDir
Set-FirefoxOptions -systemEnableGlobalAddOns
}
function Set-UserOptions {
[cmdletbinding()] param(
[switch] $ShowHiddenFiles,
[switch] $ShowSystemFiles,
[switch] $ShowFileExtensions,
[switch] $ShowStatusBar,
[switch] $DisableSharingWizard,
[switch] $EnablePSOnWinX,
[switch] $EnableQuickEdit,
[switch] $DisableSystrayHide,
[switch] $DisableIEFirstRunCustomize
)
$explorerAdvancedKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced'
if ($ShowHiddenFiles) { Set-ItemProperty -path $explorerAdvancedKey -name Hidden -value 1 }
if ($ShowSystemFiles) { Set-ItemProperty -path $explorerAdvancedKey -name ShowSuperHidden -value 1 }
if ($ShowFileExtensions) { Set-ItemProperty -path $explorerAdvancedKey -name HideFileExt -value 0 }
if ($ShowStatusBar) { Set-ItemProperty -path $explorerAdvancedKey -name ShowStatusBar -value 1 }
if ($DisableSharingWizard) { Set-ItemProperty -path $explorerAdvancedKey -name SharingWizardOn -value 0 }
if ($EnablePSOnWinX) { Set-ItemProperty -path $explorerAdvancedKey -name DontUsePowerShellOnWinX -value 0 }
$explorerKey = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer'
if ($DisableSystrayHide) { Set-ItemProperty -path $explorerKey -name EnableAutoTray -value 0 }
$consoleKey = "HKCU:\Console"
if ($EnableQuickEdit) { Set-ItemProperty -path $consoleKey -name QuickEdit -value 1 }
$internetExplorerKey = "HKCU:\Software\Policies\Microsoft\Internet Explorer\Main"
mkdir -Force $internetExplorerKey
if ($DisableIEFirstRunCustomize) { Set-ItemProperty -path $internetExplorerKey -name DisableFirstRunCustomize -value 1 }
}
<#
.SYNOPSIS
This function are used to pin and unpin programs from the taskbar and Start-menu in Windows 7 and Windows Server 2008 R2
.DESCRIPTION
The function have to parameteres which are mandatory:
Action: PinToTaskbar, PinToStartMenu, UnPinFromTaskbar, UnPinFromStartMenu
FilePath: The path to the program to perform the action on
.notes
from: https://gallery.technet.microsoft.com/scriptcenter/b66434f1-4b3f-4a94-8dc3-e406eb30b750
TODO: I hate it when things pollute the global variable space!
.EXAMPLE
Set-PinnedApplication -Action PinToTaskbar -FilePath "C:\WINDOWS\system32\notepad.exe"
.EXAMPLE
Set-PinnedApplication -Action UnPinFromTaskbar -FilePath "C:\WINDOWS\system32\notepad.exe"
#>
function Set-PinnedApplication {
[CmdletBinding()] param(
[Parameter(Mandatory=$true)][string]$Action,
[Parameter(Mandatory=$true)][string]$FilePath
)
if (-not (test-path $FilePath)) { throw "No file at '$FilePath'" }
function InvokeVerb {
param([string]$FilePath,$verb)
$verb = $verb.Replace("&","")
$path = split-path $FilePath
$shell = new-object -com "Shell.Application"
$folder = $shell.Namespace($path)
$item = $folder.Parsename((split-path $FilePath -leaf))
$itemVerb = $item.Verbs() | ? {$_.Name.Replace("&","") -eq $verb}
if ($itemVerb) { $itemVerb.DoIt() } else { throw "Verb $verb not found." }
}
function GetVerb {
param([int]$verbId)
try { $t = [type]"CosmosKey.Util.MuiHelper" }
catch {
$def = [Text.StringBuilder]""
[void]$def.AppendLine('[DllImport("user32.dll")]')
[void]$def.AppendLine('public static extern int LoadString(IntPtr h,uint id, System.Text.StringBuilder sb,int maxBuffer);')
[void]$def.AppendLine('[DllImport("kernel32.dll")]')
[void]$def.AppendLine('public static extern IntPtr LoadLibrary(string s);')
add-type -MemberDefinition $def.ToString() -name MuiHelper -namespace CosmosKey.Util
}
if($global:CosmosKey_Utils_MuiHelper_Shell32 -eq $null){
$global:CosmosKey_Utils_MuiHelper_Shell32 = [CosmosKey.Util.MuiHelper]::LoadLibrary("shell32.dll")
}
$maxVerbLength = 255
$verbBuilder = new-object Text.StringBuilder "",$maxVerbLength
[void][CosmosKey.Util.MuiHelper]::LoadString($CosmosKey_Utils_MuiHelper_Shell32,$verbId,$verbBuilder,$maxVerbLength)
return $verbBuilder.ToString()
}
$verbs = @{
"PintoStartMenu"=5381
"UnpinfromStartMenu"=5382
"PintoTaskbar"=5386
"UnpinfromTaskbar"=5387
}
if ($verbs.$Action -eq $null) {
throw "Action $action not supported`nSupported actions are:`n`tPintoStartMenu`n`tUnpinfromStartMenu`n`tPintoTaskbar`n`tUnpinfromTaskbar"
}
InvokeVerb -FilePath $FilePath -Verb $(GetVerb -VerbId $verbs.$action)
}
function Disable-HibernationFile {
[cmdletbinding()] param()
Write-EventLogWrapper "Removing Hibernation file..."
$powerKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Power'
Set-ItemProperty -path $powerKey -name HibernateFileSizePercent -value 0 # hiberfil is zero bytes
Set-ItemProperty -path $powerKey -name HibernateEnabled -value 0 # disable hibernation altogether
}
<#
.synopsis
Forcibly enable WinRM
.notes
TODO: Rewrite in pure Powershell
#>
function Enable-WinRM {
[cmdletbinding()] param()
Write-EventLogWrapper "Enabling WinRM..."
# I've had the best luck doing it this way - NOT doing it in a single batch script
# Sometimes one of these commands will stop further execution in a batch script, but when I
# call cmd.exe over and over like this, that problem goes away.
# Note: order is important. This order makes sure that any time packer can successfully
# connect to WinRm, it won't later turn winrm back off or make it unavailable.
Invoke-ExpressionEx -invokeWithCmdExe -command 'net stop winrm'
Invoke-ExpressionEx -invokeWithCmdExe -command 'sc.exe config winrm start= auto'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm quickconfig -q'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm quickconfig -transport:http'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config @{MaxTimeoutms="1800000"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config/winrs @{MaxMemoryPerShellMB="2048"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config/service @{AllowUnencrypted="true"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config/client @{AllowUnencrypted="true"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config/service/auth @{Basic="true"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config/client/auth @{Basic="true"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config/service/auth @{CredSSP="true"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"}'
Invoke-ExpressionEx -invokeWithCmdExe -command 'netsh advfirewall firewall set rule group="remote administration" new enable=yes'
Invoke-ExpressionEx -invokeWithCmdExe -command 'netsh firewall add portopening TCP 5985 "Port 5985"'
Invoke-ExpressionEx -invokeWithCmdExe -command 'net start winrm'
}
function Add-LocalSamUser {
[cmdletbinding()] param(
[Parameter(Mandatory=$true)] [string] $userName,
[Parameter(Mandatory=$true)] [string] $password,
[string] $fullName,
[switch] $PassThru
)
Write-EventLogWrapper "Creating a new local user called '$userName'"
$computer = [ADSI]"WinNT://$env:COMPUTERNAME,Computer"
$newUser = $computer.Create("User", $userName)
$newUser.SetPassword($password)
$newUser.SetInfo()
$newUser.FullName = $fullName
$newUser.SetInfo()
Add-LocalSamUserToGroup -userName $userName -groupName "Users"
if ($PassThru) { return $newUser }
}
function Add-LocalSamUserToGroup {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $userName,
[parameter(mandatory=$true)] [string] $groupName
)
Write-EventLogWrapper "Adding '$userName' to the local '$groupName' group"
$localAdmins = [ADSI]"WinNT://$env:COMPUTERNAME/$groupName,group"
$localAdmins.Add("WinNT://$userName")
}
function Set-PasswordExpiry { # TODO fixme use pure Powershell
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $accountName,
[parameter(mandatory=$true,ParameterSetName="EnablePasswordExpiry")] [switch] $enable,
[parameter(mandatory=$true,ParameterSetName="DisablePasswordExpiry")] [switch] $disable
)
$passwordExpires = if ($PsCmdlet.ParameterSetName -match "EnablePasswordExpiry") {"TRUE"} else {"FALSE"}
$command = @"
wmic useraccount where "name='{0}'" set "PasswordExpires={1}"
"@
$command = $command -f $accountName,$passwordExpires
Invoke-ExpressionEx -command $command
}
<#
.synopsis
Set all attached networks to Private
.description
(On some OSes) you cannot enable Windows PowerShell Remoting on network connections that are set to Public
Spin through all the network locations and if they are set to Public, set them to Private
using the INetwork interface:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa370750(v=vs.85).aspx
For more info, see:
http://blogs.msdn.com/b/powershell/archive/2009/04/03/setting-network-location-to-private.aspx
#>
function Set-AllNetworksToPrivate {
[cmdletbinding()] param()
# Network location feature was only introduced in Windows Vista - no need to bother with this
# if the operating system is older than Vista
if([environment]::OSVersion.version.Major -lt 6) {
Write-EventLogWrapper "Set-AllNetworksToPrivate: Running on pre-Vista machine, no changes necessary"
return
}
Write-EventLogWrapper "Setting all networks to private..."
if(1,3,4,5 -contains (Get-WmiObject win32_computersystem).DomainRole) { throw "Cannot change network location on a domain-joined computer" }
# Disable the GUI which will modally pop up (at least on Win10) lol
New-Item "HKLM:\System\CurrentControlSet\Control\Network\NewNetworkWindowOff" -force | out-null
# Get network connections
$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
foreach ($connection in $networkListManager.GetNetworkConnections()) {
$connName = $connection.GetNetwork().GetName()
$oldCategory = $connection.GetNetwork().GetCategory()
$connection.getNetwork().SetCategory(1)
$newCategory = $connection.GetNetwork().GetCategory()
Write-EventLogWrapper "Changed connection category for '$connName' from '$oldCategory' to '$newCategory'"
}
}
<#
function Get-PowerScheme {
[cmdletbinding(DefaultParameterSetName("Active"))] param(
[Parameter(Mandatory=$true,ParameterSetName="Active")] [switch] $Active,
[Parameter(Mandatory=$true,ParameterSetName="ByGuid")] [switch] $ByGuid,
[Parameter(Mandatory=$true,ParameterSetName="ByName")] [switch] $ByName,
)
$powerScheme = New-Object PSObject -Property @{Name="";GUID="";}
$psre = '^Power Scheme GUID\:\s+([A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12})\s+\((.*)\)'
switch ($PsCmdlet.ParameterSetName) {
"Active" {
$activeSchemeString = powercfg /getactivescheme
if ($activeSchemeString -match $psre) {
$powerScheme.Name = $matches[2]
$powerScheme.GUID = $matches[1]
}
else { write-error "Error: could not find active power configuration"}
}
"ByGuid" {
foreach ($powerSchemeString in (powercfg /list)) {
$
}
}
"ByName" {}
default {write-error "Error: not sure how to process a parameter set named $($PsCmdlet.ParameterSetName)"}
}
return $powerScheme
}
#>
<#
.synopsis
Set the idle time that must elapse before Windows will power off a display
.parameter seconds
The number of seconds before poweroff. A value of 0 means never power off.
.notes
AFAIK, this cannot be done without shelling out to powercfg
#>
function Set-IdleDisplayPoweroffTime {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [int] $seconds
)
$currentScheme = (powercfg /getactivescheme).split()[3]
$DisplaySubgroupGUID = "7516b95f-f776-4464-8c53-06167f40cc99"
$TurnOffAfterGUID = "3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e"
set-alias powercfg "${env:SystemRoot}\System32\powercfg.exe"
powercfg /setacvalueindex $currentScheme $DisplaySubgroupGUID $TurnOffAfterGUID 0
}
<#
.description
Allow connecting to HTTPS WinRM servers (used with, for example, Enter-PSSession) without checking the certificate. This is not recommended, but can be useful for non-domain-joined VMs that will connect to a remote network over a VPN. (Note that not checking the RDP certificate is no improvement over not checking the WinRM certificate.)
#>
function Enable-UntrustedOutboundWinRmConnections {
[CmdletBinding()] Param()
Set-Item WSMan:\localhost\Client\Auth\CredSSP $True
Set-Item WSMan:\localhost\Service\Auth\CredSSP $True
set-item WSMan:\localhost\Client\TrustedHosts *
Restart-Service WinRm
}
# Exports: #TODO
$emmParams = @{
Alias = @("sevenzip")
Variable = @("ArchitectureId")
Function = "*"
# Function = @(
# "Get-OSArchitecture"
# "Get-LabTempDir"
# "Install-SevenZip"
# "Install-VBoxAdditions"
# )
}
export-modulemember @emmParams