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

679 lines
30 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"
}
$script:ScriptPath = $MyInvocation.MyCommand.Path
### Private support functions I use behind the scenes
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") {
$filename = [System.IO.Path]::GetFileName($url)
$outFile = "$outDir\$filename"
}
$outFile = [IO.Path]::GetFullPath($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
$commandSb = if ($invokeWithCmdExe) {{cmd /c "$command"}} else {{invoke-expression -command $command}}
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 faile3d 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
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 "$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 $_
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 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 -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\oracle-vbox.cer" | select -expand path
# NOTE: Checking for exit code, but this command will fail with an error if the cert is already installed
Invoke-ExpressionEx -checkExitCode -command ('& "{0}" add-trusted-publisher "{1}" --root "{1}"' -f "$baseDir\cert\VBoxCertUtil.exe",$oracleCert)
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 Disable-AutoAdminLogon {
Write-EventLogWrapper "Disabling auto admin logon"
set-itemproperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value 0
}
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
$ngen32 = "${env:WinDir}\microsoft.net\framework\v4.0.30319\ngen.exe"
Invoke-ExpressionEx "$ngen32 update /force /queue"
Invoke-ExpressionEx "$ngen32 executequeueditems"
# set-alias ngen32 "${env:WinDir}\microsoft.net\framework\v4.0.30319\ngen.exe"
# ngen32 update /force /queue
# ngen32 executequeueditems
if ((Get-OSArchitecture) -match $ArchitectureId.amd64) {
# set-alias ngen64 "${env:WinDir}\microsoft.net\framework64\v4.0.30319\ngen.exe"
# ngen64 update /force /queue
# ngen64 executequeueditems
$ngen64 = "${env:WinDir}\microsoft.net\framework64\v4.0.30319\ngen.exe"
Invoke-ExpressionEx "$ngen64 update /force /queue"
Invoke-ExpressionEx "$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 -checkExitCode -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 -checkExitCode -command ('sevenzip x "{0}" "-o{1}"' -f $sdZipPath,$sdExPath)
stop-service wuauserv
rm -recurse -force ${env:WinDir}\SoftwareDistribution\Download
start-service wuauserv
Invoke-ExpressionEx -checkExitCode -logToStdout -command ('& {0} --optimize --repeat "{1}"' -f "$udfExPath\udefrag.exe","$env:SystemDrive")
Invoke-ExpressionEx -checkExitCode -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 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"
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 Set-PasswordExpiry { # TODO fixme use pure Powershell
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $accountName,
[parameter(mandatory=$true)] [bool] $expirePassword
)
$passwordExpires = if ($expirePassword) {"TRUE"} else {"FALSE"}
$command = @"
wmic useraccount where "name='{0}'" set "PasswordExpires={1}"
"@
$command = $command -f $accountName, $passwordExpiress
Invoke-ExpressionEx -invokeWithCmdExe -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
}
# Exports: #TODO
$emmParams = @{
Alias = @("sevenzip")
Variable = @("ArchitectureId")
Function = "*"
# Function = @(
# "Get-OSArchitecture"
# "Get-LabTempDir"
# "Install-SevenZip"
# "Install-VBoxAdditions"
# )
}
export-modulemember @emmParams